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