You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
15 KiB

11 years ago
11 years ago
  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. /**
  25. * @file swap_layers.cpp
  26. * @brief Dialog to move board items between layers.
  27. */
  28. #include <fctsys.h>
  29. #include <class_drawpanel.h>
  30. #include <pcb_edit_frame.h>
  31. #include <dialog_shim.h>
  32. #include <class_board.h>
  33. #include <class_track.h>
  34. #include <class_drawsegment.h>
  35. #include <pcbnew.h>
  36. #include <board_commit.h>
  37. #include <wx/statline.h>
  38. #define NO_CHANGE PCB_LAYER_ID(-3)
  39. enum swap_layer_id {
  40. ID_WINEDA_SWAPLAYERFRAME = 1800,
  41. ID_BUTTON_0,
  42. ID_TEXT_0 = ID_BUTTON_0 + PCB_LAYER_ID_COUNT
  43. };
  44. class MOVE_SWAP_LAYER_DIALOG : public DIALOG_SHIM
  45. {
  46. public:
  47. MOVE_SWAP_LAYER_DIALOG( PCB_BASE_FRAME* parent, PCB_LAYER_ID* aArray );
  48. // ~MOVE_SWAP_LAYER_DIALOG() { };
  49. private:
  50. PCB_BASE_FRAME* m_Parent;
  51. wxBoxSizer* m_outerBoxSizer;
  52. wxBoxSizer* m_mainBoxSizer;
  53. wxFlexGridSizer* FlexColumnBoxSizer;
  54. wxStdDialogButtonSizer* StdDialogButtonSizer;
  55. PCB_LAYER_ID* m_callers_nlayers; // DIM() is PCB_LAYER_ID_COUNT
  56. wxStaticText* layer_list[PCB_LAYER_ID_COUNT];
  57. void Sel_Layer( wxCommandEvent& event );
  58. void OnOkClick( wxCommandEvent& event );
  59. void OnCancelClick( wxCommandEvent& event );
  60. DECLARE_EVENT_TABLE()
  61. };
  62. BEGIN_EVENT_TABLE( MOVE_SWAP_LAYER_DIALOG, wxDialog )
  63. EVT_COMMAND_RANGE( ID_BUTTON_0, ID_BUTTON_0 + PCB_LAYER_ID_COUNT - 1,
  64. wxEVT_COMMAND_BUTTON_CLICKED, MOVE_SWAP_LAYER_DIALOG::Sel_Layer )
  65. EVT_BUTTON( wxID_OK, MOVE_SWAP_LAYER_DIALOG::OnOkClick )
  66. EVT_BUTTON( wxID_CANCEL, MOVE_SWAP_LAYER_DIALOG::OnCancelClick )
  67. END_EVENT_TABLE()
  68. MOVE_SWAP_LAYER_DIALOG::MOVE_SWAP_LAYER_DIALOG( PCB_BASE_FRAME* parent, PCB_LAYER_ID* aArray ) :
  69. DIALOG_SHIM( parent, -1, _( "Move Layers:" ), wxPoint( -1, -1 ),
  70. wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
  71. m_callers_nlayers( aArray )
  72. {
  73. memset( layer_list, 0, sizeof( layer_list ) );
  74. BOARD* board = parent->GetBoard();
  75. m_outerBoxSizer = NULL;
  76. m_mainBoxSizer = NULL;
  77. FlexColumnBoxSizer = NULL;
  78. StdDialogButtonSizer = NULL;
  79. m_Parent = parent;
  80. int item_ID;
  81. wxSize goodSize;
  82. /* Experimentation has shown that buttons in the Windows version can be
  83. * 20 pixels wide and 20 pixels high, but that they need to be 26 pixels
  84. * wide and 26 pixels high in the Linux version. (And although the
  85. * dimensions of those buttons could be set to 26 pixels wide and 26
  86. * pixels high in both of those versions, that would result in a dialog
  87. * box which would be excessively high in the Windows version.)
  88. */
  89. #ifdef __WINDOWS__
  90. int w = 20;
  91. int h = 20;
  92. #else
  93. int w = 26;
  94. int h = 26;
  95. #endif
  96. /* As currently implemented, the dimensions of the buttons in the Mac
  97. * version are also 26 pixels wide and 26 pixels high. If appropriate,
  98. * the above code should be modified as required in the event that those
  99. * buttons should be some other size in that version.
  100. */
  101. m_outerBoxSizer = new wxBoxSizer( wxVERTICAL );
  102. SetSizer( m_outerBoxSizer );
  103. m_mainBoxSizer = new wxBoxSizer( wxHORIZONTAL );
  104. m_outerBoxSizer->Add( m_mainBoxSizer, 1, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 );
  105. for( unsigned layer = 0; layer < DIM( layer_list ); ++layer )
  106. {
  107. // Provide a vertical line to separate the two FlexGrid sizers
  108. if( layer == 32 )
  109. {
  110. wxStaticLine* line = new wxStaticLine( this, -1, wxDefaultPosition,
  111. wxDefaultSize, wxLI_VERTICAL );
  112. m_mainBoxSizer->Add( line, 0, wxGROW | wxLEFT | wxRIGHT, 5 );
  113. }
  114. // Provide a separate FlexGrid sizer for every sixteen sets of controls
  115. if( layer % 16 == 0 )
  116. {
  117. /* Each layer has an associated static text string (to identify
  118. * that layer), a button (for invoking a child dialog box to
  119. * change which layer that the layer is mapped to), and a second
  120. * static text string (to depict which layer that the layer has
  121. * been mapped to). Each of those items are placed into the left
  122. * hand column, middle column, and right hand column (respectively)
  123. * of the Flexgrid sizer, and the color of the second text string
  124. * is set to fuchsia or blue (to respectively indicate whether the
  125. * layer has been swapped to another layer or is not being swapped
  126. * at all). (Experimentation has shown that if a text control is
  127. * used to depict which layer that each layer is mapped to (instead
  128. * of a static text string), then those controls do not behave in
  129. * a fully satisfactory manner in the Linux version. Even when the
  130. * read-only attribute is specified for all of those controls, they
  131. * can still be selected when the arrow keys or Tab key is used
  132. * to step through all of the controls within the dialog box, and
  133. * directives to set the foreground color of the text of each such
  134. * control to blue (to indicate that the text is of a read-only
  135. * nature) are disregarded.)
  136. *
  137. * Specify a FlexGrid sizer with sixteen rows and three columns.
  138. */
  139. FlexColumnBoxSizer = new wxFlexGridSizer( 16, 3, 0, 0 );
  140. // Specify that all of the rows can be expanded.
  141. for( int jj = 0; jj < 16; jj++ )
  142. {
  143. FlexColumnBoxSizer->AddGrowableRow( jj );
  144. }
  145. // Specify that (just) the right-hand column can be expanded.
  146. FlexColumnBoxSizer->AddGrowableCol( 2 );
  147. m_mainBoxSizer->Add( FlexColumnBoxSizer, 1, wxGROW | wxTOP, 5 );
  148. }
  149. /* Provide a text string to identify this layer (with trailing spaces
  150. * within that string being purged).
  151. */
  152. wxStaticText* label = new wxStaticText( this, wxID_STATIC,
  153. board->GetLayerName( ToLAYER_ID( layer ) ),
  154. wxDefaultPosition, wxDefaultSize,
  155. wxALIGN_RIGHT );
  156. FlexColumnBoxSizer->Add( label, 0,
  157. wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL |
  158. wxLEFT | wxBOTTOM,
  159. 5 );
  160. // Provide a button for this layer (which will invoke a child dialog box)
  161. item_ID = ID_BUTTON_0 + layer;
  162. wxButton* Button = new wxButton( this, item_ID, wxT( "..." ), wxDefaultPosition,
  163. wxSize( w, h ), 0 );
  164. FlexColumnBoxSizer->Add( Button, 0,
  165. wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL |
  166. wxLEFT | wxBOTTOM, 5 );
  167. /* Provide another text string to specify which layer that this layer
  168. * is mapped to, set the initial text to "No Change" (to indicate that
  169. * this layer is currently unmapped to any other layer), and set the
  170. * foreground color of the text to blue (which also indicates that the
  171. * layer is currently unmapped to any other layer).
  172. */
  173. item_ID = ID_TEXT_0 + layer;
  174. /* When the first of these text strings is being added, determine
  175. * what size is necessary to to be able to display the longest
  176. * string without truncation. Then use that size as the
  177. * minimum size for all text strings. (If the minimum
  178. * size is not this size, strings can be truncated after
  179. * some other layer is selected.)
  180. */
  181. wxStaticText* text;
  182. if( layer == 0 )
  183. {
  184. text = new wxStaticText( this, item_ID,
  185. board->GetLayerName( PCB_LAYER_ID( 0 ) ),
  186. wxDefaultPosition, wxDefaultSize, 0 );
  187. goodSize = text->GetSize();
  188. for( unsigned jj = 1; jj < DIM( layer_list ); ++jj )
  189. {
  190. text->SetLabel( board->GetLayerName( ToLAYER_ID( jj ) ) );
  191. if( goodSize.x < text->GetSize().x )
  192. goodSize.x = text->GetSize().x;
  193. }
  194. text->SetLabel( _( "No Change" ) );
  195. if( goodSize.x < text->GetSize().x )
  196. goodSize.x = text->GetSize().x;
  197. }
  198. else
  199. {
  200. text = new wxStaticText( this, item_ID, _( "No Change" ),
  201. wxDefaultPosition, wxDefaultSize, 0 );
  202. }
  203. text->SetMinSize( goodSize );
  204. FlexColumnBoxSizer->Add( text, 1,
  205. wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL |
  206. wxLEFT | wxRIGHT | wxBOTTOM, 5 );
  207. layer_list[layer] = text;
  208. }
  209. /* Provide spacers to occupy otherwise blank cells within the second
  210. * FlexGrid sizer. (Because there are three columns, three spacers
  211. * are thus required for each unused row.)
  212. for( int ii = 3 * NB_PCB_LAYERS; ii < 96; ii++ )
  213. {
  214. FlexColumnBoxSizer->Add( 5, h, 0, wxALIGN_CENTER_HORIZONTAL |
  215. wxALIGN_CENTER_VERTICAL | wxLEFT |
  216. wxRIGHT | wxBOTTOM, 5 );
  217. }
  218. */
  219. // Provide a line to separate the controls which have been provided so far
  220. // from the OK and Cancel buttons (which will be provided after this line)
  221. wxStaticLine* line = new wxStaticLine( this, -1, wxDefaultPosition,
  222. wxDefaultSize, wxLI_HORIZONTAL );
  223. m_outerBoxSizer->Add( line, 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 );
  224. // Provide a StdDialogButtonSizer to accommodate the OK and Cancel buttons;
  225. // using that type of sizer results in those buttons being automatically
  226. // located in positions appropriate for each (OS) version of KiCad.
  227. StdDialogButtonSizer = new wxStdDialogButtonSizer;
  228. m_outerBoxSizer->Add( StdDialogButtonSizer, 0, wxGROW | wxALL, 10 );
  229. wxButton* Button = new wxButton( this, wxID_OK, _( "&OK" ),
  230. wxDefaultPosition, wxDefaultSize, 0 );
  231. Button->SetDefault();
  232. StdDialogButtonSizer->AddButton( Button );
  233. Button = new wxButton( this, wxID_CANCEL, _( "&Cancel" ),
  234. wxDefaultPosition, wxDefaultSize, 0 );
  235. StdDialogButtonSizer->AddButton( Button );
  236. StdDialogButtonSizer->Realize();
  237. // Resize the dialog
  238. GetSizer()->SetSizeHints( this );
  239. Center();
  240. }
  241. void MOVE_SWAP_LAYER_DIALOG::Sel_Layer( wxCommandEvent& event )
  242. {
  243. int ii;
  244. ii = event.GetId();
  245. if( ii < ID_BUTTON_0 || ii >= ID_BUTTON_0 + PCB_LAYER_ID_COUNT )
  246. return;
  247. ii = event.GetId() - ID_BUTTON_0;
  248. PCB_LAYER_ID layer = m_callers_nlayers[ii];
  249. LSET notallowed_mask = IsCopperLayer( ii ) ? LSET::AllNonCuMask() : LSET::AllCuMask();
  250. layer = m_Parent->SelectLayer( layer == NO_CHANGE ? ToLAYER_ID( ii ): layer, notallowed_mask );
  251. if( !IsValidLayer( layer ) )
  252. return;
  253. if( layer != m_callers_nlayers[ii] )
  254. {
  255. m_callers_nlayers[ii] = layer;
  256. if( layer == NO_CHANGE || layer == ii )
  257. {
  258. layer_list[ii]->SetLabel( _( "No Change" ) );
  259. // Change the text color to blue (to highlight
  260. // that this layer is *not* being swapped)
  261. layer_list[ii]->SetForegroundColour( *wxBLUE );
  262. }
  263. else
  264. {
  265. layer_list[ii]->SetLabel( m_Parent->GetBoard()->GetLayerName( layer ) );
  266. // Change the text color to fuchsia (to highlight
  267. // that this layer *is* being swapped)
  268. layer_list[ii]->SetForegroundColour( wxColour( 255, 0, 128 ) );
  269. }
  270. layer_list[ii]->Refresh();
  271. }
  272. }
  273. void MOVE_SWAP_LAYER_DIALOG::OnCancelClick( wxCommandEvent& event )
  274. {
  275. EndModal( wxID_CANCEL );
  276. }
  277. void MOVE_SWAP_LAYER_DIALOG::OnOkClick( wxCommandEvent& event )
  278. {
  279. EndModal( wxID_OK );
  280. }
  281. void PCB_EDIT_FRAME::Swap_Layers( wxCommandEvent& event )
  282. {
  283. PCB_LAYER_ID new_layer[PCB_LAYER_ID_COUNT];
  284. for( unsigned i = 0; i < DIM( new_layer ); ++i )
  285. new_layer[i] = NO_CHANGE;
  286. MOVE_SWAP_LAYER_DIALOG dlg( this, new_layer );
  287. if( dlg.ShowModal() != wxID_OK )
  288. return; // (Canceled dialog box returns -1 instead)
  289. BOARD_COMMIT commit( this );
  290. bool hasChanges = false;
  291. // Change traces.
  292. for( TRACK* segm = GetBoard()->m_Track; segm; segm = segm->Next() )
  293. {
  294. OnModify();
  295. if( segm->Type() == PCB_VIA_T )
  296. {
  297. VIA* via = (VIA*) segm;
  298. if( via->GetViaType() == VIA_THROUGH )
  299. continue;
  300. PCB_LAYER_ID top_layer, bottom_layer;
  301. via->LayerPair( &top_layer, &bottom_layer );
  302. if( new_layer[bottom_layer] != NO_CHANGE )
  303. bottom_layer = new_layer[bottom_layer];
  304. if( new_layer[top_layer] != NO_CHANGE )
  305. top_layer = new_layer[top_layer];
  306. commit.Modify( via );
  307. hasChanges = true;
  308. via->SetLayerPair( top_layer, bottom_layer );
  309. }
  310. else
  311. {
  312. int jj = segm->GetLayer();
  313. if( new_layer[jj] != NO_CHANGE )
  314. {
  315. commit.Modify( segm );
  316. hasChanges = true;
  317. segm->SetLayer( new_layer[jj] );
  318. }
  319. }
  320. }
  321. // Change deprecated zones segments, only found in very old boards.
  322. for( TRACK* segm = GetBoard()->m_SegZoneDeprecated; segm; segm = segm->Next() )
  323. {
  324. OnModify();
  325. int jj = segm->GetLayer();
  326. if( new_layer[jj] != NO_CHANGE )
  327. segm->SetLayer( new_layer[jj] );
  328. }
  329. // Change other segments.
  330. for( auto item : GetBoard()->Drawings() )
  331. {
  332. if( item->Type() == PCB_LINE_T )
  333. {
  334. OnModify();
  335. DRAWSEGMENT* drawsegm = (DRAWSEGMENT*) item;
  336. int jj = drawsegm->GetLayer();
  337. if( new_layer[jj] != NO_CHANGE )
  338. {
  339. commit.Modify( drawsegm );
  340. hasChanges = true;
  341. drawsegm->SetLayer( new_layer[jj] );
  342. }
  343. }
  344. }
  345. if( hasChanges )
  346. commit.Push( "Layers moved" );
  347. m_canvas->Refresh( true );
  348. }