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.

485 lines
16 KiB

5 years ago
2 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 KiCad Developers, see AUTHORS.TXT for contributors.
  5. * Copyright (C) 2017-2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Kristoffer Ödmark
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <wx/clipbrd.h>
  26. #include <wx/log.h>
  27. #include <board.h>
  28. #include <build_version.h>
  29. #include <core/ignore.h>
  30. #include <pad.h>
  31. #include <pcb_group.h>
  32. #include <pcb_generator.h>
  33. #include <pcb_shape.h>
  34. #include <pcb_text.h>
  35. #include <pcb_textbox.h>
  36. #include <zone.h>
  37. #include <locale_io.h>
  38. #include <netinfo.h>
  39. #include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h>
  40. #include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h>
  41. #include <kicad_clipboard.h>
  42. #include "confirm.h"
  43. CLIPBOARD_IO::CLIPBOARD_IO():
  44. PCB_IO_KICAD_SEXPR(CTL_FOR_CLIPBOARD ),
  45. m_formatter()
  46. {
  47. m_out = &m_formatter;
  48. }
  49. CLIPBOARD_IO::~CLIPBOARD_IO()
  50. {
  51. }
  52. void CLIPBOARD_IO::SetBoard( BOARD* aBoard )
  53. {
  54. m_board = aBoard;
  55. }
  56. void CLIPBOARD_IO::SaveSelection( const PCB_SELECTION& aSelected, bool isFootprintEditor )
  57. {
  58. VECTOR2I refPoint( 0, 0 );
  59. // dont even start if the selection is empty
  60. if( aSelected.Empty() )
  61. return;
  62. if( aSelected.HasReferencePoint() )
  63. refPoint = aSelected.GetReferencePoint();
  64. // Prepare net mapping that assures that net codes saved in a file are consecutive integers
  65. m_mapping->SetBoard( m_board );
  66. if( aSelected.Size() == 1 && aSelected.Front()->Type() == PCB_FOOTPRINT_T )
  67. {
  68. // make the footprint safe to transfer to other pcbs
  69. const FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aSelected.Front() );
  70. // Do not modify existing board
  71. FOOTPRINT newFootprint( *footprint );
  72. for( PAD* pad : newFootprint.Pads() )
  73. pad->SetNetCode( 0 );
  74. // locked means "locked in place"; copied items therefore can't be locked
  75. newFootprint.SetLocked( false );
  76. // locate the reference point at (0, 0) in the copied items
  77. newFootprint.Move( VECTOR2I( -refPoint.x, -refPoint.y ) );
  78. Format( static_cast<BOARD_ITEM*>( &newFootprint ) );
  79. newFootprint.SetParent( nullptr );
  80. newFootprint.SetParentGroup( nullptr );
  81. }
  82. else if( isFootprintEditor )
  83. {
  84. FOOTPRINT partialFootprint( m_board );
  85. // Useful to copy the selection to the board editor (if any), and provides
  86. // a dummy lib id.
  87. // Perhaps not a good Id, but better than a empty id
  88. KIID dummy;
  89. LIB_ID id( "clipboard", dummy.AsString() );
  90. partialFootprint.SetFPID( id );
  91. for( const EDA_ITEM* item : aSelected )
  92. {
  93. const BOARD_ITEM* boardItem = dynamic_cast<const BOARD_ITEM*>( item );
  94. const PCB_GROUP* group = dynamic_cast<const PCB_GROUP*>( item );
  95. BOARD_ITEM* clone = nullptr;
  96. wxCHECK2( boardItem, continue );
  97. if( const PCB_FIELD* field = dynamic_cast<const PCB_FIELD*>( item ) )
  98. {
  99. if( field->IsMandatoryField() )
  100. continue;
  101. }
  102. if( group )
  103. clone = static_cast<BOARD_ITEM*>( group->DeepClone() );
  104. else
  105. clone = static_cast<BOARD_ITEM*>( boardItem->Clone() );
  106. // If it is only a footprint, clear the nets from the pads
  107. if( PAD* pad = dynamic_cast<PAD*>( clone ) )
  108. pad->SetNetCode( 0 );
  109. // Don't copy group membership information for the 1st level objects being copied
  110. // since the group they belong to isn't being copied.
  111. clone->SetParentGroup( nullptr );
  112. // Add the pad to the new footprint before moving to ensure the local coords are
  113. // correct
  114. partialFootprint.Add( clone );
  115. // A list of not added items, when adding items to the footprint
  116. // some PCB_TEXT (reference and value) cannot be added to the footprint
  117. std::vector<BOARD_ITEM*> skipped_items;
  118. if( group )
  119. {
  120. clone->RunOnDescendants(
  121. [&]( BOARD_ITEM* descendant )
  122. {
  123. // One cannot add an additional mandatory field to a given footprint:
  124. // only one is allowed. So add only non-mandatory fields.
  125. bool can_add = true;
  126. if( const PCB_FIELD* field = dynamic_cast<const PCB_FIELD*>( item ) )
  127. {
  128. if( field->IsMandatoryField() )
  129. can_add = false;
  130. }
  131. if( can_add )
  132. partialFootprint.Add( descendant );
  133. else
  134. skipped_items.push_back( descendant );
  135. } );
  136. }
  137. // locate the reference point at (0, 0) in the copied items
  138. clone->Move( -refPoint );
  139. // Now delete items, duplicated but not added:
  140. for( BOARD_ITEM* skp_item : skipped_items )
  141. {
  142. static_cast<PCB_GROUP*>( clone )->RemoveItem( skp_item );
  143. skp_item->SetParentGroup( nullptr );
  144. delete skp_item;
  145. }
  146. }
  147. // Set the new relative internal local coordinates of copied items
  148. FOOTPRINT* editedFootprint = m_board->Footprints().front();
  149. VECTOR2I moveVector = partialFootprint.GetPosition() + editedFootprint->GetPosition();
  150. partialFootprint.MoveAnchorPosition( moveVector );
  151. Format( &partialFootprint, 0 );
  152. partialFootprint.SetParent( nullptr );
  153. }
  154. else
  155. {
  156. // we will fake being a .kicad_pcb to get the full parser kicking
  157. // This means we also need layers and nets
  158. LOCALE_IO io;
  159. m_formatter.Print( 0, "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version \"%s\")\n",
  160. SEXPR_BOARD_FILE_VERSION, GetMajorMinorVersion().c_str().AsChar() );
  161. m_formatter.Print( 0, "\n" );
  162. formatBoardLayers( m_board );
  163. formatNetInformation( m_board );
  164. m_formatter.Print( 0, "\n" );
  165. for( EDA_ITEM* item : aSelected )
  166. {
  167. BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item );
  168. BOARD_ITEM* copy = nullptr;
  169. wxCHECK2( boardItem, continue );
  170. if( boardItem->Type() == PCB_FIELD_T )
  171. {
  172. PCB_FIELD* field = static_cast<PCB_FIELD*>( boardItem );
  173. copy = new PCB_TEXT( m_board );
  174. PCB_TEXT* textItem = static_cast<PCB_TEXT*>( copy );
  175. textItem->SetPosition( field->GetPosition() );
  176. textItem->SetLayer( field->GetLayer() );
  177. textItem->SetHyperlink( field->GetHyperlink() );
  178. textItem->SetText( field->GetText() );
  179. textItem->SetAttributes( field->GetAttributes() );
  180. textItem->SetTextAngle( field->GetDrawRotation() );
  181. if ( textItem->GetText() == wxT( "${VALUE}" ) )
  182. textItem->SetText( boardItem->GetParentFootprint()->GetValue() );
  183. else if ( textItem->GetText() == wxT( "${REFERENCE}" ) )
  184. textItem->SetText( boardItem->GetParentFootprint()->GetReference() );
  185. }
  186. else if( boardItem->Type() == PCB_TEXT_T )
  187. {
  188. copy = static_cast<BOARD_ITEM*>( boardItem->Clone() );
  189. PCB_TEXT* textItem = static_cast<PCB_TEXT*>( copy );
  190. if( textItem->GetText() == wxT( "${VALUE}" ) )
  191. textItem->SetText( boardItem->GetParentFootprint()->GetValue() );
  192. else if( textItem->GetText() == wxT( "${REFERENCE}" ) )
  193. textItem->SetText( boardItem->GetParentFootprint()->GetReference() );
  194. }
  195. else if( boardItem->Type() == PCB_GROUP_T )
  196. {
  197. copy = static_cast<PCB_GROUP*>( boardItem )->DeepClone();
  198. }
  199. else if( boardItem->Type() == PCB_GENERATOR_T )
  200. {
  201. copy = static_cast<PCB_GENERATOR*>( boardItem )->DeepClone();
  202. }
  203. else
  204. {
  205. copy = static_cast<BOARD_ITEM*>( boardItem->Clone() );
  206. }
  207. if( copy )
  208. {
  209. if( copy->Type() == PCB_FIELD_T || copy->Type() == PCB_PAD_T )
  210. {
  211. // Create a parent footprint to own the copied item
  212. FOOTPRINT* footprint = new FOOTPRINT( m_board );
  213. footprint->SetPosition( copy->GetPosition() );
  214. footprint->Add( copy );
  215. // Convert any mandatory fields to user fields. The destination footprint
  216. // will already have its own mandatory fields.
  217. if( PCB_FIELD* field = dynamic_cast<PCB_FIELD*>( copy ) )
  218. {
  219. if( field->IsMandatoryField() )
  220. field->SetId( footprint->GetFieldCount() );
  221. }
  222. copy = footprint;
  223. }
  224. copy->SetLocked( false );
  225. // locate the reference point at (0, 0) in the copied items
  226. copy->Move( -refPoint );
  227. Format( copy, 1 );
  228. if( copy->Type() == PCB_GROUP_T || copy->Type() == PCB_GENERATOR_T )
  229. {
  230. copy->RunOnDescendants(
  231. [&]( BOARD_ITEM* titem )
  232. {
  233. titem->SetLocked( false );
  234. Format( titem, 1 );
  235. } );
  236. }
  237. copy->SetParentGroup( nullptr );
  238. delete copy;
  239. }
  240. }
  241. m_formatter.Print( 0, "\n)" );
  242. }
  243. // These are placed at the end to minimize the open time of the clipboard
  244. wxLogNull doNotLog; // disable logging of failed clipboard actions
  245. auto clipboard = wxTheClipboard;
  246. wxClipboardLocker clipboardLock( clipboard );
  247. if( !clipboardLock || !clipboard->IsOpened() )
  248. return;
  249. clipboard->SetData( new wxTextDataObject( wxString( m_formatter.GetString().c_str(),
  250. wxConvUTF8 ) ) );
  251. clipboard->Flush();
  252. #ifndef __WXOSX__
  253. // This section exists to return the clipboard data, ensuring it has fully
  254. // been processed by the system clipboard. This appears to be needed for
  255. // extremely large clipboard copies on asynchronous linux clipboard managers
  256. // such as KDE's Klipper. However, a read back of the data on OSX before the
  257. // clipboard is closed seems to cause an ASAN error (heap-buffer-overflow)
  258. // since it uses the cached version of the clipboard data and not the system
  259. // clipboard data.
  260. if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
  261. {
  262. wxTextDataObject data;
  263. clipboard->GetData( data );
  264. ignore_unused( data.GetText() );
  265. }
  266. #endif
  267. }
  268. BOARD_ITEM* CLIPBOARD_IO::Parse()
  269. {
  270. BOARD_ITEM* item;
  271. wxString result;
  272. wxLogNull doNotLog; // disable logging of failed clipboard actions
  273. auto clipboard = wxTheClipboard;
  274. wxClipboardLocker clipboardLock( clipboard );
  275. if( !clipboardLock )
  276. return nullptr;
  277. if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
  278. {
  279. wxTextDataObject data;
  280. clipboard->GetData( data );
  281. result = data.GetText();
  282. }
  283. try
  284. {
  285. item = PCB_IO_KICAD_SEXPR::Parse( result );
  286. }
  287. catch (...)
  288. {
  289. item = nullptr;
  290. }
  291. return item;
  292. }
  293. void CLIPBOARD_IO::SaveBoard( const wxString& aFileName, BOARD* aBoard,
  294. const STRING_UTF8_MAP* aProperties )
  295. {
  296. init( aProperties );
  297. m_board = aBoard; // after init()
  298. // Prepare net mapping that assures that net codes saved in a file are consecutive integers
  299. m_mapping->SetBoard( aBoard );
  300. STRING_FORMATTER formatter;
  301. m_out = &formatter;
  302. m_out->Print( 0, "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version \"%s\")\n", SEXPR_BOARD_FILE_VERSION, GetMajorMinorVersion().c_str().AsChar() );
  303. Format( aBoard, 1 );
  304. m_out->Print( 0, ")\n" );
  305. wxLogNull doNotLog; // disable logging of failed clipboard actions
  306. auto clipboard = wxTheClipboard;
  307. wxClipboardLocker clipboardLock( clipboard );
  308. if( !clipboardLock )
  309. return;
  310. clipboard->SetData( new wxTextDataObject(
  311. wxString( m_formatter.GetString().c_str(), wxConvUTF8 ) ) );
  312. clipboard->Flush();
  313. // This section exists to return the clipboard data, ensuring it has fully
  314. // been processed by the system clipboard. This appears to be needed for
  315. // extremely large clipboard copies on asynchronous linux clipboard managers
  316. // such as KDE's Klipper
  317. if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
  318. {
  319. wxTextDataObject data;
  320. clipboard->GetData( data );
  321. ignore_unused( data.GetText() );
  322. }
  323. }
  324. BOARD* CLIPBOARD_IO::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
  325. const STRING_UTF8_MAP* aProperties, PROJECT* aProject )
  326. {
  327. std::string result;
  328. wxLogNull doNotLog; // disable logging of failed clipboard actions
  329. auto clipboard = wxTheClipboard;
  330. wxClipboardLocker clipboardLock( clipboard );
  331. if( !clipboardLock )
  332. return nullptr;
  333. if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
  334. {
  335. wxTextDataObject data;
  336. clipboard->GetData( data );
  337. result = data.GetText().mb_str();
  338. }
  339. std::function<bool( wxString, int, wxString, wxString )> queryUser =
  340. [&]( wxString aTitle, int aIcon, wxString aMessage, wxString aAction ) -> bool
  341. {
  342. KIDIALOG dlg( nullptr, aMessage, aTitle, wxOK | wxCANCEL | aIcon );
  343. if( !aAction.IsEmpty() )
  344. dlg.SetOKLabel( aAction );
  345. dlg.DoNotShowCheckbox( aMessage, 0 );
  346. return dlg.ShowModal() == wxID_OK;
  347. };
  348. STRING_LINE_READER reader( result, wxT( "clipboard" ) );
  349. PCB_IO_KICAD_SEXPR_PARSER parser( &reader, aAppendToMe, queryUser );
  350. init( aProperties );
  351. BOARD_ITEM* item;
  352. BOARD* board;
  353. try
  354. {
  355. item = parser.Parse();
  356. }
  357. catch( const FUTURE_FORMAT_ERROR& )
  358. {
  359. // Don't wrap a FUTURE_FORMAT_ERROR in another
  360. throw;
  361. }
  362. catch( const PARSE_ERROR& parse_error )
  363. {
  364. if( parser.IsTooRecent() )
  365. throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
  366. else
  367. throw;
  368. }
  369. if( item->Type() != PCB_T )
  370. {
  371. // The parser loaded something that was valid, but wasn't a board.
  372. THROW_PARSE_ERROR( _( "Clipboard content is not KiCad compatible" ), parser.CurSource(),
  373. parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
  374. }
  375. else
  376. {
  377. board = dynamic_cast<BOARD*>( item );
  378. }
  379. // Give the filename to the board if it's new
  380. if( board && !aAppendToMe )
  381. board->SetFileName( aFileName );
  382. return board;
  383. }