KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_design_block_utils.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <pgm_base.h>
25#include <kiway.h>
26#include <board_commit.h>
27#include <design_block.h>
29#include <footprint.h>
30#include <pad.h>
31#include <pcb_group.h>
33#include <pcb_edit_frame.h>
34#include <pcb_io/pcb_io.h>
35#include <pcb_io/pcb_io_mgr.h>
36#include <wx/choicdlg.h>
37#include <wx/msgdlg.h>
38#include <wx/textdlg.h>
40#include <paths.h>
41#include <env_paths.h>
42#include <common.h>
43#include <confirm.h>
44#include <kidialog.h>
45#include <locale_io.h>
46#include <netinfo.h>
47#include <tool/tool_manager.h>
50#include <json_common.h>
51
52bool checkOverwriteDb( wxWindow* aFrame, wxString& libname, wxString& newName )
53{
54 wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ), newName.GetData(),
55 libname.GetData() );
56
57 if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) )
58 != wxID_OK )
59 {
60 return false;
61 }
62
63 return true;
64}
65
66
67bool checkOverwriteDbLayout( wxWindow* aFrame, const LIB_ID& aLibId )
68{
69 wxString msg = wxString::Format( _( "Design block '%s' already has a layout." ), aLibId.GetUniStringLibItemName() );
70
71 if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing layout?" ), _( "Overwrite" ) )
72 != wxID_OK )
73 {
74 return false;
75 }
76
77 return true;
78}
79
80
81bool PCB_EDIT_FRAME::saveBoardAsFile( BOARD* aBoard, const wxString& aFileName, bool aHeadless )
82{
83 // Ensure the "C" locale is temporary set, before saving any file
84 // It also avoid wxWidget alerts about locale issues, later, when using Python 3
86
87 wxFileName pcbFileName( aFileName );
88
89 if( !IsWritable( pcbFileName ) )
90 {
91 if( !aHeadless )
92 {
93 DisplayError( this, wxString::Format( _( "Insufficient permissions to write file '%s'." ),
94 pcbFileName.GetFullPath() ) );
95 }
96 return false;
97 }
98
99 try
100 {
102
103 wxASSERT( pcbFileName.IsAbsolute() );
104
105 pi->SaveBoard( pcbFileName.GetFullPath(), aBoard, nullptr );
106 }
107 catch( const IO_ERROR& ioe )
108 {
109 if( !aHeadless )
110 {
111 DisplayError( this, wxString::Format( _( "Error saving board file '%s'.\n%s" ), pcbFileName.GetFullPath(),
112 ioe.What() ) );
113 }
114
115 return false;
116 }
117
118 return true;
119}
120
121
122bool PCB_EDIT_FRAME::SaveBoardAsDesignBlock( const wxString& aLibraryName )
123{
124 // Make sure the user has selected a library to save into
125 if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
126 {
127 DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
128 return false;
129 }
130
131 DESIGN_BLOCK blk;
132 wxFileName fn = wxFileNameFromPath( GetBoard()->GetFileName() );
133
134 blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) );
135
136 DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk );
137
138 if( dlg.ShowModal() != wxID_OK )
139 return false;
140
141 wxString libName = blk.GetLibId().GetLibNickname();
142 wxString newName = blk.GetLibId().GetLibItemName();
143
144 if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) )
145 {
146 return false;
147 }
148
149 // Save a temporary copy of the schematic file, as the plugin is just going to move it
150 wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
151
152 if( !SavePcbCopy( tempFile, false, false ) )
153 {
154 DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
155 wxRemoveFile( tempFile );
156 return false;
157 }
158
159 blk.SetBoardFile( tempFile );
160
161 bool success = false;
162
163 try
164 {
165 success = Prj().DesignBlockLibs()->SaveDesignBlock( aLibraryName, &blk )
167 }
168 catch( const IO_ERROR& ioe )
169 {
170 DisplayError( this, ioe.What() );
171 }
172
173 // Clean up the temporary file
174 wxRemoveFile( tempFile );
175
176 m_designBlocksPane->RefreshLibs();
177 m_designBlocksPane->SelectLibId( blk.GetLibId() );
178
179 return success;
180}
181
182
184{
185 // Make sure the user has selected a library to save into
186 if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
187 {
188 DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
189 return false;
190 }
191
192 std::unique_ptr<DESIGN_BLOCK> blk;
193
194 try
195 {
196 blk.reset( Prj().DesignBlockLibs()->LoadDesignBlock( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) );
197 }
198 catch( const IO_ERROR& ioe )
199 {
200 DisplayError( this, ioe.What() );
201 return false;
202 }
203
204 if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) )
205 return false;
206
207 // Save a temporary copy of the schematic file, as the plugin is just going to move it
208 wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
209
210 if( !SavePcbCopy( tempFile, false, false ) )
211 {
212 DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
213 wxRemoveFile( tempFile );
214 return false;
215 }
216
217 blk->SetBoardFile( tempFile );
218
219 bool success = false;
220
221 try
222 {
223 success = Prj().DesignBlockLibs()->SaveDesignBlock( aLibId.GetLibNickname(), blk.get() )
225 }
226 catch( const IO_ERROR& ioe )
227 {
228 DisplayError( this, ioe.What() );
229 }
230
231 // Clean up the temporary file
232 wxRemoveFile( tempFile );
233
234 m_designBlocksPane->RefreshLibs();
235 m_designBlocksPane->SelectLibId( blk->GetLibId() );
236
237 return success;
238}
239
240
241bool PCB_EDIT_FRAME::saveSelectionToDesignBlock( const wxString& aNickname, PCB_SELECTION& aSelection,
242 DESIGN_BLOCK& aBlock )
243{
244 // Create a temporary board
245 BOARD* tempBoard = new BOARD();
247 tempBoard->SetProject( &Prj(), true );
248 tempBoard->SynchronizeProperties();
249
250 // For copying net info of selected items into the new board
251 auto addNetIfNeeded =
252 [&]( EDA_ITEM* aItem )
253 {
254 BOARD_CONNECTED_ITEM* cItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
255
256 if( cItem )
257 {
258 NETINFO_ITEM* netinfo = cItem->GetNet();
259
260 if( netinfo )
261 {
262 NETINFO_ITEM* existingInfo = tempBoard->FindNet( netinfo->GetNetname() );
263
264 // If the net has already been added to the new board, update our info to match
265 if( existingInfo )
266 cItem->SetNet( existingInfo );
267 else
268 {
269 NETINFO_ITEM* newNet = new NETINFO_ITEM( tempBoard, netinfo->GetNetname() );
270 tempBoard->Add( newNet );
271 cItem->SetNet( newNet );
272 }
273 }
274 }
275 };
276
277 auto cloneAndAdd =
278 [&] ( EDA_ITEM* aItem )
279 {
280 if( !aItem->IsBOARD_ITEM() )
281 return static_cast<BOARD_ITEM*>( nullptr );
282
283 BOARD_ITEM* copy = static_cast<BOARD_ITEM*>( aItem->Clone() );
284 tempBoard->Add( copy, ADD_MODE::APPEND, false );
285 return copy;
286 };
287
288 // Copy the selected items to the temporary board
289 for( EDA_ITEM* item : aSelection )
290 {
291 BOARD_ITEM* copy = cloneAndAdd( item );
292
293 if( !copy )
294 continue;
295
296 copy->SetParentGroup( nullptr );
297
298 if( copy->Type() == PCB_FOOTPRINT_T )
299 {
300 static_cast<FOOTPRINT*>( copy )->RunOnChildren( addNetIfNeeded, RECURSE_MODE::NO_RECURSE );
301 }
302 else if( copy->Type() == PCB_GROUP_T || copy->Type() == PCB_GENERATOR_T )
303 {
304 PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
305
306 // Groups also need their children copied
307 group->RunOnChildren( cloneAndAdd, RECURSE_MODE::RECURSE );
308 group->RunOnChildren( addNetIfNeeded, RECURSE_MODE::RECURSE );
309 }
310 else
311 addNetIfNeeded( copy );
312 }
313
314 wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
315
316 if( !saveBoardAsFile( tempBoard, tempFile, false ) )
317 {
318 DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
319 wxRemoveFile( tempFile );
320 return false;
321 }
322
323 aBlock.SetBoardFile( tempFile );
324
325 bool success = false;
326
327 try
328 {
329 success = Prj().DesignBlockLibs()->SaveDesignBlock( aNickname, &aBlock )
331 }
332 catch( const IO_ERROR& ioe )
333 {
334 DisplayError( this, ioe.What() );
335 }
336
337 // Clean up the temporary file
338 wxRemoveFile( tempFile );
339
340 m_designBlocksPane->RefreshLibs();
341 m_designBlocksPane->SelectLibId( aBlock.GetLibId() );
342
343 return success;
344}
345
346
347bool PCB_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
348{
349 // Make sure the user has selected a library to save into
350 if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
351 {
352 DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
353 return false;
354 }
355
356 // Get all selected items
357 PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
358
359 if( selection.Empty() )
360 {
361 DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
362 return false;
363 }
364
365 DESIGN_BLOCK blk;
366 wxFileName fn = wxFileNameFromPath( GetBoard()->GetFileName() );
367
368 blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) );
369
370 DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk );
371
372 if( dlg.ShowModal() != wxID_OK )
373 return false;
374
375 wxString libName = blk.GetLibId().GetLibNickname();
376 wxString newName = blk.GetLibId().GetLibItemName();
377
378 if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) )
379 {
380 return false;
381 }
382
383 return saveSelectionToDesignBlock( libName, selection, blk );
384}
385
386
388{
389 // Make sure the user has selected a library to save into
390 if( !aLibId.IsValid() )
391 {
392 DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
393 return false;
394 }
395
396 // Get all selected items
397 PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
398
399 if( selection.Empty() )
400 {
401 DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
402 return false;
403 }
404
405 // If we have a single group, we want to strip the group and select the children
406 PCB_GROUP* group = nullptr;
407
408 if( selection.Size() == 1 )
409 {
410 EDA_ITEM* item = selection.Front();
411
412 if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
413 {
414 group = static_cast<PCB_GROUP*>( item );
415
416 selection.Remove( item );
417
418 // Don't recurse; if we have a group of groups the user probably intends the inner groups to be saved
419 group->RunOnChildren( [&]( EDA_ITEM* aItem ) { selection.Add( aItem ); }, RECURSE_MODE::NO_RECURSE );
420 }
421 }
422
423 std::unique_ptr<DESIGN_BLOCK> blk;
424
425 try
426 {
427 blk.reset( Prj().DesignBlockLibs()->LoadDesignBlock( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) );
428 }
429 catch( const IO_ERROR& ioe )
430 {
431 DisplayError( this, ioe.What() );
432 return false;
433 }
434
435 if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) )
436 return false;
437
438 if( !saveSelectionToDesignBlock( aLibId.GetLibNickname(), selection, *blk ) )
439 return false;
440
441 // If we had a group, we need to reselect it
442 if( group )
443 {
444 selection.Clear();
445 selection.Add( group );
446
447 // If we didn't have a design block link before, add one for convenience
448 if( !group->HasDesignBlockLink() )
449 {
450 BOARD_COMMIT commit( m_toolManager );
451
452 commit.Modify( group, nullptr, RECURSE_MODE::NO_RECURSE );
453 group->SetDesignBlockLibId( aLibId );
454
455 commit.Push( "Set Group Design Block Link" );
456 }
457 }
458
459 return true;
460}
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1167
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2285
void SetDesignSettings(const BOARD_DESIGN_SETTINGS &aSettings)
Definition board.cpp:1050
void SetProject(PROJECT *aProject, bool aReferenceOnly=false)
Link a board to a given project.
Definition board.cpp:202
void SynchronizeProperties()
Copy the current project's text variables into the boards property cache.
Definition board.cpp:2407
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
SAVE_T SaveDesignBlock(const wxString &aNickname, const DESIGN_BLOCK *aDesignBlock, bool aOverwrite=true)
Write aDesignBlock to an existing library given by aNickname.
void SetBoardFile(const wxString &aFile)
void SetLibId(const LIB_ID &aName)
const LIB_ID & GetLibId() const
int ShowModal() override
bool IsWritable(const wxFileName &aFileName, bool aVerbose=true)
Check if aFileName can be written.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:172
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:112
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:87
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:41
Handle the data for a net.
Definition netinfo.h:54
const wxString & GetNetname() const
Definition netinfo.h:112
BOARD * GetBoard() const
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Return the BOARD_DESIGN_SETTINGS for the open project.
bool saveBoardAsFile(BOARD *aBoard, const wxString &aFileName, bool aHeadless=false)
Save a board object to a file.
bool SaveBoardToDesignBlock(const LIB_ID &aLibId)
bool saveSelectionToDesignBlock(const wxString &aNickname, PCB_SELECTION &aSelection, DESIGN_BLOCK &aBlock)
bool SavePcbCopy(const wxString &aFileName, bool aCreateProject=false, bool aHeadless=false)
Write the board data structures to aFileName.
bool SaveSelectionAsDesignBlock(const wxString &aLibraryName)
bool SaveSelectionToDesignBlock(const LIB_ID &aLibId)
bool SaveBoardAsDesignBlock(const wxString &aLibraryName)
PCB_DESIGN_BLOCK_PANE * m_designBlocksPane
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
static PCB_IO * PluginFind(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:58
The selection tool: currently supports:
virtual DESIGN_BLOCK_LIBRARY_ADAPTER * DesignBlockLibs()
Return the table of design block libraries.
Definition project.cpp:448
virtual void Add(EDA_ITEM *aItem)
Definition selection.cpp:42
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:60
EDA_ITEM * Front() const
Definition selection.h:177
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:98
int Size() const
Returns the number of selected parts.
Definition selection.h:121
bool Empty() const
Checks if there is anything selected.
Definition selection.h:115
TOOL_MANAGER * m_toolManager
The common library.
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Display a warning dialog with aMessage and returns the user response.
Definition confirm.cpp:150
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:177
This file is part of the common library.
#define _(s)
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
Helper functions to substitute paths with environmental variables.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
PROJECT & Prj()
Definition kicad.cpp:623
bool checkOverwriteDb(wxWindow *aFrame, wxString &libname, wxString &newName)
bool checkOverwriteDbLayout(wxWindow *aFrame, const LIB_ID &aLibId)
Class to handle a set of BOARD_ITEMs.
see class PGM_BASE
bool checkOverwriteDb(wxWindow *aFrame, wxString &libname, wxString &newName)
std::vector< FAB_LAYER_COLOR > dummy
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:91
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
Definition of file extensions used in Kicad.