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.

562 lines
19 KiB

5 years ago
  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. * @author Kristoffer Ödmark
  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. #include <wx/clipbrd.h>
  25. #include <wx/log.h>
  26. #include <board.h>
  27. #include <build_version.h>
  28. #include <core/ignore.h>
  29. #include <font/fontconfig.h>
  30. #include <pad.h>
  31. #include <pcb_group.h>
  32. #include <pcb_generator.h>
  33. #include <pcb_text.h>
  34. #include <pcb_table.h>
  35. #include <zone.h>
  36. #include <locale_io.h>
  37. #include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h>
  38. #include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h>
  39. #include <kicad_clipboard.h>
  40. #include <kidialog.h>
  41. #include <io/kicad/kicad_io_utils.h>
  42. CLIPBOARD_IO::CLIPBOARD_IO():
  43. PCB_IO_KICAD_SEXPR(CTL_FOR_CLIPBOARD ),
  44. m_formatter(),
  45. m_writer( &CLIPBOARD_IO::clipboardWriter ),
  46. m_reader( &CLIPBOARD_IO::clipboardReader )
  47. {
  48. m_out = &m_formatter;
  49. }
  50. CLIPBOARD_IO::~CLIPBOARD_IO()
  51. {
  52. }
  53. void CLIPBOARD_IO::SetBoard( BOARD* aBoard )
  54. {
  55. m_board = aBoard;
  56. }
  57. void CLIPBOARD_IO::clipboardWriter( const wxString& aData )
  58. {
  59. wxLogNull doNotLog; // disable logging of failed clipboard actions
  60. auto clipboard = wxTheClipboard;
  61. wxClipboardLocker clipboardLock( clipboard );
  62. if( !clipboardLock || !clipboard->IsOpened() )
  63. return;
  64. clipboard->SetData( new wxTextDataObject( aData ) );
  65. clipboard->Flush();
  66. #ifndef __WXOSX__
  67. // This section exists to return the clipboard data, ensuring it has fully
  68. // been processed by the system clipboard. This appears to be needed for
  69. // extremely large clipboard copies on asynchronous linux clipboard managers
  70. // such as KDE's Klipper. However, a read back of the data on OSX before the
  71. // clipboard is closed seems to cause an ASAN error (heap-buffer-overflow)
  72. // since it uses the cached version of the clipboard data and not the system
  73. // clipboard data.
  74. if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
  75. {
  76. wxTextDataObject data;
  77. clipboard->GetData( data );
  78. ignore_unused( data.GetText() );
  79. }
  80. #endif
  81. }
  82. wxString CLIPBOARD_IO::clipboardReader()
  83. {
  84. wxLogNull doNotLog; // disable logging of failed clipboard actions
  85. auto clipboard = wxTheClipboard;
  86. wxClipboardLocker clipboardLock( clipboard );
  87. if( !clipboardLock )
  88. return wxEmptyString;
  89. if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
  90. {
  91. wxTextDataObject data;
  92. clipboard->GetData( data );
  93. return data.GetText();
  94. }
  95. return wxEmptyString;
  96. }
  97. void CLIPBOARD_IO::SaveSelection( const PCB_SELECTION& aSelected, bool isFootprintEditor )
  98. {
  99. VECTOR2I refPoint( 0, 0 );
  100. // dont even start if the selection is empty
  101. if( aSelected.Empty() )
  102. return;
  103. if( aSelected.HasReferencePoint() )
  104. refPoint = aSelected.GetReferencePoint();
  105. // Prepare net mapping that assures that net codes saved in a file are consecutive integers
  106. m_mapping->SetBoard( m_board );
  107. auto deleteUnselectedCells =
  108. []( PCB_TABLE* aTable )
  109. {
  110. int minCol = aTable->GetColCount();
  111. int maxCol = -1;
  112. int minRow = aTable->GetRowCount();
  113. int maxRow = -1;
  114. for( int row = 0; row < aTable->GetRowCount(); ++row )
  115. {
  116. for( int col = 0; col < aTable->GetColCount(); ++col )
  117. {
  118. PCB_TABLECELL* cell = aTable->GetCell( row, col );
  119. if( cell->IsSelected() )
  120. {
  121. minRow = std::min( minRow, row );
  122. maxRow = std::max( maxRow, row );
  123. minCol = std::min( minCol, col );
  124. maxCol = std::max( maxCol, col );
  125. }
  126. else
  127. {
  128. cell->SetFlags( STRUCT_DELETED );
  129. }
  130. }
  131. }
  132. wxCHECK_MSG( maxCol >= minCol && maxRow >= minRow, /*void*/,
  133. wxT( "No selected cells!" ) );
  134. // aTable is always a clone in the clipboard case
  135. int destRow = 0;
  136. for( int row = minRow; row <= maxRow; row++ )
  137. aTable->SetRowHeight( destRow++, aTable->GetRowHeight( row ) );
  138. int destCol = 0;
  139. for( int col = minCol; col <= maxCol; col++ )
  140. aTable->SetColWidth( destCol++, aTable->GetColWidth( col ) );
  141. aTable->DeleteMarkedCells();
  142. aTable->SetColCount( ( maxCol - minCol ) + 1 );
  143. aTable->Normalize();
  144. };
  145. std::set<PCB_TABLE*> promotedTables;
  146. auto parentIsPromoted =
  147. [&]( PCB_TABLECELL* cell ) -> bool
  148. {
  149. for( PCB_TABLE* table : promotedTables )
  150. {
  151. if( table->m_Uuid == cell->GetParent()->m_Uuid )
  152. return true;
  153. }
  154. return false;
  155. };
  156. if( aSelected.Size() == 1 && aSelected.Front()->Type() == PCB_FOOTPRINT_T )
  157. {
  158. // make the footprint safe to transfer to other pcbs
  159. const FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aSelected.Front() );
  160. // Do not modify existing board
  161. FOOTPRINT newFootprint( *footprint );
  162. for( PAD* pad : newFootprint.Pads() )
  163. pad->SetNetCode( 0 );
  164. // locked means "locked in place"; copied items therefore can't be locked
  165. newFootprint.SetLocked( false );
  166. // locate the reference point at (0, 0) in the copied items
  167. newFootprint.Move( VECTOR2I( -refPoint.x, -refPoint.y ) );
  168. Format( static_cast<BOARD_ITEM*>( &newFootprint ) );
  169. newFootprint.SetParent( nullptr );
  170. newFootprint.SetParentGroup( nullptr );
  171. }
  172. else if( isFootprintEditor )
  173. {
  174. FOOTPRINT partialFootprint( m_board );
  175. // Useful to copy the selection to the board editor (if any), and provides
  176. // a dummy lib id.
  177. // Perhaps not a good Id, but better than a empty id
  178. KIID dummy;
  179. LIB_ID id( "clipboard", dummy.AsString() );
  180. partialFootprint.SetFPID( id );
  181. for( EDA_ITEM* item : aSelected )
  182. {
  183. if( !item->IsBOARD_ITEM() )
  184. continue;
  185. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
  186. BOARD_ITEM* copy = nullptr;
  187. if( PCB_FIELD* field = dynamic_cast<PCB_FIELD*>( item ) )
  188. {
  189. if( field->IsMandatory() )
  190. continue;
  191. }
  192. if( boardItem->Type() == PCB_GROUP_T )
  193. {
  194. copy = static_cast<PCB_GROUP*>( boardItem )->DeepClone();
  195. }
  196. else if( boardItem->Type() == PCB_GENERATOR_T )
  197. {
  198. copy = static_cast<PCB_GENERATOR*>( boardItem )->DeepClone();
  199. }
  200. else if( item->Type() == PCB_TABLECELL_T )
  201. {
  202. if( parentIsPromoted( static_cast<PCB_TABLECELL*>( item ) ) )
  203. continue;
  204. copy = static_cast<BOARD_ITEM*>( item->GetParent()->Clone() );
  205. promotedTables.insert( static_cast<PCB_TABLE*>( copy ) );
  206. }
  207. else
  208. {
  209. copy = static_cast<BOARD_ITEM*>( boardItem->Clone() );
  210. }
  211. // If it is only a footprint, clear the nets from the pads
  212. if( PAD* pad = dynamic_cast<PAD*>( copy ) )
  213. pad->SetNetCode( 0 );
  214. // Don't copy group membership information for the 1st level objects being copied
  215. // since the group they belong to isn't being copied.
  216. copy->SetParentGroup( nullptr );
  217. // Add the pad to the new footprint before moving to ensure the local coords are
  218. // correct
  219. partialFootprint.Add( copy );
  220. // A list of not added items, when adding items to the footprint
  221. // some PCB_TEXT (reference and value) cannot be added to the footprint
  222. std::vector<BOARD_ITEM*> skipped_items;
  223. if( copy->Type() == PCB_GROUP_T || copy->Type() == PCB_GENERATOR_T )
  224. {
  225. copy->RunOnDescendants(
  226. [&]( BOARD_ITEM* descendant )
  227. {
  228. // One cannot add an additional mandatory field to a given footprint:
  229. // only one is allowed. So add only non-mandatory fields.
  230. bool can_add = true;
  231. if( const PCB_FIELD* field = dynamic_cast<const PCB_FIELD*>( item ) )
  232. {
  233. if( field->IsMandatory() )
  234. can_add = false;
  235. }
  236. if( can_add )
  237. partialFootprint.Add( descendant );
  238. else
  239. skipped_items.push_back( descendant );
  240. } );
  241. }
  242. // locate the reference point at (0, 0) in the copied items
  243. copy->Move( -refPoint );
  244. // Now delete items, duplicated but not added:
  245. for( BOARD_ITEM* skipped_item : skipped_items )
  246. {
  247. static_cast<PCB_GROUP*>( copy )->RemoveItem( skipped_item );
  248. skipped_item->SetParentGroup( nullptr );
  249. delete skipped_item;
  250. }
  251. }
  252. // Set the new relative internal local coordinates of copied items
  253. FOOTPRINT* editedFootprint = m_board->Footprints().front();
  254. VECTOR2I moveVector = partialFootprint.GetPosition() + editedFootprint->GetPosition();
  255. partialFootprint.MoveAnchorPosition( moveVector );
  256. for( PCB_TABLE* table : promotedTables )
  257. deleteUnselectedCells( table );
  258. Format( &partialFootprint );
  259. partialFootprint.SetParent( nullptr );
  260. }
  261. else
  262. {
  263. // we will fake being a .kicad_pcb to get the full parser kicking
  264. // This means we also need layers and nets
  265. LOCALE_IO io;
  266. m_formatter.Print( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version %s)",
  267. SEXPR_BOARD_FILE_VERSION,
  268. m_formatter.Quotew( GetMajorMinorVersion() ).c_str() );
  269. formatBoardLayers( m_board );
  270. formatNetInformation( m_board );
  271. for( EDA_ITEM* item : aSelected )
  272. {
  273. if( !item->IsBOARD_ITEM() )
  274. continue;
  275. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
  276. BOARD_ITEM* copy = nullptr;
  277. wxCHECK2( boardItem, continue );
  278. if( boardItem->Type() == PCB_FIELD_T )
  279. {
  280. PCB_FIELD* field = static_cast<PCB_FIELD*>( boardItem );
  281. copy = new PCB_TEXT( m_board );
  282. PCB_TEXT* textItem = static_cast<PCB_TEXT*>( copy );
  283. textItem->SetPosition( field->GetPosition() );
  284. textItem->SetLayer( field->GetLayer() );
  285. textItem->SetHyperlink( field->GetHyperlink() );
  286. textItem->SetText( field->GetText() );
  287. textItem->SetAttributes( field->GetAttributes() );
  288. textItem->SetTextAngle( field->GetDrawRotation() );
  289. if ( textItem->GetText() == wxT( "${VALUE}" ) )
  290. textItem->SetText( boardItem->GetParentFootprint()->GetValue() );
  291. else if ( textItem->GetText() == wxT( "${REFERENCE}" ) )
  292. textItem->SetText( boardItem->GetParentFootprint()->GetReference() );
  293. }
  294. else if( boardItem->Type() == PCB_TEXT_T )
  295. {
  296. copy = static_cast<BOARD_ITEM*>( boardItem->Clone() );
  297. PCB_TEXT* textItem = static_cast<PCB_TEXT*>( copy );
  298. if( textItem->GetText() == wxT( "${VALUE}" ) )
  299. textItem->SetText( boardItem->GetParentFootprint()->GetValue() );
  300. else if( textItem->GetText() == wxT( "${REFERENCE}" ) )
  301. textItem->SetText( boardItem->GetParentFootprint()->GetReference() );
  302. }
  303. else if( boardItem->Type() == PCB_GROUP_T )
  304. {
  305. copy = static_cast<PCB_GROUP*>( boardItem )->DeepClone();
  306. }
  307. else if( boardItem->Type() == PCB_GENERATOR_T )
  308. {
  309. copy = static_cast<PCB_GENERATOR*>( boardItem )->DeepClone();
  310. }
  311. else if( item->Type() == PCB_TABLECELL_T )
  312. {
  313. if( parentIsPromoted( static_cast<PCB_TABLECELL*>( item ) ) )
  314. continue;
  315. copy = static_cast<BOARD_ITEM*>( item->GetParent()->Clone() );
  316. promotedTables.insert( static_cast<PCB_TABLE*>( copy ) );
  317. }
  318. else
  319. {
  320. copy = static_cast<BOARD_ITEM*>( boardItem->Clone() );
  321. }
  322. if( copy )
  323. {
  324. if( copy->Type() == PCB_FIELD_T || copy->Type() == PCB_PAD_T )
  325. {
  326. // Create a parent footprint to own the copied item
  327. FOOTPRINT* footprint = new FOOTPRINT( m_board );
  328. footprint->SetPosition( copy->GetPosition() );
  329. footprint->Add( copy );
  330. // Convert any mandatory fields to user fields. The destination footprint
  331. // will already have its own mandatory fields.
  332. if( PCB_FIELD* field = dynamic_cast<PCB_FIELD*>( copy ) )
  333. {
  334. if( field->IsMandatory() )
  335. field->SetId( footprint->GetNextFieldId() );
  336. }
  337. copy = footprint;
  338. }
  339. copy->SetLocked( false );
  340. copy->SetParent( m_board );
  341. copy->SetParentGroup( nullptr );
  342. // locate the reference point at (0, 0) in the copied items
  343. copy->Move( -refPoint );
  344. if( copy->Type() == PCB_TABLE_T )
  345. {
  346. PCB_TABLE* table = static_cast<PCB_TABLE*>( copy );
  347. if( promotedTables.count( table ) )
  348. deleteUnselectedCells( table );
  349. }
  350. Format( copy );
  351. if( copy->Type() == PCB_GROUP_T || copy->Type() == PCB_GENERATOR_T )
  352. {
  353. copy->RunOnDescendants(
  354. [&]( BOARD_ITEM* descendant )
  355. {
  356. descendant->SetLocked( false );
  357. Format( descendant );
  358. } );
  359. }
  360. delete copy;
  361. }
  362. }
  363. m_formatter.Print( ")" );
  364. }
  365. std::string prettyData = m_formatter.GetString();
  366. KICAD_FORMAT::Prettify( prettyData, true );
  367. // These are placed at the end to minimize the open time of the clipboard
  368. m_writer( wxString( prettyData.c_str(), wxConvUTF8 ) );
  369. }
  370. BOARD_ITEM* CLIPBOARD_IO::Parse()
  371. {
  372. BOARD_ITEM* item;
  373. wxString result = m_reader();
  374. try
  375. {
  376. item = PCB_IO_KICAD_SEXPR::Parse( result );
  377. }
  378. catch (...)
  379. {
  380. item = nullptr;
  381. }
  382. return item;
  383. }
  384. void CLIPBOARD_IO::SaveBoard( const wxString& aFileName, BOARD* aBoard,
  385. const std::map<std::string, UTF8>* aProperties )
  386. {
  387. init( aProperties );
  388. m_board = aBoard; // after init()
  389. // Prepare net mapping that assures that net codes saved in a file are consecutive integers
  390. m_mapping->SetBoard( aBoard );
  391. m_formatter.Print( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version %s)",
  392. SEXPR_BOARD_FILE_VERSION,
  393. m_formatter.Quotew( GetMajorMinorVersion() ).c_str() );
  394. Format( aBoard );
  395. m_formatter.Print( ")" );
  396. std::string prettyData = m_formatter.GetString();
  397. KICAD_FORMAT::Prettify( prettyData, true );
  398. m_writer( wxString( prettyData.c_str(), wxConvUTF8 ) );
  399. }
  400. BOARD* CLIPBOARD_IO::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
  401. const std::map<std::string, UTF8>* aProperties, PROJECT* aProject )
  402. {
  403. std::string result( m_reader().mb_str() );
  404. std::function<bool( wxString, int, wxString, wxString )> queryUser =
  405. [&]( wxString aTitle, int aIcon, wxString aMessage, wxString aAction ) -> bool
  406. {
  407. KIDIALOG dlg( nullptr, aMessage, aTitle, wxOK | wxCANCEL | aIcon );
  408. if( !aAction.IsEmpty() )
  409. dlg.SetOKLabel( aAction );
  410. dlg.DoNotShowCheckbox( aMessage, 0 );
  411. return dlg.ShowModal() == wxID_OK;
  412. };
  413. STRING_LINE_READER reader( result, wxT( "clipboard" ) );
  414. PCB_IO_KICAD_SEXPR_PARSER parser( &reader, aAppendToMe, queryUser );
  415. init( aProperties );
  416. BOARD_ITEM* item;
  417. BOARD* board;
  418. try
  419. {
  420. item = parser.Parse();
  421. }
  422. catch( const FUTURE_FORMAT_ERROR& )
  423. {
  424. // Don't wrap a FUTURE_FORMAT_ERROR in another
  425. throw;
  426. }
  427. catch( const PARSE_ERROR& parse_error )
  428. {
  429. if( parser.IsTooRecent() )
  430. throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
  431. else
  432. throw;
  433. }
  434. if( item->Type() != PCB_T )
  435. {
  436. // The parser loaded something that was valid, but wasn't a board.
  437. THROW_PARSE_ERROR( _( "Clipboard content is not KiCad compatible" ), parser.CurSource(),
  438. parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
  439. }
  440. else
  441. {
  442. board = dynamic_cast<BOARD*>( item );
  443. }
  444. // Give the filename to the board if it's new
  445. if( board && !aAppendToMe )
  446. board->SetFileName( aFileName );
  447. return board;
  448. }