KiCad PCB EDA Suite
kicad_clipboard.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) 2017 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 
25 #include <wx/clipbrd.h>
26 #include <wx/log.h>
27 
28 #include <build_version.h>
29 #include <board.h>
30 #include <ignore.h>
31 #include <pad.h>
32 #include <pcb_group.h>
33 #include <pcb_shape.h>
34 #include <pcb_text.h>
35 #include <fp_text.h>
36 #include <zone.h>
37 #include <locale_io.h>
38 #include <netinfo.h>
40 
42 #include <kicad_clipboard.h>
43 
46  m_formatter()
47 {
48  m_out = &m_formatter;
49 }
50 
51 
53 {
54 }
55 
56 
58 {
59  m_board = aBoard;
60 }
61 
62 
63 void CLIPBOARD_IO::SaveSelection( const PCB_SELECTION& aSelected, bool isFootprintEditor )
64 {
65  VECTOR2I refPoint( 0, 0 );
66 
67  // dont even start if the selection is empty
68  if( aSelected.Empty() )
69  return;
70 
71  if( aSelected.HasReferencePoint() )
72  refPoint = aSelected.GetReferencePoint();
73 
74  // Prepare net mapping that assures that net codes saved in a file are consecutive integers
76 
77  if( aSelected.Size() == 1 && aSelected.Front()->Type() == PCB_FOOTPRINT_T )
78  {
79  // make the footprint safe to transfer to other pcbs
80  const FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aSelected.Front() );
81  // Do not modify existing board
82  FOOTPRINT newFootprint( *footprint );
83 
84  for( PAD* pad : newFootprint.Pads() )
85  pad->SetNetCode( 0 );
86 
87  // locked means "locked in place"; copied items therefore can't be locked
88  newFootprint.SetLocked( false );
89 
90  // locate the reference point at (0, 0) in the copied items
91  newFootprint.Move( wxPoint( -refPoint.x, -refPoint.y ) );
92 
93  Format( static_cast<BOARD_ITEM*>( &newFootprint ) );
94  }
95  else if( isFootprintEditor )
96  {
97  FOOTPRINT partialFootprint( m_board );
98 
99  // Useful to copy the selection to the board editor (if any), and provides
100  // a dummy lib id.
101  // Perhaps not a good Id, but better than a empty id
102  KIID dummy;
103  LIB_ID id( "clipboard", dummy.AsString() );
104  partialFootprint.SetFPID( id );
105 
106  for( const EDA_ITEM* item : aSelected )
107  {
108  const PCB_GROUP* group = dynamic_cast<const PCB_GROUP*>( item );
109  BOARD_ITEM* clone;
110 
111  if( const FP_TEXT* text = dyn_cast<const FP_TEXT*>( item ) )
112  {
113  if( text->GetType() != FP_TEXT::TEXT_is_DIVERS )
114  continue;
115  }
116 
117  if( group )
118  clone = static_cast<BOARD_ITEM*>( group->DeepClone() );
119  else
120  clone = static_cast<BOARD_ITEM*>( item->Clone() );
121 
122  // If it is only a footprint, clear the nets from the pads
123  if( PAD* pad = dyn_cast<PAD*>( clone ) )
124  pad->SetNetCode( 0 );
125 
126  // Add the pad to the new footprint before moving to ensure the local coords are
127  // correct
128  partialFootprint.Add( clone );
129 
130  // A list of not added items, when adding items to the footprint
131  // some FP_TEXT (reference and value) cannot be added to the footprint
132  std::vector<BOARD_ITEM*> skipped_items;
133 
134  if( group )
135  {
136  static_cast<PCB_GROUP*>( clone )->RunOnDescendants(
137  [&]( BOARD_ITEM* descendant )
138  {
139  // One cannot add a text reference or value to a given footprint:
140  // only one is allowed. So add only FP_TEXT::TEXT_is_DIVERS
141  bool can_add = true;
142 
143  if( const FP_TEXT* text = dyn_cast<const FP_TEXT*>( descendant ) )
144  {
145  if( text->GetType() != FP_TEXT::TEXT_is_DIVERS )
146  can_add = false;
147  }
148 
149  if( can_add )
150  partialFootprint.Add( descendant );
151  else
152  skipped_items.push_back( descendant );
153  } );
154  }
155 
156  // locate the reference point at (0, 0) in the copied items
157  clone->Move( (wxPoint) -refPoint );
158 
159  // Now delete items, duplicated but not added:
160  for( BOARD_ITEM* skp_item : skipped_items )
161  delete skp_item;
162  }
163 
164  // Set the new relative internal local coordinates of copied items
165  FOOTPRINT* editedFootprint = m_board->Footprints().front();
166  wxPoint moveVector = partialFootprint.GetPosition() + editedFootprint->GetPosition();
167 
168  partialFootprint.MoveAnchorPosition( moveVector );
169 
170  Format( &partialFootprint, 0 );
171  }
172  else
173  {
174  // we will fake being a .kicad_pcb to get the full parser kicking
175  // This means we also need layers and nets
176  LOCALE_IO io;
177 
178  m_formatter.Print( 0, "(kicad_pcb (version %d) (generator pcbnew)\n",
180 
181  m_formatter.Print( 0, "\n" );
182 
185 
186  m_formatter.Print( 0, "\n" );
187 
188  for( EDA_ITEM* i : aSelected )
189  {
190  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( i );
191  BOARD_ITEM* copy = nullptr;
192 
193  if( item->Type() == PCB_FP_SHAPE_T )
194  {
195  // Convert to PCB_SHAPE_T
196  copy = (BOARD_ITEM*) reinterpret_cast<PCB_SHAPE*>( item )->Clone();
197  copy->SetLayer( item->GetLayer() );
198  }
199  else if( item->Type() == PCB_FP_TEXT_T )
200  {
201  // Convert to PCB_TEXT_T
202  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item->GetParent() );
203  FP_TEXT* fp_text = static_cast<FP_TEXT*>( item );
204  PCB_TEXT* pcb_text = new PCB_TEXT( m_board );
205 
206  if( fp_text->GetText() == wxT( "${VALUE}" ) )
207  pcb_text->SetText( footprint->GetValue() );
208  else if( fp_text->GetText() == wxT( "${REFERENCE}" ) )
209  pcb_text->SetText( footprint->GetReference() );
210  else
211  pcb_text->CopyText( *fp_text );
212 
213  pcb_text->SetEffects( *fp_text );
214  pcb_text->SetLayer( fp_text->GetLayer() );
215  copy = pcb_text;
216  }
217  else if( item->Type() == PCB_PAD_T )
218  {
219  // Create a parent to own the copied pad
220  FOOTPRINT* footprint = new FOOTPRINT( m_board );
221  PAD* pad = (PAD*) item->Clone();
222 
223  footprint->SetPosition( pad->GetPosition() );
224  pad->SetPos0( wxPoint() );
225  footprint->Add( pad );
226  copy = footprint;
227  }
228  else if( item->Type() == PCB_FP_ZONE_T )
229  {
230  // Convert to PCB_ZONE_T
231  ZONE* zone = new ZONE( m_board );
232  zone->InitDataFromSrcInCopyCtor( *static_cast<ZONE*>( item ) );
233  copy = zone;
234  }
235  else if( item->Type() == PCB_GROUP_T )
236  {
237  copy = static_cast<PCB_GROUP*>( item )->DeepClone();
238  }
239  else
240  {
241  copy = static_cast<BOARD_ITEM*>( item->Clone() );
242  }
243 
244  auto prepItem = [&]( BOARD_ITEM* aItem )
245  {
246  aItem->SetLocked( false );
247  };
248 
249  if( copy )
250  {
251  prepItem( copy );
252 
253  // locate the reference point at (0, 0) in the copied items
254  copy->Move( (wxPoint) -refPoint );
255 
256  Format( copy, 1 );
257 
258  if( copy->Type() == PCB_GROUP_T )
259  {
260  static_cast<PCB_GROUP*>( copy )->RunOnDescendants( prepItem );
261  static_cast<PCB_GROUP*>( copy )->RunOnDescendants( [&]( BOARD_ITEM* titem )
262  {
263  Format( titem, 1 );
264  } );
265  }
266 
267  delete copy;
268  }
269  }
270  m_formatter.Print( 0, "\n)" );
271  }
272 
273  // These are placed at the end to minimize the open time of the clipboard
274  wxLogNull doNotLog; // disable logging of failed clipboard actions
275  auto clipboard = wxTheClipboard;
276  wxClipboardLocker clipboardLock( clipboard );
277 
278  if( !clipboardLock || !clipboard->IsOpened() )
279  return;
280 
281  clipboard->SetData( new wxTextDataObject( wxString( m_formatter.GetString().c_str(),
282  wxConvUTF8 ) ) );
283 
284  clipboard->Flush();
285 
286  #ifndef __WXOSX__
287  // This section exists to return the clipboard data, ensuring it has fully
288  // been processed by the system clipboard. This appears to be needed for
289  // extremely large clipboard copies on asynchronous linux clipboard managers
290  // such as KDE's Klipper. However, a read back of the data on OSX before the
291  // clipboard is closed seems to cause an ASAN error (heap-buffer-overflow)
292  // since it uses the cached version of the clipboard data and not the system
293  // clipboard data.
294  if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
295  {
296  wxTextDataObject data;
297  clipboard->GetData( data );
298  ignore_unused( data.GetText() );
299  }
300  #endif
301 }
302 
303 
305 {
306  BOARD_ITEM* item;
307  wxString result;
308 
309  wxLogNull doNotLog; // disable logging of failed clipboard actions
310 
311  auto clipboard = wxTheClipboard;
312  wxClipboardLocker clipboardLock( clipboard );
313 
314  if( !clipboardLock )
315  return nullptr;
316 
317  if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
318  {
319  wxTextDataObject data;
320  clipboard->GetData( data );
321  result = data.GetText();
322  }
323 
324  try
325  {
326  item = PCB_PLUGIN::Parse( result );
327  }
328  catch (...)
329  {
330  item = nullptr;
331  }
332 
333  return item;
334 }
335 
336 
337 void CLIPBOARD_IO::Save( const wxString& aFileName, BOARD* aBoard,
338  const PROPERTIES* aProperties )
339 {
340  init( aProperties );
341 
342  m_board = aBoard; // after init()
343 
344  // Prepare net mapping that assures that net codes saved in a file are consecutive integers
345  m_mapping->SetBoard( aBoard );
346 
347  STRING_FORMATTER formatter;
348 
349  m_out = &formatter;
350 
351  m_out->Print( 0, "(kicad_pcb (version %d) (generator pcbnew)\n", SEXPR_BOARD_FILE_VERSION );
352 
353  Format( aBoard, 1 );
354 
355  m_out->Print( 0, ")\n" );
356 
357  wxLogNull doNotLog; // disable logging of failed clipboard actions
358 
359  auto clipboard = wxTheClipboard;
360  wxClipboardLocker clipboardLock( clipboard );
361 
362  if( !clipboardLock )
363  return;
364 
365  clipboard->SetData( new wxTextDataObject(
366  wxString( m_formatter.GetString().c_str(), wxConvUTF8 ) ) );
367  clipboard->Flush();
368 
369  // This section exists to return the clipboard data, ensuring it has fully
370  // been processed by the system clipboard. This appears to be needed for
371  // extremely large clipboard copies on asynchronous linux clipboard managers
372  // such as KDE's Klipper
373  if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
374  {
375  wxTextDataObject data;
376  clipboard->GetData( data );
377  ignore_unused( data.GetText() );
378  }
379 }
380 
381 
382 BOARD* CLIPBOARD_IO::Load( const wxString& aFileName, BOARD* aAppendToMe,
383  const PROPERTIES* aProperties, PROJECT* aProject,
384  PROGRESS_REPORTER* aProgressReporter )
385 {
386  std::string result;
387 
388  wxLogNull doNotLog; // disable logging of failed clipboard actions
389 
390  auto clipboard = wxTheClipboard;
391  wxClipboardLocker clipboardLock( clipboard );
392 
393  if( !clipboardLock )
394  return nullptr;
395 
396  if( clipboard->IsSupported( wxDF_TEXT ) || clipboard->IsSupported( wxDF_UNICODETEXT ) )
397  {
398  wxTextDataObject data;
399  clipboard->GetData( data );
400 
401  result = data.GetText().mb_str();
402  }
403 
404  STRING_LINE_READER reader(result, wxT( "clipboard" ) );
405 
406  init( aProperties );
407 
408  m_parser->SetLineReader( &reader );
409  m_parser->SetBoard( aAppendToMe );
410 
411  BOARD_ITEM* item;
412  BOARD* board;
413 
414  try
415  {
416  item = m_parser->Parse();
417  }
418  catch( const FUTURE_FORMAT_ERROR& )
419  {
420  // Don't wrap a FUTURE_FORMAT_ERROR in another
421  throw;
422  }
423  catch( const PARSE_ERROR& parse_error )
424  {
425  if( m_parser->IsTooRecent() )
426  throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() );
427  else
428  throw;
429  }
430 
431  if( item->Type() != PCB_T )
432  {
433  // The parser loaded something that was valid, but wasn't a board.
434  THROW_PARSE_ERROR( _( "Clipboard content is not KiCad compatible" ),
435  m_parser->CurSource(), m_parser->CurLine(),
436  m_parser->CurLineNumber(), m_parser->CurOffset() );
437  }
438  else
439  {
440  board = dynamic_cast<BOARD*>( item );
441  }
442 
443  // Give the filename to the board if it's new
444  if( board && !aAppendToMe )
445  board->SetFileName( aFileName );
446 
447  return board;
448 }
VECTOR2I GetReferencePoint() const
Definition: selection.h:184
BOARD_ITEM * Parse()
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
Definition: pcb_plugin.h:105
#define CTL_FOR_CLIPBOARD
Format output for the clipboard instead of footprint library or BOARD.
Definition: pcb_plugin.h:125
Definition: typeinfo.h:84
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
Container for project specific data.
Definition: project.h:62
const wxString & GetValue() const
Definition: footprint.h:488
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
void CopyText(const EDA_TEXT &aSrc)
Definition: eda_text.cpp:131
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:164
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
void Save(const wxString &aFileName, BOARD *aBoard, const PROPERTIES *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PLUGIN implementation knows about or it can be u...
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
A progress reporter interface for use in multi-threaded environments.
void SetEffects(const EDA_TEXT &aSrc)
Set the text effects from another instance.
Definition: eda_text.cpp:139
void init(const PROPERTIES *aProperties)
A PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
Definition: pcb_plugin.h:141
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
class PAD, a pad in a footprint
Definition: typeinfo.h:89
STRING_FORMATTER m_formatter
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
PCB_PARSER * m_parser
Definition: pcb_plugin.h:296
A name/value tuple with unique names and optional values.
Definition: properties.h:33
void SetBoard(BOARD *aBoard)
BOARD_ITEM * Parse(const wxString &aClipboardSourceInput)
Definition: pcb_plugin.cpp:378
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition: eda_item.cpp:83
void SetBoard(const BOARD *aBoard)
Set a BOARD object that is used to prepare the net code map.
Definition: netinfo.h:200
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
Definition: kiid.h:44
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
Definition: pcb_parser.cpp:247
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
void SetBoard(BOARD *aBoard)
Definition: pcb_parser.h:102
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:124
OUTPUTFORMATTER * m_out
output any Format()s to this, no ownership
Definition: pcb_plugin.h:294
FOOTPRINTS & Footprints()
Definition: board.h:234
void InitDataFromSrcInCopyCtor(const ZONE &aZone)
Copy aZone data to me.
Definition: zone.cpp:111
const wxString & GetReference() const
Definition: footprint.h:466
#define _(s)
void MoveAnchorPosition(const wxPoint &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:1608
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
const std::string & GetString()
Definition: richio.h:438
void SetFileName(const wxString &aFileName)
Definition: board.h:227
BOARD_ITEM * Parse()
Definition: pcb_parser.cpp:627
BOARD * m_board
which BOARD, no ownership here
Definition: pcb_plugin.h:284
void Format(const BOARD_ITEM *aItem, int aNestLevel=0) const
Output aItem to aFormatter in s-expression format.
Definition: pcb_plugin.cpp:400
LINE_READER * SetLineReader(LINE_READER *aReader)
Set aLineReader into the parser, and returns the previous one, if any.
Definition: pcb_parser.h:95
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:98
NETINFO_MAPPING * m_mapping
mapping for net codes, so only not empty net codes are stored with consecutive integers as net codes
Definition: pcb_plugin.h:297
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:196
bool IsTooRecent()
Return whether a version number, if any was parsed, was too recent.
Definition: pcb_parser.h:133
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:118
bool HasReferencePoint() const
Definition: selection.h:179
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
void formatBoardLayers(const BOARD *aBoard, int aNestLevel=0) const
formats the board layer information
Definition: pcb_plugin.cpp:542
class ZONE, managed by a footprint
Definition: typeinfo.h:94
int Size() const
Returns the number of selected parts.
Definition: selection.h:104
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:99
void ignore_unused(const T &)
Definition: ignore.h:24
wxPoint GetPosition() const override
Definition: footprint.h:187
void formatNetInformation(const BOARD *aBoard, int aNestLevel=0) const
formats the Nets and Netclasses
Definition: pcb_plugin.cpp:612
Pcbnew s-expression file format parser definition.
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:174
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Removes an item from the container.
Definition: footprint.cpp:513
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:426
Definition: pad.h:57
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1562
void SaveSelection(const PCB_SELECTION &selected, bool isFootprintEditor)
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:136
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition: richio.h:240
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:414
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:143
EDA_ITEM * Front() const
Definition: selection.h:145
BOARD * Load(const wxString &aFileName, BOARD *aAppendToMe, const PROPERTIES *aProperties=nullptr, PROJECT *aProject=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr) override
Load information from some input file format that this PLUGIN implementation knows about into either ...
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112