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()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
166 }
167 catch( const IO_ERROR& ioe )
168 {
169 DisplayError( this, ioe.What() );
170 }
171
172 // Clean up the temporary file
173 wxRemoveFile( tempFile );
174
175 m_designBlocksPane->RefreshLibs();
176 m_designBlocksPane->SelectLibId( blk.GetLibId() );
177
178 return success;
179}
180
181
183{
184 // Make sure the user has selected a library to save into
185 if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
186 {
187 DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
188 return false;
189 }
190
191 std::unique_ptr<DESIGN_BLOCK> blk;
192
193 try
194 {
195 blk.reset( Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) );
196 }
197 catch( const IO_ERROR& ioe )
198 {
199 DisplayError( this, ioe.What() );
200 return false;
201 }
202
203 if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) )
204 return false;
205
206 // Save a temporary copy of the schematic file, as the plugin is just going to move it
207 wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
208
209 if( !SavePcbCopy( tempFile, false, false ) )
210 {
211 DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
212 wxRemoveFile( tempFile );
213 return false;
214 }
215
216 blk->SetBoardFile( tempFile );
217
218 bool success = false;
219
220 try
221 {
222 success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk.get() )
224 }
225 catch( const IO_ERROR& ioe )
226 {
227 DisplayError( this, ioe.What() );
228 }
229
230 // Clean up the temporary file
231 wxRemoveFile( tempFile );
232
233 m_designBlocksPane->RefreshLibs();
234 m_designBlocksPane->SelectLibId( blk->GetLibId() );
235
236 return success;
237}
238
239
240bool PCB_EDIT_FRAME::saveSelectionToDesignBlock( const wxString& aNickname, PCB_SELECTION& aSelection,
241 DESIGN_BLOCK& aBlock )
242{
243 // Create a temporary board
244 BOARD* tempBoard = new BOARD();
246 tempBoard->SetProject( &Prj(), true );
247 tempBoard->SynchronizeProperties();
248
249 // For copying net info of selected items into the new board
250 auto addNetIfNeeded =
251 [&]( EDA_ITEM* aItem )
252 {
253 BOARD_CONNECTED_ITEM* cItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
254
255 if( cItem )
256 {
257 NETINFO_ITEM* netinfo = cItem->GetNet();
258
259 if( netinfo )
260 {
261 NETINFO_ITEM* existingInfo = tempBoard->FindNet( netinfo->GetNetname() );
262
263 // If the net has already been added to the new board, update our info to match
264 if( existingInfo )
265 cItem->SetNet( existingInfo );
266 else
267 {
268 NETINFO_ITEM* newNet = new NETINFO_ITEM( tempBoard, netinfo->GetNetname() );
269 tempBoard->Add( newNet );
270 cItem->SetNet( newNet );
271 }
272 }
273 }
274 };
275
276 auto cloneAndAdd =
277 [&] ( EDA_ITEM* aItem )
278 {
279 if( !aItem->IsBOARD_ITEM() )
280 return static_cast<BOARD_ITEM*>( nullptr );
281
282 BOARD_ITEM* copy = static_cast<BOARD_ITEM*>( aItem->Clone() );
283 tempBoard->Add( copy, ADD_MODE::APPEND, false );
284 return copy;
285 };
286
287 // Copy the selected items to the temporary board
288 for( EDA_ITEM* item : aSelection )
289 {
290 BOARD_ITEM* copy = cloneAndAdd( item );
291
292 if( !copy )
293 continue;
294
295 copy->SetParentGroup( nullptr );
296
297 if( copy->Type() == PCB_FOOTPRINT_T )
298 {
299 static_cast<FOOTPRINT*>( copy )->RunOnChildren( addNetIfNeeded, RECURSE_MODE::NO_RECURSE );
300 }
301 else if( copy->Type() == PCB_GROUP_T || copy->Type() == PCB_GENERATOR_T )
302 {
303 PCB_GROUP* group = static_cast<PCB_GROUP*>( item );
304
305 // Groups also need their children copied
306 group->RunOnChildren( cloneAndAdd, RECURSE_MODE::RECURSE );
307 group->RunOnChildren( addNetIfNeeded, RECURSE_MODE::RECURSE );
308 }
309 else
310 addNetIfNeeded( copy );
311 }
312
313 wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
314
315 if( !saveBoardAsFile( tempBoard, tempFile, false ) )
316 {
317 DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
318 wxRemoveFile( tempFile );
319 return false;
320 }
321
322 aBlock.SetBoardFile( tempFile );
323
324 bool success = false;
325
326 try
327 {
328 success = Prj().DesignBlockLibs()->DesignBlockSave( aNickname, &aBlock ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
329 }
330 catch( const IO_ERROR& ioe )
331 {
332 DisplayError( this, ioe.What() );
333 }
334
335 // Clean up the temporary file
336 wxRemoveFile( tempFile );
337
338 m_designBlocksPane->RefreshLibs();
339 m_designBlocksPane->SelectLibId( aBlock.GetLibId() );
340
341 return success;
342}
343
344
345bool PCB_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
346{
347 // Make sure the user has selected a library to save into
348 if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
349 {
350 DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
351 return false;
352 }
353
354 // Get all selected items
355 PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
356
357 if( selection.Empty() )
358 {
359 DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
360 return false;
361 }
362
363 DESIGN_BLOCK blk;
364 wxFileName fn = wxFileNameFromPath( GetBoard()->GetFileName() );
365
366 blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) );
367
368 DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk );
369
370 if( dlg.ShowModal() != wxID_OK )
371 return false;
372
373 wxString libName = blk.GetLibId().GetLibNickname();
374 wxString newName = blk.GetLibId().GetLibItemName();
375
376 if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) )
377 {
378 return false;
379 }
380
381 return saveSelectionToDesignBlock( libName, selection, blk );
382}
383
384
386{
387 // Make sure the user has selected a library to save into
388 if( !aLibId.IsValid() )
389 {
390 DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
391 return false;
392 }
393
394 // Get all selected items
395 PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
396
397 if( selection.Empty() )
398 {
399 DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
400 return false;
401 }
402
403 // If we have a single group, we want to strip the group and select the children
404 PCB_GROUP* group = nullptr;
405
406 if( selection.Size() == 1 )
407 {
408 EDA_ITEM* item = selection.Front();
409
410 if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
411 {
412 group = static_cast<PCB_GROUP*>( item );
413
414 selection.Remove( item );
415
416 // Don't recurse; if we have a group of groups the user probably intends the inner groups to be saved
417 group->RunOnChildren( [&]( EDA_ITEM* aItem ) { selection.Add( aItem ); }, RECURSE_MODE::NO_RECURSE );
418 }
419 }
420
421 std::unique_ptr<DESIGN_BLOCK> blk;
422
423 try
424 {
425 blk.reset( Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) );
426 }
427 catch( const IO_ERROR& ioe )
428 {
429 DisplayError( this, ioe.What() );
430 return false;
431 }
432
433 if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) )
434 return false;
435
436 if( !saveSelectionToDesignBlock( aLibId.GetLibNickname(), selection, *blk ) )
437 return false;
438
439 // If we had a group, we need to reselect it
440 if( group )
441 {
442 selection.Clear();
443 selection.Add( group );
444
445 // If we didn't have a design block link before, add one for convenience
446 if( !group->HasDesignBlockLink() )
447 {
448 BOARD_COMMIT commit( m_toolManager );
449
450 commit.Modify( group, nullptr, RECURSE_MODE::NO_RECURSE );
451 group->SetDesignBlockLibId( aLibId );
452
453 commit.Push( "Set Group Design Block Link" );
454 }
455 }
456
457 return true;
458}
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:317
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1163
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2152
void SetDesignSettings(const BOARD_DESIGN_SETTINGS &aSettings)
Definition board.cpp:1046
void SetProject(PROJECT *aProject, bool aReferenceOnly=false)
Link a board to a given project.
Definition board.cpp:198
void SynchronizeProperties()
Copy the current project's text variables into the boards property cache.
Definition board.cpp:2274
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 DesignBlockSave(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_LIB_TABLE * DesignBlockLibs()
Return the table of design block libraries.
Definition project.cpp:445
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:142
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:194
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:169
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:612
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:110
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
Definition of file extensions used in Kicad.