From 84b803042cfefd9146ae8d43b2e9517ff33ac1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20=C3=96dmark?= Date: Thu, 18 May 2017 19:21:30 +0200 Subject: [PATCH] Improvements to copy-paste -Can paste items from board to module-editor (only module parts) -Can paste entire modules just as copy -Can copy items inside module-editor Known limitations: -Will crash if trying to paste a module containing module_text inside the module editor ( dont know why, problem existed before ) -If copying things with strange layer names, the layer names will be changed. --- pcbnew/kicad_clipboard.cpp | 141 +++++++++++++++++++++------ pcbnew/kicad_clipboard.h | 4 + pcbnew/kicad_plugin.h | 4 + pcbnew/tools/module_editor_tools.cpp | 21 ++-- pcbnew/tools/pcb_editor_control.cpp | 10 +- pcbnew/tools/pcbnew_control.cpp | 72 +++++++++++++- pcbnew/tools/selection_tool.cpp | 5 +- 7 files changed, 208 insertions(+), 49 deletions(-) diff --git a/pcbnew/kicad_clipboard.cpp b/pcbnew/kicad_clipboard.cpp index 618f87c750..5cf3900d1f 100644 --- a/pcbnew/kicad_clipboard.cpp +++ b/pcbnew/kicad_clipboard.cpp @@ -39,7 +39,7 @@ #include CLIPBOARD_IO::CLIPBOARD_IO(): - PCB_IO(), + PCB_IO( CTL_STD_LAYER_NAMES ), m_formatter(), m_parser( new CLIPBOARD_PARSER() ) { @@ -58,49 +58,110 @@ void CLIPBOARD_IO::setBoard(BOARD* aBoard) m_board = aBoard; } -void CLIPBOARD_IO::writeHeader(BOARD* aBoard) -{ - formatHeader( aBoard ); -} - - void CLIPBOARD_IO::SaveSelection( SELECTION& aSelected ) { LOCALE_IO toggle; // toggles on, then off, the C locale. - // Prepare net mapping that assures that net codes saved in a file are consecutive integers - m_mapping->SetBoard( m_board ); - - // we will fake being a .kicad_pcb to get the full parser kicking - // This means we also need layers and nets - - m_formatter.Print( 0, "(kicad_pcb (version %d) (host pcbnew %s)\n", SEXPR_BOARD_FILE_VERSION, - m_formatter.Quotew( GetBuildVersion() ).c_str() ); - + // dont even start if the selection is empty if( aSelected.Empty() ) return; - writeHeader( m_board ); - - m_formatter.Print( 0, "\n" ); + // Prepare net mapping that assures that net codes saved in a file are consecutive integers + m_mapping->SetBoard( m_board ); + // Differentiate how it is formatted depending on what selection contains + bool onlyModuleParts = true; for( auto i : aSelected ) { - - // Dont format stuff that cannot exist standalone! + // check if it not one of the module primitives if( ( i->Type() != PCB_MODULE_EDGE_T ) && ( i->Type() != PCB_MODULE_TEXT_T ) && ( i->Type() != PCB_PAD_T ) ) { - //std::cout <<"type "<< i->Type() << std::endl; - auto item = static_cast( i ); - Format( item, 1 ); + onlyModuleParts = false; + continue; + } + } + + // if there is only parts of a module selected, format it as a new module else + // format it as an entire board + MODULE module( m_board ); + + // only a module selected. + if( aSelected.Size() == 1 && aSelected.Front()->Type() == PCB_MODULE_T ) + { + // make the module safe to transfer to other pcbs + MODULE* mod = static_cast( aSelected.Front() ); + for( D_PAD* pad = mod->Pads().GetFirst(); pad; pad = pad->Next() ) + { + pad->SetNetCode( 0,0 ); + } + mod->SetPath( "" ); + Format(static_cast( mod ) ); + } + + // partial module selected. + else if( onlyModuleParts ) + { + for( auto item : aSelected ) + { + auto clone = static_cast( item->Clone() ); + + // Do not add reference/value - convert them to the common type + if( TEXTE_MODULE* text = dyn_cast( clone ) ) + text->SetType( TEXTE_MODULE::TEXT_is_DIVERS ); + + // If it is only a module, clear the nets from the pads + if( clone->Type() == PCB_PAD_T ) + { + D_PAD* pad = static_cast(clone); + pad->SetNetCode(0,0); + } + + module.Add( clone ); } + // Set the new relative internal local coordinates of copied items + MODULE* editedModule = m_board->m_Modules; + wxPoint moveVector = module.GetPosition() + editedModule->GetPosition(); + + module.MoveAnchorPosition( moveVector ); + + Format( &module, 0 ); + } - m_formatter.Print( 0, "\n)" ); + // lots of shite selected + else + { + // we will fake being a .kicad_pcb to get the full parser kicking + // This means we also need layers and nets + m_formatter.Print( 0, "(kicad_pcb (version %d) (host pcbnew %s)\n", SEXPR_BOARD_FILE_VERSION, + m_formatter.Quotew( GetBuildVersion() ).c_str() ); + m_formatter.Print( 0, "\n" ); + + formatBoardLayers( m_board ); + formatNetInformation( m_board ); + + m_formatter.Print( 0, "\n" ); + + + for( auto i : aSelected ) + { + // Dont format stuff that cannot exist standalone! + if( ( i->Type() != PCB_MODULE_EDGE_T ) && + ( i->Type() != PCB_MODULE_TEXT_T ) && + ( i->Type() != PCB_PAD_T ) ) + { + //std::cout <<"type "<< i->Type() << std::endl; + auto item = static_cast( i ); + Format( item, 1 ); + } + + } + m_formatter.Print( 0, "\n)" ); + } if( wxTheClipboard->Open() ) { wxTheClipboard->SetData( new wxTextDataObject( wxString( m_formatter.GetString().c_str(), wxConvUTF8 ) ) ); @@ -108,6 +169,26 @@ void CLIPBOARD_IO::SaveSelection( SELECTION& aSelected ) } } +BOARD_ITEM* CLIPBOARD_IO::Parse() +{ + std::string result; + + if( wxTheClipboard->Open() ) + { + if( wxTheClipboard->IsSupported( wxDF_TEXT ) ) + { + wxTextDataObject data; + wxTheClipboard->GetData( data ); + + result = data.GetText().mb_str(); + } + + wxTheClipboard->Close(); + } + + return PCB_IO::Parse( result ); +} + void CLIPBOARD_IO::Save( const wxString& aFileName, BOARD* aBoard, const PROPERTIES* aProperties ) { @@ -163,11 +244,12 @@ BOARD* CLIPBOARD_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, const m_parser->SetLineReader( &reader ); m_parser->SetBoard( aAppendToMe ); + BOARD_ITEM* item; BOARD* board; try { - board = dynamic_cast( m_parser->Parse() ); + item = m_parser->Parse(); } catch( const FUTURE_FORMAT_ERROR& ) { @@ -182,14 +264,17 @@ BOARD* CLIPBOARD_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, const throw; } - if( !board ) + if( item->Type() != PCB_T ) { // The parser loaded something that was valid, but wasn't a board. THROW_PARSE_ERROR( _( "Clipboard content is not Kicad compatible" ), m_parser->CurSource(), m_parser->CurLine(), m_parser->CurLineNumber(), m_parser->CurOffset() ); } - + else + { + board = dynamic_cast( item ); + } // Give the filename to the board if it's new if( !aAppendToMe ) board->SetFileName( aFileName ); diff --git a/pcbnew/kicad_clipboard.h b/pcbnew/kicad_clipboard.h index 7c9badbe74..bb002a8286 100644 --- a/pcbnew/kicad_clipboard.h +++ b/pcbnew/kicad_clipboard.h @@ -47,6 +47,8 @@ public: throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR ) override { MODULE* mod = PCB_PARSER::parseMODULE(aInitialComments); + + //TODO: figure out better way of handling paths mod->SetPath( wxT( "" ) ); return mod; } @@ -66,6 +68,8 @@ public: */ void SaveSelection( SELECTION& selected ); + BOARD_ITEM* Parse(); + BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ) override; CLIPBOARD_IO(); ~CLIPBOARD_IO(); diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 89e038e595..ba006bfab2 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -188,15 +188,19 @@ protected: void init( const PROPERTIES* aProperties ); + /// formats the board setup information void formatSetup( BOARD* aBoard, int aNestLevel = 0 ) const throw( IO_ERROR ); + /// formats the General section of the file void formatGeneral( BOARD* aBoard, int aNestLevel = 0 ) const throw( IO_ERROR ); + /// formats the board layer information void formatBoardLayers( BOARD* aBoard, int aNestLevel = 0 ) const throw( IO_ERROR ); + /// formats the Nets and Netclasses void formatNetInformation( BOARD* aBoard, int aNestLevel = 0 ) const throw( IO_ERROR ); diff --git a/pcbnew/tools/module_editor_tools.cpp b/pcbnew/tools/module_editor_tools.cpp index 7c02ad5651..3a2e20b58a 100644 --- a/pcbnew/tools/module_editor_tools.cpp +++ b/pcbnew/tools/module_editor_tools.cpp @@ -23,6 +23,7 @@ */ #include "module_editor_tools.h" +#include "kicad_clipboard.h" #include "selection_tool.h" #include "pcb_actions.h" #include @@ -63,11 +64,11 @@ TOOL_ACTION PCB_ACTIONS::enumeratePads( "pcbnew.ModuleEditor.enumeratePads", _( "Enumerate Pads" ), _( "Enumerate pads" ), pad_enumerate_xpm, AF_ACTIVATE ); TOOL_ACTION PCB_ACTIONS::copyItems( "pcbnew.ModuleEditor.copyItems", - AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_COPY_ITEM ), + AS_ACTIVE, 0, _( "Copy" ), _( "Copy items" ), NULL, AF_ACTIVATE ); TOOL_ACTION PCB_ACTIONS::pasteItems( "pcbnew.ModuleEditor.pasteItems", - AS_GLOBAL, MD_CTRL + int( 'V' ), + AS_GLOBAL, 0, _( "Paste" ), _( "Paste items" ), NULL, AF_ACTIVATE ); TOOL_ACTION PCB_ACTIONS::moduleEdgeOutlines( "pcbnew.ModuleEditor.graphicOutlines", @@ -333,19 +334,15 @@ int MODULE_EDITOR_TOOLS::CopyItems( const TOOL_EVENT& aEvent ) int MODULE_EDITOR_TOOLS::PasteItems( const TOOL_EVENT& aEvent ) { - // Parse clipboard - PCB_IO io( CTL_FOR_CLIPBOARD ); - MODULE* pastedModule = NULL; - try - { - BOARD_ITEM* item = io.Parse( wxString( m_toolMgr->GetClipboard().c_str(), wxConvUTF8 ) ); - assert( item->Type() == PCB_MODULE_T ); - pastedModule = dyn_cast( item ); - } - catch( ... ) + MODULE* pastedModule = aEvent.Parameter(); + + for( BOARD_ITEM* item = pastedModule->GraphicalItems().GetFirst(); item; + item = item->Next() ) { frame()->DisplayToolMsg( _( "Invalid clipboard contents" ) ); + if( item->Type() == PCB_MODULE_TEXT_T ) + std::cout << "Crashing on this" << std::endl; return 0; } diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index e5d57ccc03..cb1af979f7 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -143,7 +143,7 @@ TOOL_ACTION PCB_ACTIONS::appendBoard( "pcbnew.EditorControl.appendBoard", "", "" ); TOOL_ACTION PCB_ACTIONS::appendClipboard( "pcbnew.EditorControl.appendClipboard", - AS_GLOBAL, MD_CTRL + MD_SHIFT + int( 'V' ), + AS_GLOBAL, MD_CTRL + int( 'V' ), "", "" ); TOOL_ACTION PCB_ACTIONS::highlightNet( "pcbnew.EditorControl.highlightNet", @@ -394,7 +394,7 @@ int PCB_EDITOR_CONTROL::ViaSizeDec( const TOOL_EVENT& aEvent ) int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent ) { - MODULE* module = NULL; + MODULE* module = aEvent.Parameter(); KIGFX::VIEW* view = getView(); KIGFX::VIEW_CONTROLS* controls = getViewControls(); BOARD* board = getModel(); @@ -410,6 +410,12 @@ int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent ) Activate(); m_frame->SetToolID( ID_PCB_MODULE_BUTT, wxCURSOR_PENCIL, _( "Add footprint" ) ); + // Add all the drawable parts to preview + if( module ) + { + preview.Add( module ); + } + // Main loop: keep receiving events while( OPT_TOOL_EVENT evt = Wait() ) { diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp index 98ebb63583..7a0263286a 100644 --- a/pcbnew/tools/pcbnew_control.cpp +++ b/pcbnew/tools/pcbnew_control.cpp @@ -28,6 +28,7 @@ #include "pcb_actions.h" #include "selection_tool.h" #include "picker_tool.h" +#include "pcb_editor_control.h" #include "grid_helper.h" #include @@ -729,11 +730,67 @@ int PCBNEW_CONTROL::DeleteItemCursor( const TOOL_EVENT& aEvent ) return 0; } + int PCBNEW_CONTROL::AppendBoardFromClipboard( const TOOL_EVENT& aEvent ) { CLIPBOARD_IO pi; - wxString noString(""); - return AppendBoard( pi, noString ); + wxString noString( "" ); + + BOARD* board = getModel(); + pi.setBoard( board ); + + BOARD_ITEM* clipItem = pi.Parse(); + TOOL_EVENT event; + + // The clipboard can contain two different things, an entire kicad_pcb + // or a single module + + PCB_BASE_FRAME* frame = getEditFrame(); + if(frame->IsType( FRAME_PCB) ) + { + + } + else if( frame->IsType( FRAME_PCB_MODULE_EDITOR ) ) + { + + } + else + { + return 1; + } + + switch( clipItem->Type() ) + { + case PCB_T: + if(frame->IsType( FRAME_PCB ) ) + { + if( AppendBoard( pi, noString ) ) + { + return 0; + } + } + break; + case PCB_MODULE_T: + + if(frame->IsType( FRAME_PCB) ) + { + m_toolMgr->RunAction( "pcbnew.EditorControl.placeModule", true, + static_cast( clipItem ) ); + return 0; + } + else if( frame->IsType( FRAME_PCB_MODULE_EDITOR ) ) + { + m_toolMgr->RunAction( "pcbnew.ModuleEditor.pasteItems", true, + static_cast( clipItem ) ); + return 0; + } + break; + default: + m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) ); + // FAILED + break; + } + return 1; } int PCBNEW_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent ) @@ -744,26 +801,31 @@ int PCBNEW_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent ) PCB_EDIT_FRAME* editFrame = dynamic_cast( m_frame ); if( !editFrame ) - return 0; + return 1; // Pick a file to append if( !AskLoadBoardFileName( editFrame, &open_ctl, &fileName, true ) ) - return 0; + return 1; IO_MGR::PCB_FILE_T pluginType = plugin_type( fileName, open_ctl ); PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) ); return AppendBoard( *pi, fileName ); - } + int PCBNEW_CONTROL::AppendBoard( PLUGIN& pi, wxString& fileName ) { PCB_EDIT_FRAME* editFrame = dynamic_cast( m_frame ); + if(!editFrame) + return 1; + // Mark existing tracks, in order to know what are the new tracks // Tracks are inserted, not appended, so mark existing tracks to be // able to select the new tracks only later BOARD* board = getModel(); + if(!board) + return 1; for( TRACK* track = board->m_Track; track; track = track->Next() ) track->SetFlags( FLAG0 ); diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index f5cde57247..902935695c 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -124,7 +124,7 @@ TOOL_ACTION PCB_ACTIONS::filterSelection( "pcbnew.InteractiveSelection.FilterSel nullptr ); TOOL_ACTION PCB_ACTIONS::selectionToClipboard( "pcbnew.InteractiveSelection.CopyToClipboard", - AS_GLOBAL, MD_CTRL + MD_SHIFT + int( 'C' ), + AS_GLOBAL, MD_CTRL + int( 'C' ), _( "Copy to Clipboard" ), _( "Copy selected content to clipboard" ), nullptr ); @@ -631,7 +631,8 @@ int SELECTION_TOOL::selectionToClipboard( const TOOL_EVENT& aEvent ) BOARD* board = getModel(); io.setBoard( board ); - auto& selection = RequestSelection( SELECTION_DELETABLE | SELECTION_SANITIZE_PADS ); + //auto& selection = RequestSelection( SELECTION_DELETABLE | SELECTION_SANITIZE_PADS ); + auto& selection = GetSelection(); io.SaveSelection( selection ); return 0;