Merge ~kristoffer-odmark/kicad:tom-copypasta into ~kicad-product-committers/kicad:master

Proposed by Kristoffer
Status: Merged
Merged at revision: 4a5d400ec26d8805935da3663293528be0fd9a06
Proposed branch: ~kristoffer-odmark/kicad:tom-copypasta
Merge into: ~kicad-product-committers/kicad:master
Diff against target: 1779 lines (+987/-155)
20 files modified
common/CMakeLists.txt (+1/-0)
include/tool/selection.h (+23/-0)
pcbnew/board_commit.cpp (+1/-0)
pcbnew/kicad_clipboard.cpp (+311/-0)
pcbnew/kicad_clipboard.h (+81/-0)
pcbnew/kicad_plugin.cpp (+111/-5)
pcbnew/kicad_plugin.h (+16/-1)
pcbnew/pcb_parser.h (+8/-8)
pcbnew/tools/edit_tool.cpp (+101/-13)
pcbnew/tools/edit_tool.h (+24/-1)
pcbnew/tools/grid_helper.cpp (+7/-1)
pcbnew/tools/module_editor_tools.cpp (+4/-17)
pcbnew/tools/pcb_actions.h (+10/-0)
pcbnew/tools/pcb_editor_control.cpp (+12/-2)
pcbnew/tools/pcb_tool.h (+2/-0)
pcbnew/tools/pcbnew_control.cpp (+245/-95)
pcbnew/tools/pcbnew_control.h (+12/-4)
pcbnew/tools/picker_tool.cpp (+11/-2)
pcbnew/tools/picker_tool.h (+3/-4)
pcbnew/tools/selection_tool.cpp (+4/-2)
Reviewer Review Type Date Requested Status
Tomasz Wlostowski Pending
Review via email: mp+331369@code.launchpad.net

Description of the change

Added copy-paste functionality to pcbnew. Coworked with Tomasz. I think it is fully acceptable in the current state and ready for mainstream testing.

To post a comment you must log in.
Revision history for this message
Wayne Stambaugh (stambaughw) wrote :

Kristoffer,

Please do not put license text inside Doxygen comments. I also saw
quite a few coding policy violations:

Trailing white space.

Missing spaces around function argument braces.

100 character line length exceeded.

Only one empty space (should be 2) between method definitions in .cpp
source files.

Curly bracket not on next line.

Incorrect indentation level.

Please clean these up before we merge your patch.

Thanks,

Wayne

On 9/26/2017 1:58 PM, Kristoffer wrote:
> Kristoffer has proposed merging ~kristoffer-odmark/kicad:tom-copypasta into kicad:master.
>
> Requested reviews:
> Tomasz Wlostowski (twlostow)
>
> For more details, see:
> https://code.launchpad.net/~kristoffer-odmark/kicad/+git/kicad/+merge/331369
>
> Added copy-paste functionality to pcbnew. Coworked with Tomasz. I think it is fully acceptable in the current state and ready for mainstream testing.
>
>

1faee98... by Kristoffer

some coding violations fixed

Revision history for this message
Kristoffer (kristoffer-odmark) wrote :

Does it look better now?

Revision history for this message
Tomasz Wlostowski (twlostow) wrote :

On 27.09.2017 20:46, Kristoffer wrote:
> Does it look better now?
>
Hi Kristoffer,

I've had a quick look (not at the code style yet):
- there's a bug that makes the selection go in crazy direciton when
moving cursor with arrow keys. I'm working now to fix it.
- the code for merging NETINFO_ITEMs from the board being pasted is OK,
but the net propagation is not what I meant (in some cases, pasted
tracks/vias don't inherit the nets of the pads they're pasted over).

Tom

Revision history for this message
Kristoffer (kristoffer-odmark) wrote :

Any progress?

Revision history for this message
Franck78 (fbourdonnec) wrote :

Why is this thing still in 'pending' state ????????????????

It ages around 4.0.7 release (2017)
We are 2 years latter 5.1.x (2019)
Some Copy/paste in v5 v6 is implemented

Except for discouraging a new developer (too late for Kristoffer) to join, a patch is accepted or rejected in a reasonable delay.

Revision history for this message
Tomasz Wlostowski (twlostow) wrote :

The branch was merged in 2017, with the exception of two last commits (https://git.launchpad.net/~kristoffer-odmark/kicad/commit/?id=7a58afc154868efbe4f4a62961e1c11aa81d01e2 - this one does introduce new functionality without undo, which doesn't fit in our plans).

And since you mentioned discouraging developers - stop bitching, Franck. You are not contributing anything valuable to the project, just complaining. I sometimes feel quite discouraged from contributing just because of your negativity.

Tom

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
2index a325d23..93d4692 100644
3--- a/common/CMakeLists.txt
4+++ b/common/CMakeLists.txt
5@@ -403,6 +403,7 @@ set( PCB_COMMON_SRCS
6 ../pcbnew/eagle_plugin.cpp
7 ../pcbnew/legacy_plugin.cpp
8 ../pcbnew/kicad_plugin.cpp
9+ ../pcbnew/kicad_clipboard.cpp
10 ../pcbnew/gpcb_plugin.cpp
11 ../pcbnew/pcb_netlist.cpp
12 widgets/widget_net_selector.cpp
13diff --git a/include/tool/selection.h b/include/tool/selection.h
14index 7e4f488..fb0c735 100644
15--- a/include/tool/selection.h
16+++ b/include/tool/selection.h
17@@ -170,7 +170,30 @@ public:
18
19 virtual const VIEW_GROUP::ITEMS updateDrawList() const override;
20
21+ bool HasReferencePoint() const
22+ {
23+ return m_referencePoint != boost::none;
24+ }
25+
26+ VECTOR2I GetReferencePoint() const
27+ {
28+ return *m_referencePoint;
29+ }
30+
31+ void SetReferencePoint( const VECTOR2I& aP )
32+ {
33+ m_referencePoint = aP;
34+ }
35+
36+ void ClearReferencePoint()
37+ {
38+ m_referencePoint = boost::none;
39+ }
40+
41 private:
42+
43+ boost::optional<VECTOR2I> m_referencePoint;
44+
45 /// Set of selected items
46 std::set<EDA_ITEM*> m_items;
47
48diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp
49index 8ddfbcb..a967096 100644
50--- a/pcbnew/board_commit.cpp
51+++ b/pcbnew/board_commit.cpp
52@@ -132,6 +132,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry )
53 // modules inside modules are not supported yet
54 assert( boardItem->Type() != PCB_MODULE_T );
55
56+ boardItem->SetParent( board->m_Modules.GetFirst() );
57 if( !( changeFlags & CHT_DONE ) )
58 board->m_Modules->Add( boardItem );
59 }
60diff --git a/pcbnew/kicad_clipboard.cpp b/pcbnew/kicad_clipboard.cpp
61new file mode 100644
62index 0000000..38b59cf
63--- /dev/null
64+++ b/pcbnew/kicad_clipboard.cpp
65@@ -0,0 +1,311 @@
66+/*
67+ * This program source code file is part of KiCad, a free EDA CAD application.
68+ *
69+ * Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors.
70+ * @author Kristoffer Ödmark
71+ *
72+ * This program is free software; you can redistribute it and/or
73+ * modify it under the terms of the GNU General Public License
74+ * as published by the Free Software Foundation; either version 2
75+ * of the License, or (at your option) any later version.
76+ *
77+ * This program is distributed in the hope that it will be useful,
78+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
79+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
80+ * GNU General Public License for more details.
81+ *
82+ * You should have received a copy of the GNU General Public License
83+ * along with this program; if not, you may find one here:
84+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
85+ * or you may search the http://www.gnu.org website for the version 2 license,
86+ * or you may write to the Free Software Foundation, Inc.,
87+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
88+ */
89+
90+#include <wx/clipbrd.h>
91+#include <common.h>
92+#include <pcb_parser.h>
93+#include <class_netinfo.h>
94+#include <class_board.h>
95+#include <build_version.h>
96+
97+#include <kicad_plugin.h>
98+#include <kicad_clipboard.h>
99+
100+CLIPBOARD_IO::CLIPBOARD_IO():
101+ PCB_IO( CTL_STD_LAYER_NAMES ),
102+ m_formatter(),
103+ m_parser( new CLIPBOARD_PARSER() )
104+{
105+ m_out = &m_formatter;
106+}
107+
108+
109+CLIPBOARD_IO::~CLIPBOARD_IO(){}
110+
111+
112+STRING_FORMATTER* CLIPBOARD_IO::GetFormatter()
113+{
114+ return &m_formatter;
115+}
116+
117+
118+void CLIPBOARD_IO::SetBoard( BOARD* aBoard )
119+{
120+ m_board = aBoard;
121+}
122+
123+
124+void CLIPBOARD_IO::SaveSelection( const SELECTION& aSelected )
125+{
126+ LOCALE_IO toggle; // toggles on, then off, the C locale.
127+ VECTOR2I refPoint( 0, 0 );
128+
129+ // dont even start if the selection is empty
130+ if( aSelected.Empty() )
131+ return;
132+
133+ if( aSelected.HasReferencePoint() )
134+ refPoint = aSelected.GetReferencePoint();
135+
136+ // Prepare net mapping that assures that net codes saved in a file are consecutive integers
137+ m_mapping->SetBoard( m_board );
138+
139+ // Differentiate how it is formatted depending on what selection contains
140+ bool onlyModuleParts = true;
141+ for( const auto i : aSelected )
142+ {
143+ // check if it not one of the module primitives
144+ if( ( i->Type() != PCB_MODULE_EDGE_T ) &&
145+ ( i->Type() != PCB_MODULE_TEXT_T ) &&
146+ ( i->Type() != PCB_PAD_T ) )
147+ {
148+ onlyModuleParts = false;
149+ continue;
150+ }
151+ }
152+
153+ // if there is only parts of a module selected, format it as a new module else
154+ // format it as an entire board
155+ MODULE partialModule( m_board );
156+
157+ // only a module selected.
158+ if( aSelected.Size() == 1 && aSelected.Front()->Type() == PCB_MODULE_T )
159+ {
160+ // make the module safe to transfer to other pcbs
161+ const MODULE* mod = static_cast<MODULE*>( aSelected.Front() );
162+ // Do not modify existing board
163+ MODULE newModule(*mod);
164+
165+ for( D_PAD* pad = newModule.PadsList().begin(); pad; pad = pad->Next() )
166+ {
167+ pad->SetNetCode( 0, 0 );
168+ }
169+
170+ // locate the reference point at (0, 0) in the copied items
171+ newModule.Move( wxPoint( -refPoint.x, -refPoint.y ) );
172+
173+ Format( static_cast<BOARD_ITEM*>( &newModule ) );
174+ }
175+ // partial module selected.
176+ else if( onlyModuleParts )
177+ {
178+ for( const auto item : aSelected )
179+ {
180+ auto clone = static_cast<BOARD_ITEM*>( item->Clone() );
181+
182+ // Do not add reference/value - convert them to the common type
183+ if( TEXTE_MODULE* text = dyn_cast<TEXTE_MODULE*>( clone ) )
184+ text->SetType( TEXTE_MODULE::TEXT_is_DIVERS );
185+
186+ // If it is only a module, clear the nets from the pads
187+ if( clone->Type() == PCB_PAD_T )
188+ {
189+ D_PAD* pad = static_cast<D_PAD*>( clone );
190+ pad->SetNetCode( 0, 0 );
191+ }
192+
193+ // locate the reference point at (0, 0) in the copied items
194+ clone->Move( wxPoint(-refPoint.x, -refPoint.y ) );
195+
196+ partialModule.Add( clone );
197+ }
198+
199+ // Set the new relative internal local coordinates of copied items
200+ MODULE* editedModule = m_board->m_Modules;
201+ wxPoint moveVector = partialModule.GetPosition() + editedModule->GetPosition();
202+
203+ partialModule.MoveAnchorPosition( moveVector );
204+
205+ Format( &partialModule, 0 );
206+
207+ }
208+ // lots of stuff selected
209+ else
210+ {
211+ // we will fake being a .kicad_pcb to get the full parser kicking
212+ // This means we also need layers and nets
213+ m_formatter.Print( 0, "(kicad_pcb (version %d) (host pcbnew %s)\n",
214+ SEXPR_BOARD_FILE_VERSION, m_formatter.Quotew( GetBuildVersion() ).c_str() );
215+
216+
217+ m_formatter.Print( 0, "\n" );
218+
219+ formatBoardLayers( m_board );
220+ formatNetInformation( m_board );
221+
222+ m_formatter.Print( 0, "\n" );
223+
224+
225+ for( const auto i : aSelected )
226+ {
227+ // Dont format stuff that cannot exist standalone!
228+ if( ( i->Type() != PCB_MODULE_EDGE_T ) &&
229+ ( i->Type() != PCB_MODULE_TEXT_T ) &&
230+ ( i->Type() != PCB_PAD_T ) )
231+ {
232+ auto item = static_cast<BOARD_ITEM*>( i );
233+ std::unique_ptr<BOARD_ITEM> clone( static_cast<BOARD_ITEM*> ( item->Clone() ) );
234+
235+ // locate the reference point at (0, 0) in the copied items
236+ clone->Move( wxPoint(-refPoint.x, -refPoint.y ) );
237+
238+ Format( clone.get(), 1 );
239+ }
240+
241+ }
242+ m_formatter.Print( 0, "\n)" );
243+ }
244+ if( wxTheClipboard->Open() )
245+ {
246+ wxTheClipboard->SetData( new wxTextDataObject(
247+ wxString( m_formatter.GetString().c_str(), wxConvUTF8 ) ) );
248+ wxTheClipboard->Close();
249+ }
250+}
251+
252+
253+BOARD_ITEM* CLIPBOARD_IO::Parse()
254+{
255+ std::string result;
256+
257+ if( wxTheClipboard->Open() )
258+ {
259+ if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
260+ {
261+ wxTextDataObject data;
262+ wxTheClipboard->GetData( data );
263+
264+ result = data.GetText().mb_str();
265+ }
266+
267+ wxTheClipboard->Close();
268+ }
269+
270+ BOARD_ITEM *item;
271+ try
272+ {
273+ item = PCB_IO::Parse( result );
274+ } catch (...) {
275+ item = nullptr;
276+ }
277+
278+ return item;
279+}
280+
281+
282+void CLIPBOARD_IO::Save( const wxString& aFileName, BOARD* aBoard,
283+ const PROPERTIES* aProperties )
284+{
285+ LOCALE_IO toggle; // toggles on, then off, the C locale.
286+
287+ init( aProperties );
288+
289+ m_board = aBoard; // after init()
290+
291+ // Prepare net mapping that assures that net codes saved in a file are consecutive integers
292+ m_mapping->SetBoard( aBoard );
293+
294+ STRING_FORMATTER formatter;
295+
296+ m_out = &formatter;
297+
298+ m_out->Print( 0, "(kicad_pcb (version %d) (host pcbnew %s)\n", SEXPR_BOARD_FILE_VERSION,
299+ formatter.Quotew( GetBuildVersion() ).c_str() );
300+
301+ Format( aBoard, 1 );
302+
303+ m_out->Print( 0, ")\n" );
304+
305+ if( wxTheClipboard->Open() )
306+ {
307+ wxTheClipboard->SetData( new wxTextDataObject(
308+ wxString( m_formatter.GetString().c_str(), wxConvUTF8 ) ) );
309+ wxTheClipboard->Close();
310+ }
311+
312+}
313+
314+
315+BOARD* CLIPBOARD_IO::Load( const wxString& aFileName,
316+ BOARD* aAppendToMe, const PROPERTIES* aProperties )
317+{
318+ std::string result;
319+
320+ if( wxTheClipboard->Open() )
321+ {
322+ if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
323+ {
324+ wxTextDataObject data;
325+ wxTheClipboard->GetData( data );
326+
327+ result = data.GetText().mb_str();
328+ }
329+
330+ wxTheClipboard->Close();
331+ }
332+
333+ STRING_LINE_READER reader(result, wxT( "clipboard" ) );
334+
335+ init( aProperties );
336+
337+ m_parser->SetLineReader( &reader );
338+ m_parser->SetBoard( aAppendToMe );
339+
340+ BOARD_ITEM* item;
341+ BOARD* board;
342+
343+ try
344+ {
345+ item = m_parser->Parse();
346+ }
347+ catch( const FUTURE_FORMAT_ERROR& )
348+ {
349+ // Don't wrap a FUTURE_FORMAT_ERROR in another
350+ throw;
351+ }
352+ catch( const PARSE_ERROR& parse_error )
353+ {
354+ if( m_parser->IsTooRecent() )
355+ throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() );
356+ else
357+ throw;
358+ }
359+
360+ if( item->Type() != PCB_T )
361+ {
362+ // The parser loaded something that was valid, but wasn't a board.
363+ THROW_PARSE_ERROR( _( "Clipboard content is not Kicad compatible" ),
364+ m_parser->CurSource(), m_parser->CurLine(),
365+ m_parser->CurLineNumber(), m_parser->CurOffset() );
366+ }
367+ else
368+ {
369+ board = dynamic_cast<BOARD*>( item );
370+ }
371+ // Give the filename to the board if it's new
372+ if( !aAppendToMe )
373+ board->SetFileName( aFileName );
374+
375+ return board;
376+}
377diff --git a/pcbnew/kicad_clipboard.h b/pcbnew/kicad_clipboard.h
378new file mode 100644
379index 0000000..ccc2877
380--- /dev/null
381+++ b/pcbnew/kicad_clipboard.h
382@@ -0,0 +1,81 @@
383+/*
384+ * This program source code file is part of KiCad, a free EDA CAD application.
385+ *
386+ * Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors.
387+ * @author Kristoffer Ödmark
388+ *
389+ * This program is free software; you can redistribute it and/or
390+ * modify it under the terms of the GNU General Public License
391+ * as published by the Free Software Foundation; either version 2
392+ * of the License, or (at your option) any later version.
393+ *
394+ * This program is distributed in the hope that it will be useful,
395+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
396+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
397+ * GNU General Public License for more details.
398+ *
399+ * You should have received a copy of the GNU General Public License
400+ * along with this program; if not, you may find one here:
401+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
402+ * or you may search the http://www.gnu.org website for the version 2 license,
403+ * or you may write to the Free Software Foundation, Inc.,
404+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
405+ */
406+
407+#ifndef KICAD_CLIPBOARD_H
408+#define KICAD_CLIPBOARD_H
409+
410+#include <kicad_plugin.h>
411+#include <class_board_item.h>
412+#include <class_module.h>
413+#include <pcb_parser.h>
414+#include <memory.h>
415+
416+#include <tool/selection.h>
417+
418+class CLIPBOARD_PARSER : public PCB_PARSER
419+{
420+public:
421+ CLIPBOARD_PARSER( LINE_READER* aReader = NULL ): PCB_PARSER( aReader ) {};
422+
423+ MODULE* parseMODULE( wxArrayString* aInitialComments )
424+ {
425+ MODULE* mod = PCB_PARSER::parseMODULE( aInitialComments );
426+
427+ //TODO: figure out better way of handling paths
428+ mod->SetPath( wxT( "" ) );
429+ return mod;
430+ }
431+};
432+
433+
434+class CLIPBOARD_IO : public PCB_IO
435+{
436+
437+public:
438+ /* Saves the entire board to the clipboard formatted using the PCB_IO formatting */
439+ void Save( const wxString& aFileName, BOARD* aBoard,
440+ const PROPERTIES* aProperties = NULL ) override;
441+ /* Writes all the settings of the BOARD* set by setBoard() and then adds all
442+ * the BOARD_ITEM* found in selection formatted by PCB_IO to clipboard as a text
443+ */
444+ void SaveSelection( const SELECTION& selected );
445+
446+ BOARD_ITEM* Parse();
447+
448+ BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ) override;
449+ CLIPBOARD_IO();
450+ ~CLIPBOARD_IO();
451+
452+ void SetBoard( BOARD* aBoard );
453+ STRING_FORMATTER* GetFormatter();
454+
455+private:
456+ void writeHeader( BOARD* aBoard );
457+
458+ STRING_FORMATTER m_formatter;
459+ CLIPBOARD_PARSER* m_parser;
460+};
461+
462+
463+#endif /* KICAD_CLIPBOARD_H */
464diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp
465index bdc632d..fbf7ee4 100644
466--- a/pcbnew/kicad_plugin.cpp
467+++ b/pcbnew/kicad_plugin.cpp
468@@ -527,9 +527,9 @@ void PCB_IO::formatLayer( const BOARD_ITEM* aItem ) const
469 m_out->Print( 0, " (layer %s)", m_out->Quotew( aItem->GetLayerName() ).c_str() );
470 }
471
472-
473-void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
474+void PCB_IO::formatSetup( BOARD* aBoard, int aNestLevel ) const
475 {
476+
477 const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
478
479 m_out->Print( 0, "\n" );
480@@ -572,7 +572,8 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
481
482 // Save used non-copper layers in the order they are defined.
483 // desired sequence for non Cu BOARD layers.
484- static const PCB_LAYER_ID non_cu[] = {
485+ static const PCB_LAYER_ID non_cu[] =
486+ {
487 B_Adhes, // 32
488 F_Adhes,
489 B_Paste,
490@@ -719,8 +720,96 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
491 aBoard->GetPlotOptions().Format( m_out, aNestLevel+1 );
492
493 m_out->Print( aNestLevel, ")\n\n" );
494+}
495
496- // Save net codes and names
497+
498+void PCB_IO::formatGeneral( BOARD* aBoard, int aNestLevel ) const
499+{
500+ const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
501+
502+ m_out->Print( 0, "\n" );
503+ m_out->Print( aNestLevel, "(general\n" );
504+ // Write Bounding box info
505+ m_out->Print( aNestLevel+1, "(thickness %s)\n",
506+ FMTIU( dsnSettings.GetBoardThickness() ).c_str() );
507+
508+ m_out->Print( aNestLevel+1, "(drawings %d)\n", aBoard->Drawings().Size() );
509+ m_out->Print( aNestLevel+1, "(tracks %d)\n", aBoard->GetNumSegmTrack() );
510+ m_out->Print( aNestLevel+1, "(zones %d)\n", aBoard->GetNumSegmZone() );
511+ m_out->Print( aNestLevel+1, "(modules %d)\n", aBoard->m_Modules.GetCount() );
512+ m_out->Print( aNestLevel+1, "(nets %d)\n", m_mapping->GetSize() );
513+ m_out->Print( aNestLevel, ")\n\n" );
514+
515+ aBoard->GetPageSettings().Format( m_out, aNestLevel, m_ctl );
516+ aBoard->GetTitleBlock().Format( m_out, aNestLevel, m_ctl );
517+}
518+
519+
520+void PCB_IO::formatBoardLayers( BOARD* aBoard, int aNestLevel ) const
521+{
522+ m_out->Print( aNestLevel, "(layers\n" );
523+
524+ // Save only the used copper layers from front to back.
525+ LSET visible_layers = aBoard->GetVisibleLayers();
526+
527+ for( LSEQ cu = aBoard->GetEnabledLayers().CuStack(); cu; ++cu )
528+ {
529+ PCB_LAYER_ID layer = *cu;
530+
531+ m_out->Print( aNestLevel+1, "(%d %s %s", layer,
532+ m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str(),
533+ LAYER::ShowType( aBoard->GetLayerType( layer ) ) );
534+
535+ if( !visible_layers[layer] )
536+ m_out->Print( 0, " hide" );
537+
538+ m_out->Print( 0, ")\n" );
539+ }
540+
541+ // Save used non-copper layers in the order they are defined.
542+ // desired sequence for non Cu BOARD layers.
543+ static const PCB_LAYER_ID non_cu[] =
544+ {
545+ B_Adhes, // 32
546+ F_Adhes,
547+ B_Paste,
548+ F_Paste,
549+ B_SilkS,
550+ F_SilkS,
551+ B_Mask,
552+ F_Mask,
553+ Dwgs_User,
554+ Cmts_User,
555+ Eco1_User,
556+ Eco2_User,
557+ Edge_Cuts,
558+ Margin,
559+ B_CrtYd,
560+ F_CrtYd,
561+ B_Fab,
562+ F_Fab
563+ };
564+
565+ for( LSEQ seq = aBoard->GetEnabledLayers().Seq( non_cu, DIM( non_cu ) ); seq; ++seq )
566+ {
567+ PCB_LAYER_ID layer = *seq;
568+
569+ m_out->Print( aNestLevel+1, "(%d %s user", layer,
570+ m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str() );
571+
572+ if( !visible_layers[layer] )
573+ m_out->Print( 0, " hide" );
574+
575+ m_out->Print( 0, ")\n" );
576+ }
577+
578+ m_out->Print( aNestLevel, ")\n\n" );
579+}
580+
581+
582+void PCB_IO::formatNetInformation( BOARD* aBoard, int aNestLevel ) const
583+{
584+ const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
585 for( NETINFO_MAPPING::iterator net = m_mapping->begin(), netEnd = m_mapping->end();
586 net != netEnd; ++net )
587 {
588@@ -745,6 +834,23 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
589 filterNetClass( *aBoard, netclass ); // Remove empty nets (from a copy of a netclass)
590 netclass.Format( m_out, aNestLevel, m_ctl );
591 }
592+}
593+
594+
595+void PCB_IO::formatHeader( BOARD* aBoard, int aNestLevel ) const
596+{
597+ formatGeneral( aBoard );
598+ // Layers.
599+ formatBoardLayers( aBoard );
600+ // Setup
601+ formatSetup( aBoard, aNestLevel );
602+ // Save net codes and names
603+ formatNetInformation( aBoard, aNestLevel );
604+}
605+
606+void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const
607+{
608+ formatHeader( aBoard );
609
610 // Save the modules.
611 for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
612@@ -1541,7 +1647,7 @@ void PCB_IO::format( TRACK* aTrack, int aNestLevel ) const
613 {
614 PCB_LAYER_ID layer1, layer2;
615
616- const VIA* via = static_cast<const VIA*>(aTrack);
617+ const VIA* via = static_cast<const VIA*>( aTrack );
618 BOARD* board = (BOARD*) via->GetParent();
619
620 wxCHECK_RET( board != 0, wxT( "Via " ) + via->GetSelectMenuText() +
621diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h
622index 6ec6c7c..eff7590 100644
623--- a/pcbnew/kicad_plugin.h
624+++ b/pcbnew/kicad_plugin.h
625@@ -109,7 +109,7 @@ public:
626 return wxT( "kicad_pcb" );
627 }
628
629- void Save( const wxString& aFileName, BOARD* aBoard,
630+ virtual void Save( const wxString& aFileName, BOARD* aBoard,
631 const PROPERTIES* aProperties = NULL ) override;
632
633 BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe,
634@@ -188,6 +188,21 @@ protected:
635
636 void init( const PROPERTIES* aProperties );
637
638+ /// formats the board setup information
639+ void formatSetup( BOARD* aBoard, int aNestLevel = 0 ) const;
640+
641+ /// formats the General section of the file
642+ void formatGeneral( BOARD* aBoard, int aNestLevel = 0 ) const;
643+
644+ /// formats the board layer information
645+ void formatBoardLayers( BOARD* aBoard, int aNestLevel = 0 ) const;
646+
647+ /// formats the Nets and Netclasses
648+ void formatNetInformation( BOARD* aBoard, int aNestLevel = 0 ) const;
649+
650+ /// writes everything that comes before the board_items, like settings and layers etc
651+ void formatHeader( BOARD* aBoard, int aNestLevel = 0 ) const;
652+
653 private:
654 void format( BOARD* aBoard, int aNestLevel = 0 ) const;
655
656diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h
657index c03303c..b2ed26a 100644
658--- a/pcbnew/pcb_parser.h
659+++ b/pcbnew/pcb_parser.h
660@@ -119,14 +119,6 @@ class PCB_PARSER : public PCB_LEXER
661 DIMENSION* parseDIMENSION();
662
663 /**
664- * Function parseMODULE
665- * @param aInitialComments may be a pointer to a heap allocated initial comment block
666- * or NULL. If not NULL, then caller has given ownership of a wxArrayString to
667- * this function and care must be taken to delete it even on exception.
668- */
669- MODULE* parseMODULE( wxArrayString* aInitialComments = 0 );
670-
671- /**
672 * Function parseMODULE_unchecked
673 * Parse a module, but do not replace PARSE_ERROR with FUTURE_FORMAT_ERROR automatically.
674 */
675@@ -309,6 +301,13 @@ public:
676 }
677
678 BOARD_ITEM* Parse();
679+ /**
680+ * Function parseMODULE
681+ * @param aInitialComments may be a pointer to a heap allocated initial comment block
682+ * or NULL. If not NULL, then caller has given ownership of a wxArrayString to
683+ * this function and care must be taken to delete it even on exception.
684+ */
685+ MODULE* parseMODULE( wxArrayString* aInitialComments = 0 );
686
687 /**
688 * Return whether a version number, if any was parsed, was too recent
689@@ -327,4 +326,5 @@ public:
690 };
691
692
693+
694 #endif // _PCBNEW_PARSER_H_
695diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp
696index ef6cc2b..ea734a5 100644
697--- a/pcbnew/tools/edit_tool.cpp
698+++ b/pcbnew/tools/edit_tool.cpp
699@@ -54,7 +54,10 @@ using namespace std::placeholders;
700 #include "pcb_actions.h"
701 #include "selection_tool.h"
702 #include "edit_tool.h"
703+#include "picker_tool.h"
704 #include "grid_helper.h"
705+#include "kicad_clipboard.h"
706+#include "pcbnew_control.h"
707
708 #include <router/router_tool.h>
709
710@@ -161,6 +164,16 @@ TOOL_ACTION PCB_ACTIONS::measureTool( "pcbnew.InteractiveEdit.measureTool",
711 _( "Measuring tool" ), _( "Interactively measure distance between points" ),
712 nullptr, AF_ACTIVATE );
713
714+TOOL_ACTION PCB_ACTIONS::copyToClipboard( "pcbnew.InteractiveEdit.CopyToClipboard",
715+ AS_GLOBAL, MD_CTRL + int( 'C' ),
716+ _( "Copy" ), _( "Copy selected content to clipboard" ),
717+ copy_xpm );
718+
719+TOOL_ACTION PCB_ACTIONS::cutToClipboard( "pcbnew.InteractiveEdit.CutToClipboard",
720+ AS_GLOBAL, MD_CTRL + int( 'X' ),
721+ _( "Cut" ), _( "Cut selected content to clipboard" ),
722+ cut_xpm );
723+
724 static wxPoint getAnchorPoint( const SELECTION &selection, const MOVE_PARAMETERS &params )
725 {
726 wxPoint anchorPoint;
727@@ -235,8 +248,6 @@ static wxPoint getAnchorPoint( const SELECTION &selection, const MOVE_PARAMETERS
728 }
729
730
731-
732-
733 EDIT_TOOL::EDIT_TOOL() :
734 PCB_TOOL( "pcbnew.InteractiveEdit" ), m_selectionTool( NULL ),
735 m_dragging( false )
736@@ -289,6 +300,12 @@ bool EDIT_TOOL::Init()
737 menu.AddItem( PCB_ACTIONS::duplicate, SELECTION_CONDITIONS::NotEmpty );
738 menu.AddItem( PCB_ACTIONS::createArray, SELECTION_CONDITIONS::NotEmpty );
739
740+ menu.AddSeparator();
741+ menu.AddItem( PCB_ACTIONS::copyToClipboard, SELECTION_CONDITIONS::NotEmpty );
742+ menu.AddItem( PCB_ACTIONS::cutToClipboard, SELECTION_CONDITIONS::NotEmpty );
743+ menu.AddItem( PCB_ACTIONS::pasteFromClipboard );
744+ menu.AddSeparator();
745+
746 // Mirror only available in modedit
747 menu.AddItem( PCB_ACTIONS::mirror, editingModuleCondition && SELECTION_CONDITIONS::NotEmpty );
748
749@@ -376,10 +393,10 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
750 controls->SetAutoPan( true );
751
752 // cumulative translation
753- wxPoint totalMovement( 0, 0 );
754-
755+ VECTOR2I totalMovement;
756 GRID_HELPER grid( editFrame );
757 OPT_TOOL_EVENT evt = aEvent;
758+ VECTOR2I prevPos;
759
760 // Main loop: keep receiving events
761 do
762@@ -398,14 +415,20 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
763 if( m_dragging && evt->Category() == TC_MOUSE )
764 {
765 m_cursor = grid.BestSnapAnchor( evt->Position(), curr_item );
766+
767 controls->ForceCursorPosition( true, m_cursor );
768
769- wxPoint movement = wxPoint( m_cursor.x, m_cursor.y ) - curr_item->GetPosition();
770+ VECTOR2I movement( m_cursor - prevPos );// - curr_item->GetPosition();
771+
772 totalMovement += movement;
773+ prevPos = m_cursor;
774+ auto delta = movement + m_offset;
775
776 // Drag items to the current cursor position
777 for( auto item : selection )
778- static_cast<BOARD_ITEM*>( item )->Move( movement + m_offset );
779+ static_cast<BOARD_ITEM*>( item )->Move( wxPoint( delta.x, delta.y ) );
780+
781+ m_offset = VECTOR2I(0, 0);
782 }
783 else if( !m_dragging ) // Prepare to start dragging
784 {
785@@ -429,8 +452,25 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
786 m_commit->Modify( item );
787
788 m_cursor = controls->GetCursorPosition();
789+ m_offset = VECTOR2I(0, 0);
790
791- if( selection.Size() == 1 )
792+ auto refPoint = VECTOR2I( curr_item->GetPosition() );
793+
794+ if ( selection.HasReferencePoint() )
795+ {
796+ // start moving with the reference point attached to the cursor
797+ refPoint = selection.GetReferencePoint();
798+ grid.SetAuxAxes( false );
799+
800+ auto delta = m_cursor - selection.GetReferencePoint();
801+
802+ // Drag items to the current cursor position
803+ for( auto item : selection )
804+ static_cast<BOARD_ITEM*>( item )->Move( wxPoint( delta.x, delta.y ) );
805+
806+ selection.ClearReferencePoint();
807+ }
808+ else if( selection.Size() == 1 )
809 {
810 // Set the current cursor position to the first dragged item origin, so the
811 // movement vector could be computed later
812@@ -444,10 +484,7 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
813
814 controls->SetCursorPosition( m_cursor, false );
815
816- VECTOR2I o = VECTOR2I( curr_item->GetPosition() );
817- m_offset.x = o.x - m_cursor.x;
818- m_offset.y = o.y - m_cursor.y;
819-
820+ prevPos = m_cursor;
821 controls->SetAutoPan( true );
822 m_dragging = true;
823 }
824@@ -495,7 +532,8 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
825 for( auto item : selection )
826 {
827 BOARD_ITEM* i = static_cast<BOARD_ITEM*>( item );
828- i->SetPosition( i->GetPosition() - totalMovement );
829+ auto delta = VECTOR2I( i->GetPosition() ) - totalMovement;
830+ i->SetPosition( wxPoint( delta.x, delta.y ) );
831
832 // And what about flipping and rotation?
833 // for now, they won't be undone, but maybe that is how
834@@ -1211,6 +1249,10 @@ void EDIT_TOOL::setTransitions()
835 Go( &EDIT_TOOL::editFootprintInFpEditor, PCB_ACTIONS::editFootprintInFpEditor.MakeEvent() );
836 Go( &EDIT_TOOL::ExchangeFootprints, PCB_ACTIONS::exchangeFootprints.MakeEvent() );
837 Go( &EDIT_TOOL::MeasureTool, PCB_ACTIONS::measureTool.MakeEvent() );
838+
839+ Go( &EDIT_TOOL::copyToClipboard, PCB_ACTIONS::copyToClipboard.MakeEvent() );
840+ Go( &EDIT_TOOL::cutToClipboard, PCB_ACTIONS::cutToClipboard.MakeEvent() );
841+
842 }
843
844
845@@ -1218,7 +1260,9 @@ wxPoint EDIT_TOOL::getModificationPoint( const SELECTION& aSelection )
846 {
847 if( aSelection.Size() == 1 )
848 {
849- return static_cast<BOARD_ITEM*>( aSelection.Front() )->GetPosition() - m_offset;
850+ auto item = static_cast<BOARD_ITEM*>( aSelection.Front() );
851+ auto pos = item->GetPosition();
852+ return wxPoint( pos.x - m_offset.x, pos.y - m_offset.y );
853 }
854 else
855 {
856@@ -1268,6 +1312,50 @@ int EDIT_TOOL::editFootprintInFpEditor( const TOOL_EVENT& aEvent )
857 return 0;
858 }
859
860+bool EDIT_TOOL::pickCopyReferencePoint( VECTOR2I& aP )
861+{
862+ PICKER_TOOL* picker = m_toolMgr->GetTool<PICKER_TOOL>();
863+ assert( picker );
864+
865+ picker->Activate();
866+
867+ while ( picker->IsPicking() )
868+ Wait();
869+
870+ if( !picker->GetPoint() )
871+ return false;
872+
873+ aP = *picker->GetPoint();
874+ return true;
875+}
876+
877+int EDIT_TOOL::copyToClipboard( const TOOL_EVENT& aEvent )
878+{
879+ CLIPBOARD_IO io;
880+ BOARD* board = getModel<BOARD>();
881+ VECTOR2I refPoint;
882+
883+ Activate();
884+
885+ SELECTION selection = m_selectionTool->RequestSelection();
886+
887+ if( !pickCopyReferencePoint( refPoint ) )
888+ return 0;
889+
890+ selection.SetReferencePoint( refPoint );
891+ io.SetBoard( board );
892+ io.SaveSelection( selection );
893+
894+ return 0;
895+}
896+
897+int EDIT_TOOL::cutToClipboard( const TOOL_EVENT& aEvent )
898+{
899+ copyToClipboard( aEvent );
900+ Remove( aEvent );
901+ return 0;
902+}
903+
904
905 template<class T>
906 T* EDIT_TOOL::uniqueSelected()
907diff --git a/pcbnew/tools/edit_tool.h b/pcbnew/tools/edit_tool.h
908index 0868c68..0e19c44 100644
909--- a/pcbnew/tools/edit_tool.h
910+++ b/pcbnew/tools/edit_tool.h
911@@ -139,6 +139,28 @@ public:
912 ///> Sets up handlers for various events.
913 void setTransitions() override;
914
915+ /**
916+ * Function copyToClipboard()
917+ * Sends the current selection to the clipboard by formatting it as a fake pcb
918+ * see AppendBoardFromClipboard for importing
919+ * @return True if it was sent succesfully
920+ */
921+ int copyToClipboard( const TOOL_EVENT& aEvent );
922+
923+ /**
924+ * Function cutToClipboard()
925+ * Sends the current selection to the clipboard by formatting it as a fake pcb
926+ * see AppendBoardFromClipboard for importing
927+ * @return True if it was sent succesfully
928+ */
929+ int cutToClipboard( const TOOL_EVENT& aEvent );
930+
931+
932+ BOARD_COMMIT* GetCurrentCommit() const
933+ {
934+ return m_commit.get();
935+ }
936+
937 private:
938 ///> Selection tool used for obtaining selected items
939 SELECTION_TOOL* m_selectionTool;
940@@ -147,7 +169,7 @@ private:
941 bool m_dragging;
942
943 ///> Offset from the dragged item's center (anchor)
944- wxPoint m_offset;
945+ VECTOR2I m_offset;
946
947 ///> Last cursor position (needed for getModificationPoint() to avoid changes
948 ///> of edit reference point).
949@@ -163,6 +185,7 @@ private:
950 bool isInteractiveDragEnabled() const;
951
952 bool changeTrackWidthOnClick( const SELECTION& selection );
953+ bool pickCopyReferencePoint( VECTOR2I& aP );
954
955
956 /**
957diff --git a/pcbnew/tools/grid_helper.cpp b/pcbnew/tools/grid_helper.cpp
958index 7dce3b0..f01f2f8 100644
959--- a/pcbnew/tools/grid_helper.cpp
960+++ b/pcbnew/tools/grid_helper.cpp
961@@ -251,7 +251,13 @@ VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDrag
962 computeAnchors( item, aOrigin );
963 }
964
965- LSET layers( aDraggedItem->GetLayer() );
966+ LSET layers;
967+
968+ if( aDraggedItem )
969+ layers = aDraggedItem->GetLayer();
970+ else
971+ layers = LSET::AllLayersMask();
972+
973 ANCHOR* nearest = nearestAnchor( aOrigin, CORNER | SNAPPABLE, layers );
974
975 VECTOR2I nearestGrid = Align( aOrigin );
976diff --git a/pcbnew/tools/module_editor_tools.cpp b/pcbnew/tools/module_editor_tools.cpp
977index 7c02ad5..7357b76 100644
978--- a/pcbnew/tools/module_editor_tools.cpp
979+++ b/pcbnew/tools/module_editor_tools.cpp
980@@ -23,6 +23,7 @@
981 */
982
983 #include "module_editor_tools.h"
984+#include "kicad_clipboard.h"
985 #include "selection_tool.h"
986 #include "pcb_actions.h"
987 #include <tool/tool_manager.h>
988@@ -63,11 +64,11 @@ TOOL_ACTION PCB_ACTIONS::enumeratePads( "pcbnew.ModuleEditor.enumeratePads",
989 _( "Enumerate Pads" ), _( "Enumerate pads" ), pad_enumerate_xpm, AF_ACTIVATE );
990
991 TOOL_ACTION PCB_ACTIONS::copyItems( "pcbnew.ModuleEditor.copyItems",
992- AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_COPY_ITEM ),
993+ AS_ACTIVE, 0,
994 _( "Copy" ), _( "Copy items" ), NULL, AF_ACTIVATE );
995
996 TOOL_ACTION PCB_ACTIONS::pasteItems( "pcbnew.ModuleEditor.pasteItems",
997- AS_GLOBAL, MD_CTRL + int( 'V' ),
998+ AS_GLOBAL, 0,
999 _( "Paste" ), _( "Paste items" ), NULL, AF_ACTIVATE );
1000
1001 TOOL_ACTION PCB_ACTIONS::moduleEdgeOutlines( "pcbnew.ModuleEditor.graphicOutlines",
1002@@ -333,21 +334,8 @@ int MODULE_EDITOR_TOOLS::CopyItems( const TOOL_EVENT& aEvent )
1003
1004 int MODULE_EDITOR_TOOLS::PasteItems( const TOOL_EVENT& aEvent )
1005 {
1006- // Parse clipboard
1007- PCB_IO io( CTL_FOR_CLIPBOARD );
1008- MODULE* pastedModule = NULL;
1009
1010- try
1011- {
1012- BOARD_ITEM* item = io.Parse( wxString( m_toolMgr->GetClipboard().c_str(), wxConvUTF8 ) );
1013- assert( item->Type() == PCB_MODULE_T );
1014- pastedModule = dyn_cast<MODULE*>( item );
1015- }
1016- catch( ... )
1017- {
1018- frame()->DisplayToolMsg( _( "Invalid clipboard contents" ) );
1019- return 0;
1020- }
1021+ MODULE* pastedModule = aEvent.Parameter<MODULE*>();
1022
1023 // Placement tool part
1024 VECTOR2I cursorPos = getViewControls()->GetCursorPosition();
1025@@ -427,7 +415,6 @@ int MODULE_EDITOR_TOOLS::PasteItems( const TOOL_EVENT& aEvent )
1026 // Whyyyyyyyyyyyyyyyyyyyyyy?! All other items conform to rotation performed
1027 // on its parent module, but texts are so independent..
1028 text->Rotate( text->GetPosition(), pastedModule->GetOrientation() );
1029- commit.Add( text );
1030 }
1031
1032 commit.Add( clone );
1033diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h
1034index b82f142..008213e 100644
1035--- a/pcbnew/tools/pcb_actions.h
1036+++ b/pcbnew/tools/pcb_actions.h
1037@@ -82,6 +82,7 @@ public:
1038 /// Filters the items in the current selection (invokes dialog)
1039 static TOOL_ACTION filterSelection;
1040
1041+
1042 // Edit Tool
1043 /// Activation of the edit tool
1044 static TOOL_ACTION editActivate;
1045@@ -300,6 +301,15 @@ public:
1046 /// Pasting module items from clipboard
1047 static TOOL_ACTION pasteItems;
1048
1049+ /// Copy selected items to clipboard
1050+ static TOOL_ACTION copyToClipboard;
1051+
1052+ /// Paste from clipboard
1053+ static TOOL_ACTION pasteFromClipboard;
1054+
1055+ /// Paste from clipboard
1056+ static TOOL_ACTION cutToClipboard;
1057+
1058 /// Display module edges as outlines
1059 static TOOL_ACTION moduleEdgeOutlines;
1060
1061diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp
1062index 438d521..0815944 100644
1063--- a/pcbnew/tools/pcb_editor_control.cpp
1064+++ b/pcbnew/tools/pcb_editor_control.cpp
1065@@ -390,7 +390,7 @@ int PCB_EDITOR_CONTROL::ViaSizeDec( const TOOL_EVENT& aEvent )
1066
1067 int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent )
1068 {
1069- MODULE* module = NULL;
1070+ MODULE* module = aEvent.Parameter<MODULE*>();
1071 KIGFX::VIEW* view = getView();
1072 KIGFX::VIEW_CONTROLS* controls = getViewControls();
1073 BOARD* board = getModel<BOARD>();
1074@@ -406,10 +406,20 @@ int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent )
1075 Activate();
1076 m_frame->SetToolID( ID_PCB_MODULE_BUTT, wxCURSOR_PENCIL, _( "Add footprint" ) );
1077
1078+ // Add all the drawable parts to preview
1079+ VECTOR2I cursorPos = controls->GetCursorPosition();
1080+ if( module )
1081+ {
1082+ module->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
1083+ preview.Add( module );
1084+ module->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Add, &preview, _1 ) );
1085+ view->Update( &preview );
1086+ }
1087+
1088 // Main loop: keep receiving events
1089 while( OPT_TOOL_EVENT evt = Wait() )
1090 {
1091- VECTOR2I cursorPos = controls->GetCursorPosition();
1092+ cursorPos = controls->GetCursorPosition();
1093
1094 if( evt->IsCancel() || evt->IsActivate() )
1095 {
1096diff --git a/pcbnew/tools/pcb_tool.h b/pcbnew/tools/pcb_tool.h
1097index 2c83ff9..0d98f92 100644
1098--- a/pcbnew/tools/pcb_tool.h
1099+++ b/pcbnew/tools/pcb_tool.h
1100@@ -124,6 +124,8 @@ protected:
1101 KIGFX::VIEW_CONTROLS* controls() const { return getViewControls(); }
1102 PCB_EDIT_FRAME* frame() const { return getEditFrame<PCB_EDIT_FRAME>(); }
1103 BOARD* board() const { return getModel<BOARD>(); }
1104+ MODULE* module() const { return board()->m_Modules; }
1105+
1106
1107 bool m_editModules;
1108 };
1109diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp
1110index 3ef2490..24bb034 100644
1111--- a/pcbnew/tools/pcbnew_control.cpp
1112+++ b/pcbnew/tools/pcbnew_control.cpp
1113@@ -27,7 +27,9 @@
1114 #include "pcbnew_control.h"
1115 #include "pcb_actions.h"
1116 #include "selection_tool.h"
1117+#include "edit_tool.h"
1118 #include "picker_tool.h"
1119+#include "pcb_editor_control.h"
1120 #include "grid_helper.h"
1121
1122 #include <class_board.h>
1123@@ -40,6 +42,8 @@
1124 #include <hotkeys.h>
1125 #include <properties.h>
1126 #include <io_mgr.h>
1127+#include <kicad_plugin.h>
1128+#include <kicad_clipboard.h>
1129
1130 #include <pcbnew_id.h>
1131 #include <wxPcbStruct.h>
1132@@ -51,6 +55,7 @@
1133 #include <pcb_painter.h>
1134 #include <origin_viewitem.h>
1135 #include <board_commit.h>
1136+#include <bitmaps.h>
1137
1138 #include <functional>
1139 using namespace std::placeholders;
1140@@ -221,9 +226,14 @@ TOOL_ACTION PCB_ACTIONS::toBeDone( "pcbnew.Control.toBeDone",
1141 AS_GLOBAL, 0, // dialog saying it is not implemented yet
1142 "", "" ); // so users are aware of that
1143
1144+TOOL_ACTION PCB_ACTIONS::pasteFromClipboard( "pcbnew.InteractiveEdit.pasteFromClipboard",
1145+ AS_GLOBAL, MD_CTRL + int( 'V' ),
1146+ _( "Paste" ), _( "Paste content from clipboard" ),
1147+ paste_xpm );
1148+
1149
1150 PCBNEW_CONTROL::PCBNEW_CONTROL() :
1151- TOOL_INTERACTIVE( "pcbnew.Control" ), m_frame( NULL )
1152+ PCB_TOOL( "pcbnew.Control" ), m_frame( NULL )
1153 {
1154 m_gridOrigin.reset( new KIGFX::ORIGIN_VIEWITEM() );
1155 }
1156@@ -240,7 +250,7 @@ void PCBNEW_CONTROL::Reset( RESET_REASON aReason )
1157
1158 if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH )
1159 {
1160- m_gridOrigin->SetPosition( getModel<BOARD>()->GetGridOrigin() );
1161+ m_gridOrigin->SetPosition( board()->GetGridOrigin() );
1162 getView()->Remove( m_gridOrigin.get() );
1163 getView()->Add( m_gridOrigin.get() );
1164 }
1165@@ -257,10 +267,10 @@ int PCBNEW_CONTROL::TrackDisplayMode( const TOOL_EVENT& aEvent )
1166 displ_opts->m_DisplayPcbTrackFill = !displ_opts->m_DisplayPcbTrackFill;
1167 settings->LoadDisplayOptions( displ_opts );
1168
1169- for( TRACK* track = getModel<BOARD>()->m_Track; track; track = track->Next() )
1170+ for( auto track : board()->Tracks() )
1171 {
1172 if( track->Type() == PCB_TRACE_T )
1173- getView()->Update( track, KIGFX::GEOMETRY );
1174+ view()->Update( track, KIGFX::GEOMETRY );
1175 }
1176
1177 m_frame->GetGalCanvas()->Refresh();
1178@@ -280,7 +290,7 @@ int PCBNEW_CONTROL::PadDisplayMode( const TOOL_EVENT& aEvent )
1179 displ_opts->m_DisplayPadFill = !displ_opts->m_DisplayPadFill;
1180 settings->LoadDisplayOptions( displ_opts );
1181
1182- for( MODULE* module = getModel<BOARD>()->m_Modules; module; module = module->Next() )
1183+ for( auto module : board()->Modules() )
1184 {
1185 for( auto pad : module->Pads() )
1186 getView()->Update( pad, KIGFX::GEOMETRY );
1187@@ -302,7 +312,7 @@ int PCBNEW_CONTROL::ViaDisplayMode( const TOOL_EVENT& aEvent )
1188 displ_opts->m_DisplayViaFill = !displ_opts->m_DisplayViaFill;
1189 settings->LoadDisplayOptions( displ_opts );
1190
1191- for( TRACK* track = getModel<BOARD>()->m_Track; track; track = track->Next() )
1192+ for( auto track : board()->Tracks() )
1193 {
1194 if( track->Type() == PCB_TRACE_T || track->Type() == PCB_VIA_T )
1195 getView()->Update( track, KIGFX::GEOMETRY );
1196@@ -332,9 +342,8 @@ int PCBNEW_CONTROL::ZoneDisplayMode( const TOOL_EVENT& aEvent )
1197
1198 settings->LoadDisplayOptions( displ_opts );
1199
1200- BOARD* board = getModel<BOARD>();
1201- for( int i = 0; i < board->GetAreaCount(); ++i )
1202- getView()->Update( board->GetArea( i ), KIGFX::GEOMETRY );
1203+ for( int i = 0; i < board()->GetAreaCount(); ++i )
1204+ view()->Update( board()->GetArea( i ), KIGFX::GEOMETRY );
1205
1206 m_frame->GetGalCanvas()->Refresh();
1207
1208@@ -385,7 +394,7 @@ int PCBNEW_CONTROL::LayerNext( const TOOL_EVENT& aEvent )
1209 if( layer < F_Cu || layer > B_Cu )
1210 return 0;
1211
1212- int layerCount = getModel<BOARD>()->GetCopperLayerCount();
1213+ int layerCount = board()->GetCopperLayerCount();
1214
1215 if( layer == layerCount - 2 || layerCount < 2 )
1216 layer = B_Cu;
1217@@ -409,7 +418,7 @@ int PCBNEW_CONTROL::LayerPrev( const TOOL_EVENT& aEvent )
1218 if( layer < F_Cu || layer > B_Cu )
1219 return 0;
1220
1221- int layerCount = getModel<BOARD>()->GetCopperLayerCount();
1222+ int layerCount = board()->GetCopperLayerCount();
1223
1224 if( layer == F_Cu || layerCount < 2 )
1225 layer = B_Cu;
1226@@ -640,7 +649,7 @@ int PCBNEW_CONTROL::GridSetOrigin( const TOOL_EVENT& aEvent )
1227
1228 int PCBNEW_CONTROL::GridResetOrigin( const TOOL_EVENT& aEvent )
1229 {
1230- getModel<BOARD>()->SetGridOrigin( wxPoint( 0, 0 ) );
1231+ board()->SetGridOrigin( wxPoint( 0, 0 ) );
1232 m_gridOrigin->SetPosition( VECTOR2D( 0, 0 ) );
1233
1234 return 0;
1235@@ -729,7 +738,83 @@ int PCBNEW_CONTROL::DeleteItemCursor( const TOOL_EVENT& aEvent )
1236 }
1237
1238
1239-int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent )
1240+int PCBNEW_CONTROL::PasteItemsFromClipboard( const TOOL_EVENT& aEvent )
1241+{
1242+ CLIPBOARD_IO pi;
1243+
1244+ pi.SetBoard( board() );
1245+ BOARD_ITEM* clipItem = pi.Parse();
1246+
1247+ if(!clipItem )
1248+ {
1249+ return 0;
1250+ }
1251+
1252+ bool editModules = m_editModules || frame()->IsType( FRAME_PCB_MODULE_EDITOR );
1253+
1254+ // The clipboard can contain two different things, an entire kicad_pcb
1255+ // or a single module
1256+
1257+ if ( editModules && ( !board() || !module() ) )
1258+ {
1259+ wxLogDebug( wxT( "Attempting to paste to empty module editor window\n") );
1260+ return 0;
1261+ }
1262+
1263+
1264+ switch( clipItem->Type() )
1265+ {
1266+ case PCB_T:
1267+ {
1268+ if( editModules )
1269+ {
1270+ wxLogDebug( wxT( "attempting to paste a pcb in the footprint editor\n") );
1271+ return 0;
1272+ }
1273+
1274+ placeBoardItems( static_cast<BOARD*>( clipItem ) );
1275+ break;
1276+ }
1277+
1278+ case PCB_MODULE_T:
1279+ {
1280+ std::vector<BOARD_ITEM *> items;
1281+
1282+ clipItem->SetParent( board() );
1283+
1284+ if( editModules )
1285+ {
1286+ auto mod = static_cast<MODULE *>( clipItem );
1287+
1288+ for ( auto pad : mod->Pads() )
1289+ {
1290+ pad->SetParent ( board()->m_Modules.GetFirst() );
1291+ items.push_back( pad );
1292+ }
1293+ for ( auto item : mod->GraphicalItems() )
1294+ {
1295+ item->SetParent ( board()->m_Modules.GetFirst() );
1296+ items.push_back( item );
1297+ }
1298+ }
1299+ else
1300+ {
1301+ items.push_back( clipItem );
1302+ }
1303+
1304+ placeBoardItems( items );
1305+ break;
1306+ }
1307+ default:
1308+ m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) );
1309+ // FAILED
1310+ break;
1311+ }
1312+ return 1;
1313+}
1314+
1315+
1316+int PCBNEW_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent )
1317 {
1318 int open_ctl;
1319 wxString fileName;
1320@@ -737,31 +822,151 @@ int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent )
1321 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1322
1323 if( !editFrame )
1324- return 0;
1325+ return 1;
1326
1327 // Pick a file to append
1328 if( !AskLoadBoardFileName( editFrame, &open_ctl, &fileName, true ) )
1329- return 0;
1330+ return 1;
1331
1332 IO_MGR::PCB_FILE_T pluginType = plugin_type( fileName, open_ctl );
1333 PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) );
1334
1335+ return AppendBoard( *pi, fileName );
1336+}
1337+
1338+
1339+int PCBNEW_CONTROL::placeBoardItems( BOARD* aBoard )
1340+{
1341+ std::vector<BOARD_ITEM*> items;
1342+ auto currentBoard = board();
1343+
1344+ PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1345+
1346+ if( currentBoard->GetCopperLayerCount() < aBoard->GetCopperLayerCount() )
1347+ {
1348+
1349+ if( !IsOK( editFrame, _( "Pasting will add extra layers to the PCB, continue? ") ) )
1350+ return 0;
1351+
1352+ currentBoard->SetCopperLayerCount( aBoard->GetCopperLayerCount() );
1353+ currentBoard->SetEnabledLayers( aBoard->GetEnabledLayers() );
1354+
1355+ editFrame->ReCreateLayerBox();
1356+ editFrame->ReFillLayerWidget();
1357+ }
1358+
1359+ for( auto track : aBoard->Tracks() )
1360+ {
1361+ if( !currentBoard->FindNet( track->GetNetname() ) )
1362+ {
1363+ NETINFO_ITEM* newNet = new NETINFO_ITEM( currentBoard,
1364+ track->GetNet()->GetNetname(), -1 );
1365+ currentBoard->Add( newNet );
1366+ currentBoard->BuildListOfNets();
1367+ }
1368+ track->SetParent( currentBoard );
1369+ track->SetNet( currentBoard->FindNet( track->GetNetname()) );
1370+ items.push_back( track );
1371+ }
1372+
1373+ for( auto module : aBoard->Modules() )
1374+ {
1375+ module->SetParent( currentBoard );
1376+ for( auto pad : module->Pads() )
1377+ {
1378+ // Check if the net is there by name, otherwise, add a new net and
1379+ // reassign the item to the new net
1380+ if( !currentBoard->FindNet( pad->GetNet()->GetNetname() ) )
1381+ {
1382+ // A net with -1 in netcode will be autoassigned a net
1383+ NETINFO_ITEM* newNet = new NETINFO_ITEM( currentBoard,
1384+ pad->GetNet()->GetNetname(), -1 );
1385+ currentBoard->Add( newNet );
1386+ currentBoard->BuildListOfNets();
1387+ }
1388+ pad->SetParent( module );
1389+ pad->SetNet( currentBoard->FindNet( pad->GetNetname() ) );
1390+ }
1391+
1392+ items.push_back( module );
1393+ }
1394+
1395+ for( auto drawing : aBoard->Drawings() )
1396+ {
1397+ items.push_back( drawing );
1398+ }
1399+
1400+ for( auto zone : aBoard->Zones() )
1401+ {
1402+ // Check if the net is there by name, otherwise, add a new net and
1403+ // reassign the item to the new net
1404+ if( !currentBoard->FindNet( zone->GetNet()->GetNetname() ) )
1405+ {
1406+ NETINFO_ITEM* newNet = new NETINFO_ITEM( currentBoard,
1407+ zone->GetNet()->GetNetname(), -1 );
1408+ currentBoard->Add( newNet );
1409+ currentBoard->BuildListOfNets();
1410+ }
1411+ zone->SetParent( currentBoard );
1412+ zone->SetNet( currentBoard->FindNet( zone->GetNetname() ) );
1413+ items.push_back( zone );
1414+ }
1415+
1416+ // A lot of changes to the nets may have happen, be on the safe side
1417+ // and rebuild these things
1418+ currentBoard->BuildListOfNets();
1419+ currentBoard->SynchronizeNetsAndNetClasses();
1420+ currentBoard->GetConnectivity()->RecalculateRatsnest();
1421+
1422+ // the list of items should now be sanitized to fit on the current board
1423+ return placeBoardItems( items );
1424+}
1425+
1426+
1427+int PCBNEW_CONTROL::placeBoardItems( std::vector<BOARD_ITEM*>& aItems )
1428+{
1429+ m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
1430+
1431+ auto selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
1432+ auto editTool = m_toolMgr->GetTool<EDIT_TOOL>();
1433+
1434+ SELECTION& selection = selectionTool->GetSelection();
1435+
1436+ for ( auto item : aItems )
1437+ {
1438+ item->SetSelected();
1439+ selection.Add( item );
1440+ editTool->GetCurrentCommit()->Add( item );
1441+ }
1442+
1443+ selection.SetReferencePoint( VECTOR2I( 0, 0 ) );
1444+
1445+ m_toolMgr->ProcessEvent( SELECTION_TOOL::SelectedEvent );
1446+ m_toolMgr->RunAction( PCB_ACTIONS::move, true );
1447+
1448+ return 0;
1449+}
1450+
1451+int PCBNEW_CONTROL::AppendBoard( PLUGIN& pi, wxString& fileName )
1452+{
1453+
1454+ PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
1455+ if( !editFrame )
1456+ return 1;
1457+
1458 // Mark existing tracks, in order to know what are the new tracks
1459 // Tracks are inserted, not appended, so mark existing tracks to be
1460 // able to select the new tracks only later
1461- BOARD* board = getModel<BOARD>();
1462+ BOARD* brd = board();
1463+ if( !brd )
1464+ return 1;
1465
1466- for( TRACK* track = board->m_Track; track; track = track->Next() )
1467+ for( auto track : brd->Tracks() )
1468 track->SetFlags( FLAG0 );
1469
1470- // Other items are appended to the item list, so keep trace to the last existing item is enough
1471- MODULE* module = board->m_Modules.GetLast();
1472- BOARD_ITEM* drawing = board->DrawingsList().GetLast();
1473- int zonescount = board->GetAreaCount();
1474-
1475 // Keep also the count of copper layers, to adjust if necessary
1476- int initialCopperLayerCount = board->GetCopperLayerCount();
1477- LSET initialEnabledLayers = board->GetEnabledLayers();
1478+ int initialCopperLayerCount = brd->GetCopperLayerCount();
1479+ LSET initialEnabledLayers = brd->GetEnabledLayers();
1480
1481 // Load the data
1482 try
1483@@ -778,7 +983,7 @@ int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent )
1484 props["page_height"] = ybuf;
1485
1486 editFrame->GetDesignSettings().m_NetClasses.Clear();
1487- pi->Load( fileName, board, &props );
1488+ pi.Load( fileName, brd, &props );
1489 }
1490 catch( const IO_ERROR& ioe )
1491 {
1492@@ -789,86 +994,25 @@ int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent )
1493 }
1494
1495 // rebuild nets and ratsnest before any use of nets
1496- board->BuildListOfNets();
1497- board->SynchronizeNetsAndNetClasses();
1498- board->GetConnectivity()->Build( board );
1499-
1500- m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
1501-
1502- SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
1503- SELECTION& selection = selectionTool->GetSelection();
1504- BOARD_COMMIT commit( editFrame );
1505-
1506- // Process the new items
1507- for( TRACK* track = board->m_Track; track; track = track->Next() )
1508- {
1509- if( track->GetFlags() & FLAG0 )
1510- {
1511- track->ClearFlags( FLAG0 );
1512- continue;
1513- }
1514-
1515- commit.Added( track );
1516- selection.Add( track );
1517- }
1518-
1519- module = module ? module->Next() : board->m_Modules;
1520-
1521- for( ; module; module = module->Next() )
1522- {
1523- commit.Added( module );
1524- selection.Add( module );
1525- }
1526-
1527- drawing = drawing ? drawing->Next() : board->DrawingsList();
1528-
1529- for( ; drawing; drawing = drawing->Next() )
1530- {
1531- commit.Added( drawing );
1532- selection.Add( drawing );
1533- }
1534+ brd->BuildListOfNets();
1535+ brd->SynchronizeNetsAndNetClasses();
1536
1537- for( ZONE_CONTAINER* zone = board->GetArea( zonescount ); zone;
1538- zone = board->GetArea( zonescount ) )
1539- {
1540- ++zonescount;
1541- commit.Added( zone );
1542- selection.Add( zone );
1543- }
1544-
1545- if( commit.Empty() )
1546- return 0;
1547-
1548- commit.Push( _( "Append a board" ) );
1549
1550 // Synchronize layers
1551 // we should not ask PLUGINs to do these items:
1552- int copperLayerCount = board->GetCopperLayerCount();
1553+ int copperLayerCount = brd->GetCopperLayerCount();
1554
1555 if( copperLayerCount > initialCopperLayerCount )
1556- board->SetCopperLayerCount( copperLayerCount );
1557+ brd->SetCopperLayerCount( copperLayerCount );
1558
1559 // Enable all used layers, and make them visible:
1560- LSET enabledLayers = board->GetEnabledLayers();
1561+ LSET enabledLayers = brd->GetEnabledLayers();
1562 enabledLayers |= initialEnabledLayers;
1563- board->SetEnabledLayers( enabledLayers );
1564- board->SetVisibleLayers( enabledLayers );
1565- editFrame->ReCreateLayerBox();
1566- editFrame->ReFillLayerWidget();
1567- static_cast<PCB_DRAW_PANEL_GAL*>( editFrame->GetGalCanvas() )->SyncLayersVisibility( board );
1568-
1569- // Start dragging the appended board
1570- if( selection.Size() ) // be sure at least one item is loaded
1571- {
1572- // Inform other potentially interested tools
1573- m_toolMgr->ProcessEvent( SELECTION_TOOL::SelectedEvent );
1574+ brd->SetEnabledLayers( enabledLayers );
1575+ brd->SetVisibleLayers( enabledLayers );
1576
1577- VECTOR2D v( static_cast<BOARD_ITEM*>( selection.Front() )->GetPosition() );
1578- getViewControls()->WarpCursor( v, true, true );
1579- m_toolMgr->InvokeTool( "pcbnew.InteractiveEdit" );
1580- }
1581
1582- return 0;
1583+ return placeBoardItems( brd );
1584 }
1585
1586
1587@@ -945,9 +1089,15 @@ void PCBNEW_CONTROL::setTransitions()
1588 Go( &PCBNEW_CONTROL::SwitchCursor, PCB_ACTIONS::switchCursor.MakeEvent() );
1589 Go( &PCBNEW_CONTROL::SwitchUnits, PCB_ACTIONS::switchUnits.MakeEvent() );
1590 Go( &PCBNEW_CONTROL::DeleteItemCursor, PCB_ACTIONS::deleteItemCursor.MakeEvent() );
1591- Go( &PCBNEW_CONTROL::AppendBoard, PCB_ACTIONS::appendBoard.MakeEvent() );
1592 Go( &PCBNEW_CONTROL::ShowHelp, PCB_ACTIONS::showHelp.MakeEvent() );
1593 Go( &PCBNEW_CONTROL::ToBeDone, PCB_ACTIONS::toBeDone.MakeEvent() );
1594+
1595+ // Append control
1596+ Go( &PCBNEW_CONTROL::AppendBoardFromFile,
1597+ PCB_ACTIONS::appendBoard.MakeEvent() );
1598+
1599+ Go( &PCBNEW_CONTROL::PasteItemsFromClipboard,
1600+ PCB_ACTIONS::pasteFromClipboard.MakeEvent() );
1601 }
1602
1603
1604diff --git a/pcbnew/tools/pcbnew_control.h b/pcbnew/tools/pcbnew_control.h
1605index 11131a2..a63bff6 100644
1606--- a/pcbnew/tools/pcbnew_control.h
1607+++ b/pcbnew/tools/pcbnew_control.h
1608@@ -25,21 +25,23 @@
1609 #ifndef PCBNEW_CONTROL_H
1610 #define PCBNEW_CONTROL_H
1611
1612-#include <tool/tool_interactive.h>
1613+#include <io_mgr.h>
1614 #include <memory>
1615+#include <tools/pcb_tool.h>
1616
1617 namespace KIGFX {
1618 class ORIGIN_VIEWITEM;
1619 }
1620-class PCB_BASE_FRAME;
1621
1622+class PCB_BASE_FRAME;
1623+class BOARD_ITEM;
1624 /**
1625 * Class PCBNEW_CONTROL
1626 *
1627 * Handles actions that are shared between different frames in pcbnew.
1628 */
1629
1630-class PCBNEW_CONTROL : public TOOL_INTERACTIVE
1631+class PCBNEW_CONTROL : public PCB_TOOL
1632 {
1633 public:
1634 PCBNEW_CONTROL();
1635@@ -79,7 +81,9 @@ public:
1636 int SwitchCursor( const TOOL_EVENT& aEvent );
1637 int SwitchUnits( const TOOL_EVENT& aEvent );
1638 int DeleteItemCursor( const TOOL_EVENT& aEvent );
1639- int AppendBoard( const TOOL_EVENT& aEvent );
1640+ int PasteItemsFromClipboard( const TOOL_EVENT& aEvent );
1641+ int AppendBoardFromFile( const TOOL_EVENT& aEvent );
1642+ int AppendBoard( PLUGIN& pi, wxString& fileName );
1643 int ShowHelp( const TOOL_EVENT& aEvent );
1644 int ToBeDone( const TOOL_EVENT& aEvent );
1645
1646@@ -87,6 +91,10 @@ public:
1647 void setTransitions() override;
1648
1649 private:
1650+
1651+ int placeBoardItems( BOARD* aBoard );
1652+ int placeBoardItems( std::vector<BOARD_ITEM*>& aItems );
1653+
1654 ///> Pointer to the currently used edit frame.
1655 PCB_BASE_FRAME* m_frame;
1656
1657diff --git a/pcbnew/tools/picker_tool.cpp b/pcbnew/tools/picker_tool.cpp
1658index 332aacf..a8e63d6 100644
1659--- a/pcbnew/tools/picker_tool.cpp
1660+++ b/pcbnew/tools/picker_tool.cpp
1661@@ -24,6 +24,7 @@
1662
1663 #include "picker_tool.h"
1664 #include "pcb_actions.h"
1665+#include "grid_helper.h"
1666
1667 #include <wxPcbStruct.h>
1668 #include <view/view_controls.h>
1669@@ -33,7 +34,7 @@ TOOL_ACTION PCB_ACTIONS::pickerTool( "pcbnew.Picker", AS_GLOBAL, 0, "", "", NULL
1670
1671
1672 PICKER_TOOL::PICKER_TOOL()
1673- : TOOL_INTERACTIVE( "pcbnew.Picker" )
1674+ : PCB_TOOL( "pcbnew.Picker" )
1675 {
1676 reset();
1677 }
1678@@ -42,6 +43,7 @@ PICKER_TOOL::PICKER_TOOL()
1679 int PICKER_TOOL::Main( const TOOL_EVENT& aEvent )
1680 {
1681 KIGFX::VIEW_CONTROLS* controls = getViewControls();
1682+ GRID_HELPER grid( frame() );
1683
1684 assert( !m_picking );
1685 m_picking = true;
1686@@ -49,13 +51,19 @@ int PICKER_TOOL::Main( const TOOL_EVENT& aEvent )
1687
1688 setControls();
1689
1690+
1691 while( OPT_TOOL_EVENT evt = Wait() )
1692 {
1693+ auto mousePos = controls->GetMousePosition();
1694+ auto p = grid.BestSnapAnchor( mousePos, nullptr );
1695+ controls->ForceCursorPosition( true, p );
1696+
1697 if( evt->IsClick( BUT_LEFT ) )
1698 {
1699 bool getNext = false;
1700- m_picked = controls->GetCursorPosition();
1701
1702+ m_picked = p;
1703+
1704 if( m_clickHandler )
1705 {
1706 try
1707@@ -83,6 +91,7 @@ int PICKER_TOOL::Main( const TOOL_EVENT& aEvent )
1708 }
1709
1710 reset();
1711+ controls->ForceCursorPosition( false );
1712 getEditFrame<PCB_BASE_FRAME>()->SetNoToolSelected();
1713
1714 return 0;
1715diff --git a/pcbnew/tools/picker_tool.h b/pcbnew/tools/picker_tool.h
1716index 75f5571..60b5b6f 100644
1717--- a/pcbnew/tools/picker_tool.h
1718+++ b/pcbnew/tools/picker_tool.h
1719@@ -25,21 +25,20 @@
1720 #ifndef PICKER_TOOL_H
1721 #define PICKER_TOOL_H
1722
1723-#include <tool/tool_interactive.h>
1724 #include <boost/optional/optional.hpp>
1725-#include <boost/function.hpp>
1726
1727+#include "pcb_tool.h"
1728 /**
1729 * @brief Generic tool for picking a point.
1730 */
1731-class PICKER_TOOL : public TOOL_INTERACTIVE
1732+class PICKER_TOOL : public PCB_TOOL
1733 {
1734 public:
1735 PICKER_TOOL();
1736 ~PICKER_TOOL() {}
1737
1738 ///> Mouse event click handler type.
1739- typedef boost::function<bool(const VECTOR2D&)> CLICK_HANDLER;
1740+ typedef std::function<bool(const VECTOR2D&)> CLICK_HANDLER;
1741
1742 ///> @copydoc TOOL_INTERACTIVE::Reset()
1743 void Reset( RESET_REASON aReason ) override {}
1744diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
1745index 4042925..1d4c636 100644
1746--- a/pcbnew/tools/selection_tool.cpp
1747+++ b/pcbnew/tools/selection_tool.cpp
1748@@ -58,6 +58,8 @@ using namespace std::placeholders;
1749 #include "pcb_bright_box.h"
1750 #include "pcb_actions.h"
1751
1752+#include "kicad_plugin.h"
1753+
1754 // Selection tool actions
1755 TOOL_ACTION PCB_ACTIONS::selectionActivate( "pcbnew.InteractiveSelection",
1756 AS_GLOBAL, 0,
1757@@ -120,8 +122,6 @@ TOOL_ACTION PCB_ACTIONS::filterSelection( "pcbnew.InteractiveSelection.FilterSel
1758 _( "Filter Selection" ), _( "Filter the types of items in the selection" ),
1759 nullptr );
1760
1761-
1762-
1763 class SELECT_MENU: public CONTEXT_MENU
1764 {
1765 public:
1766@@ -609,11 +609,13 @@ void SELECTION_TOOL::setTransitions()
1767 Go( &SELECTION_TOOL::selectCopper, PCB_ACTIONS::selectCopper.MakeEvent() );
1768 Go( &SELECTION_TOOL::selectNet, PCB_ACTIONS::selectNet.MakeEvent() );
1769 Go( &SELECTION_TOOL::selectSameSheet, PCB_ACTIONS::selectSameSheet.MakeEvent() );
1770+
1771 Go( &SELECTION_TOOL::selectOnSheetFromEeschema, PCB_ACTIONS::selectOnSheetFromEeschema.MakeEvent() );
1772 Go( &SELECTION_TOOL::updateSelection, PCB_ACTIONS::selectionModified.MakeEvent() );
1773 }
1774
1775
1776+
1777 SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock()
1778 {
1779 if( !m_locked || m_editModules )

Subscribers

People subscribed via source and target branches

to status/vote changes: