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