KiCad PCB EDA Suite
swap_layers.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 (C) 2007-2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
30 #include <fctsys.h>
31 #include <class_drawpanel.h>
32 #include <pcb_edit_frame.h>
33 #include <dialog_shim.h>
34 
35 #include <class_board.h>
36 #include <class_track.h>
37 #include <class_drawsegment.h>
38 
39 #include <pcbnew.h>
40 #include <board_commit.h>
41 
42 #include <wx/statline.h>
43 
44 
45 #define NO_CHANGE PCB_LAYER_ID(-3)
46 
47 
52 };
53 
54 
56 {
57 public:
59  // ~MOVE_SWAP_LAYER_DIALOG() { };
60 
61 private:
63  wxBoxSizer* m_outerBoxSizer;
64  wxBoxSizer* m_mainBoxSizer;
65  wxFlexGridSizer* FlexColumnBoxSizer;
66  wxStdDialogButtonSizer* StdDialogButtonSizer;
67 
68  PCB_LAYER_ID* m_callers_nlayers; // DIM() is PCB_LAYER_ID_COUNT
70 
71  void Sel_Layer( wxCommandEvent& event );
72  void OnOkClick( wxCommandEvent& event );
73  void OnCancelClick( wxCommandEvent& event );
74 
75  DECLARE_EVENT_TABLE()
76 };
77 
78 
79 BEGIN_EVENT_TABLE( MOVE_SWAP_LAYER_DIALOG, wxDialog )
81  wxEVT_COMMAND_BUTTON_CLICKED, MOVE_SWAP_LAYER_DIALOG::Sel_Layer )
82 
83  EVT_BUTTON( wxID_OK, MOVE_SWAP_LAYER_DIALOG::OnOkClick )
84 
85  EVT_BUTTON( wxID_CANCEL, MOVE_SWAP_LAYER_DIALOG::OnCancelClick )
86 END_EVENT_TABLE()
87 
88 
90  DIALOG_SHIM( parent, -1, _( "Move Layers:" ), wxPoint( -1, -1 ),
91  wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
92  m_callers_nlayers( aArray )
93 {
94  memset( layer_list, 0, sizeof( layer_list ) );
95 
96  BOARD* board = parent->GetBoard();
97 
98  m_outerBoxSizer = NULL;
99  m_mainBoxSizer = NULL;
100  FlexColumnBoxSizer = NULL;
101  StdDialogButtonSizer = NULL;
102 
103  m_Parent = parent;
104 
105  int item_ID;
106  wxSize goodSize;
107 
108  /* Experimentation has shown that buttons in the Windows version can be
109  * 20 pixels wide and 20 pixels high, but that they need to be 26 pixels
110  * wide and 26 pixels high in the Linux version. (And although the
111  * dimensions of those buttons could be set to 26 pixels wide and 26
112  * pixels high in both of those versions, that would result in a dialog
113  * box which would be excessively high in the Windows version.)
114  */
115 #ifdef __WINDOWS__
116  int w = 20;
117  int h = 20;
118 #else
119  int w = 26;
120  int h = 26;
121 #endif
122 
123  /* As currently implemented, the dimensions of the buttons in the Mac
124  * version are also 26 pixels wide and 26 pixels high. If appropriate,
125  * the above code should be modified as required in the event that those
126  * buttons should be some other size in that version.
127  */
128 
129  m_outerBoxSizer = new wxBoxSizer( wxVERTICAL );
130  SetSizer( m_outerBoxSizer );
131 
132  m_mainBoxSizer = new wxBoxSizer( wxHORIZONTAL );
133  m_outerBoxSizer->Add( m_mainBoxSizer, 1, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 );
134 
135  for( unsigned layer = 0; layer < DIM( layer_list ); ++layer )
136  {
137  // Provide a vertical line to separate the two FlexGrid sizers
138  if( layer == 32 )
139  {
140  wxStaticLine* line = new wxStaticLine( this, -1, wxDefaultPosition,
141  wxDefaultSize, wxLI_VERTICAL );
142  m_mainBoxSizer->Add( line, 0, wxGROW | wxLEFT | wxRIGHT, 5 );
143  }
144 
145  // Provide a separate FlexGrid sizer for every sixteen sets of controls
146  if( layer % 16 == 0 )
147  {
148  /* Each layer has an associated static text string (to identify
149  * that layer), a button (for invoking a child dialog box to
150  * change which layer that the layer is mapped to), and a second
151  * static text string (to depict which layer that the layer has
152  * been mapped to). Each of those items are placed into the left
153  * hand column, middle column, and right hand column (respectively)
154  * of the Flexgrid sizer, and the color of the second text string
155  * is set to fuchsia or blue (to respectively indicate whether the
156  * layer has been swapped to another layer or is not being swapped
157  * at all). (Experimentation has shown that if a text control is
158  * used to depict which layer that each layer is mapped to (instead
159  * of a static text string), then those controls do not behave in
160  * a fully satisfactory manner in the Linux version. Even when the
161  * read-only attribute is specified for all of those controls, they
162  * can still be selected when the arrow keys or Tab key is used
163  * to step through all of the controls within the dialog box, and
164  * directives to set the foreground color of the text of each such
165  * control to blue (to indicate that the text is of a read-only
166  * nature) are disregarded.)
167  *
168  * Specify a FlexGrid sizer with sixteen rows and three columns.
169  */
170  FlexColumnBoxSizer = new wxFlexGridSizer( 16, 3, 0, 0 );
171 
172  // Specify that all of the rows can be expanded.
173  for( int jj = 0; jj < 16; jj++ )
174  {
175  FlexColumnBoxSizer->AddGrowableRow( jj );
176  }
177 
178  // Specify that (just) the right-hand column can be expanded.
179  FlexColumnBoxSizer->AddGrowableCol( 2 );
180 
181  m_mainBoxSizer->Add( FlexColumnBoxSizer, 1, wxGROW | wxTOP, 5 );
182  }
183 
184  /* Provide a text string to identify this layer (with trailing spaces
185  * within that string being purged).
186  */
187  wxStaticText* label = new wxStaticText( this, wxID_STATIC,
188  board->GetLayerName( ToLAYER_ID( layer ) ),
189  wxDefaultPosition, wxDefaultSize,
190  wxALIGN_RIGHT );
191 
192  FlexColumnBoxSizer->Add( label, 0,
193  wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL |
194  wxLEFT | wxBOTTOM,
195  5 );
196 
197  // Provide a button for this layer (which will invoke a child dialog box)
198  item_ID = ID_BUTTON_0 + layer;
199 
200  wxButton* Button = new wxButton( this, item_ID, wxT( "..." ), wxDefaultPosition,
201  wxSize( w, h ), 0 );
202  FlexColumnBoxSizer->Add( Button, 0,
203  wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL |
204  wxLEFT | wxBOTTOM, 5 );
205 
206  /* Provide another text string to specify which layer that this layer
207  * is mapped to, set the initial text to "No Change" (to indicate that
208  * this layer is currently unmapped to any other layer), and set the
209  * foreground color of the text to blue (which also indicates that the
210  * layer is currently unmapped to any other layer).
211  */
212  item_ID = ID_TEXT_0 + layer;
213 
214  /* When the first of these text strings is being added, determine
215  * what size is necessary to to be able to display the longest
216  * string without truncation. Then use that size as the
217  * minimum size for all text strings. (If the minimum
218  * size is not this size, strings can be truncated after
219  * some other layer is selected.)
220  */
221  wxStaticText* text;
222 
223  if( layer == 0 )
224  {
225  text = new wxStaticText( this, item_ID,
226  board->GetLayerName( PCB_LAYER_ID( 0 ) ),
227  wxDefaultPosition, wxDefaultSize, 0 );
228  goodSize = text->GetSize();
229 
230  for( unsigned jj = 1; jj < DIM( layer_list ); ++jj )
231  {
232  text->SetLabel( board->GetLayerName( ToLAYER_ID( jj ) ) );
233 
234  if( goodSize.x < text->GetSize().x )
235  goodSize.x = text->GetSize().x;
236  }
237 
238  text->SetLabel( _( "No Change" ) );
239 
240  if( goodSize.x < text->GetSize().x )
241  goodSize.x = text->GetSize().x;
242  }
243  else
244  {
245  text = new wxStaticText( this, item_ID, _( "No Change" ),
246  wxDefaultPosition, wxDefaultSize, 0 );
247  }
248 
249  text->SetMinSize( goodSize );
250  FlexColumnBoxSizer->Add( text, 1,
251  wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL |
252  wxLEFT | wxRIGHT | wxBOTTOM, 5 );
253  layer_list[layer] = text;
254  }
255 
256  /* Provide spacers to occupy otherwise blank cells within the second
257  * FlexGrid sizer. (Because there are three columns, three spacers
258  * are thus required for each unused row.)
259  for( int ii = 3 * NB_PCB_LAYERS; ii < 96; ii++ )
260  {
261  FlexColumnBoxSizer->Add( 5, h, 0, wxALIGN_CENTER_HORIZONTAL |
262  wxALIGN_CENTER_VERTICAL | wxLEFT |
263  wxRIGHT | wxBOTTOM, 5 );
264  }
265  */
266 
267  // Provide a line to separate the controls which have been provided so far
268  // from the OK and Cancel buttons (which will be provided after this line)
269  wxStaticLine* line = new wxStaticLine( this, -1, wxDefaultPosition,
270  wxDefaultSize, wxLI_HORIZONTAL );
271  m_outerBoxSizer->Add( line, 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 );
272 
273  // Provide a StdDialogButtonSizer to accommodate the OK and Cancel buttons;
274  // using that type of sizer results in those buttons being automatically
275  // located in positions appropriate for each (OS) version of KiCad.
276  StdDialogButtonSizer = new wxStdDialogButtonSizer;
277  m_outerBoxSizer->Add( StdDialogButtonSizer, 0, wxGROW | wxALL, 10 );
278 
279  wxButton* Button = new wxButton( this, wxID_OK, _( "&OK" ),
280  wxDefaultPosition, wxDefaultSize, 0 );
281  Button->SetDefault();
282  StdDialogButtonSizer->AddButton( Button );
283 
284  Button = new wxButton( this, wxID_CANCEL, _( "&Cancel" ),
285  wxDefaultPosition, wxDefaultSize, 0 );
286  StdDialogButtonSizer->AddButton( Button );
287  StdDialogButtonSizer->Realize();
288 
289  // Resize the dialog
290  GetSizer()->SetSizeHints( this );
291 
292  Center();
293 }
294 
295 
296 void MOVE_SWAP_LAYER_DIALOG::Sel_Layer( wxCommandEvent& event )
297 {
298  int ii;
299 
300  ii = event.GetId();
301 
302  if( ii < ID_BUTTON_0 || ii >= ID_BUTTON_0 + PCB_LAYER_ID_COUNT )
303  return;
304 
305  ii = event.GetId() - ID_BUTTON_0;
306 
307  PCB_LAYER_ID layer = m_callers_nlayers[ii];
308 
309  LSET notallowed_mask = IsCopperLayer( ii ) ? LSET::AllNonCuMask() : LSET::AllCuMask();
310 
311  layer = m_Parent->SelectLayer( layer == NO_CHANGE ? ToLAYER_ID( ii ): layer, notallowed_mask );
312 
313  if( !IsValidLayer( layer ) )
314  return;
315 
316  if( layer != m_callers_nlayers[ii] )
317  {
318  m_callers_nlayers[ii] = layer;
319 
320  if( layer == NO_CHANGE || layer == ii )
321  {
322  layer_list[ii]->SetLabel( _( "No Change" ) );
323 
324  // Change the text color to blue (to highlight
325  // that this layer is *not* being swapped)
326  layer_list[ii]->SetForegroundColour( *wxBLUE );
327  }
328  else
329  {
330  layer_list[ii]->SetLabel( m_Parent->GetBoard()->GetLayerName( layer ) );
331 
332  // Change the text color to fuchsia (to highlight
333  // that this layer *is* being swapped)
334  layer_list[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
335  }
336 
337  layer_list[ii]->Refresh();
338  }
339 }
340 
341 
342 void MOVE_SWAP_LAYER_DIALOG::OnCancelClick( wxCommandEvent& event )
343 {
344  EndModal( wxID_CANCEL );
345 }
346 
347 
348 void MOVE_SWAP_LAYER_DIALOG::OnOkClick( wxCommandEvent& event )
349 {
350  EndModal( wxID_OK );
351 }
352 
353 
354 void PCB_EDIT_FRAME::Swap_Layers( wxCommandEvent& event )
355 {
356  PCB_LAYER_ID new_layer[PCB_LAYER_ID_COUNT];
357 
358  for( unsigned i = 0; i < DIM( new_layer ); ++i )
359  new_layer[i] = NO_CHANGE;
360 
361  MOVE_SWAP_LAYER_DIALOG dlg( this, new_layer );
362 
363  if( dlg.ShowModal() != wxID_OK )
364  return; // (Canceled dialog box returns -1 instead)
365 
366  BOARD_COMMIT commit( this );
367  bool hasChanges = false;
368 
369  // Change traces.
370  for( TRACK* segm = GetBoard()->m_Track; segm; segm = segm->Next() )
371  {
372  OnModify();
373 
374  if( segm->Type() == PCB_VIA_T )
375  {
376  VIA* via = (VIA*) segm;
377 
378  if( via->GetViaType() == VIA_THROUGH )
379  continue;
380 
381  PCB_LAYER_ID top_layer, bottom_layer;
382 
383  via->LayerPair( &top_layer, &bottom_layer );
384 
385  if( new_layer[bottom_layer] != NO_CHANGE )
386  bottom_layer = new_layer[bottom_layer];
387 
388  if( new_layer[top_layer] != NO_CHANGE )
389  top_layer = new_layer[top_layer];
390 
391  commit.Modify( via );
392  hasChanges = true;
393 
394  via->SetLayerPair( top_layer, bottom_layer );
395  }
396  else
397  {
398  int jj = segm->GetLayer();
399 
400  if( new_layer[jj] != NO_CHANGE )
401  {
402  commit.Modify( segm );
403  hasChanges = true;
404  segm->SetLayer( new_layer[jj] );
405  }
406  }
407  }
408 
409  // Change deprecated zones segments, only found in very old boards.
410  for( TRACK* segm = GetBoard()->m_SegZoneDeprecated; segm; segm = segm->Next() )
411  {
412  OnModify();
413  int jj = segm->GetLayer();
414 
415  if( new_layer[jj] != NO_CHANGE )
416  segm->SetLayer( new_layer[jj] );
417  }
418 
419  // Change other segments.
420  for( auto item : GetBoard()->Drawings() )
421  {
422  if( item->Type() == PCB_LINE_T )
423  {
424  OnModify();
425 
426  DRAWSEGMENT* drawsegm = (DRAWSEGMENT*) item;
427  int jj = drawsegm->GetLayer();
428 
429  if( new_layer[jj] != NO_CHANGE )
430  {
431  commit.Modify( drawsegm );
432  hasChanges = true;
433  drawsegm->SetLayer( new_layer[jj] );
434  }
435  }
436  }
437 
438  if( hasChanges )
439  commit.Push( "Layers moved" );
440 
441  m_canvas->Refresh( true );
442 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:673
#define DIM(x)
of elements in an array
Definition: macros.h:98
MOVE_SWAP_LAYER_DIALOG(PCB_BASE_FRAME *parent, PCB_LAYER_ID *aArray)
COMMIT & Modify(EDA_ITEM *aItem)
Modifies a given item in the model.
Definition: commit.h:103
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
#define NO_CHANGE
Definition: swap_layers.cpp:45
Class BOARD to handle a board.
static LSET AllNonCuMask()
Function AllNonCuMask returns a mask holding all layer minus CU layers.
Definition: lset.cpp:696
wxStaticText * layer_list[PCB_LAYER_ID_COUNT]
Definition: swap_layers.cpp:69
BOARD * GetBoard() const
swap_layer_id
Definition: swap_layers.cpp:48
EVT_COMMAND_RANGE(ID_BUTTON_0, ID_BUTTON_0+PCB_LAYER_ID_COUNT-1, wxEVT_COMMAND_BUTTON_CLICKED, MOVE_SWAP_LAYER_DIALOG::Sel_Layer) MOVE_SWAP_LAYER_DIALOG
Definition: swap_layers.cpp:80
Class DIALOG_SHIM may sit in the inheritance tree between wxDialog and any class written by wxFormBui...
Definition: dialog_shim.h:70
void Swap_Layers(wxCommandEvent &event)
wxBoxSizer * m_outerBoxSizer
Definition: swap_layers.cpp:63
void OnCancelClick(wxCommandEvent &event)
Functions relatives to tracks, vias and segments used to fill zones.
void Sel_Layer(wxCommandEvent &event)
BOARD_ITEM * Next() const
bool IsValidLayer(LAYER_NUM aLayerId)
Function IsValidLayer tests whether a given integer is a valid layer index, i.e.
PCB_LAYER_ID
A quick note on layer IDs:
Class LSET is a set of PCB_LAYER_IDs.
VIATYPE_T GetViaType() const
Definition: class_track.h:460
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Function GetLayerName returns the name of a layer given by aLayer.
void SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
Function SetLayerPair For a via m_Layer contains the top layer, the other layer is in m_BottomLayer...
PCB_LAYER_ID * m_callers_nlayers
Definition: swap_layers.cpp:68
wxBoxSizer * m_mainBoxSizer
Definition: swap_layers.cpp:64
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
BOARD * GetBoard()
PCB_BASE_FRAME * m_Parent
Definition: swap_layers.cpp:62
Class to handle a graphic segment.
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:169
size_t i
Definition: json11.cpp:597
wxStdDialogButtonSizer * StdDialogButtonSizer
Definition: swap_layers.cpp:66
wxFlexGridSizer * FlexColumnBoxSizer
Definition: swap_layers.cpp:65
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Executes the changes.
virtual BOARD * GetBoard() const
Function GetBoard returns the BOARD in which this BOARD_ITEM resides, or NULL if none.
bool IsCopperLayer(LAYER_NUM aLayerId)
Function IsCopperLayer tests whether a layer is a copper layer.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void OnOkClick(wxCommandEvent &event)
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
PCB_LAYER_ID SelectLayer(PCB_LAYER_ID aDefaultLayer, LSET aNotAllowedLayersMask=LSET(), wxPoint aDlgPosition=wxDefaultPosition)
Install the dialog box for layer selection.
Definition: sel_layer.cpp:221
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:796
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer...