From c0981b84441f5b71186351978959a82ef2867c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20=C3=96dmark?= Date: Sat, 15 Apr 2017 18:08:23 +0200 Subject: [PATCH] Added a new plugin that can save and load to clipboard, using ctrl+shift+c/v for copy pasting Added the needed kicad_clipboard files and made copy pasted modules have no path --- common/CMakeLists.txt | 1 + pcbnew/kicad_clipboard.cpp | 199 ++++++++++++++++++++++++++++ pcbnew/kicad_clipboard.h | 81 +++++++++++ pcbnew/kicad_plugin.cpp | 10 +- pcbnew/kicad_plugin.h | 6 +- pcbnew/pcb_parser.h | 2 +- pcbnew/tools/pcb_actions.h | 4 + pcbnew/tools/pcb_editor_control.cpp | 4 + pcbnew/tools/pcbnew_control.cpp | 26 +++- pcbnew/tools/pcbnew_control.h | 5 +- pcbnew/tools/selection_tool.cpp | 23 ++++ pcbnew/tools/selection_tool.h | 8 ++ 12 files changed, 360 insertions(+), 9 deletions(-) create mode 100644 pcbnew/kicad_clipboard.cpp create mode 100644 pcbnew/kicad_clipboard.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a325d2326e..93d4692a1d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -403,6 +403,7 @@ set( PCB_COMMON_SRCS ../pcbnew/eagle_plugin.cpp ../pcbnew/legacy_plugin.cpp ../pcbnew/kicad_plugin.cpp + ../pcbnew/kicad_clipboard.cpp ../pcbnew/gpcb_plugin.cpp ../pcbnew/pcb_netlist.cpp widgets/widget_net_selector.cpp diff --git a/pcbnew/kicad_clipboard.cpp b/pcbnew/kicad_clipboard.cpp new file mode 100644 index 0000000000..618f87c750 --- /dev/null +++ b/pcbnew/kicad_clipboard.cpp @@ -0,0 +1,199 @@ +/** + * @file kicad_clipboard.cpp + * @brief Kicad clipboard plugin that piggybacks on the kicad_plugin + * + * @author Kristoffer Ödmark + * @version 1.0 + * @date 2017-05-03 + * + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +CLIPBOARD_IO::CLIPBOARD_IO(): + PCB_IO(), + m_formatter(), + m_parser( new CLIPBOARD_PARSER() ) +{ + m_out = &m_formatter; +} + +CLIPBOARD_IO::~CLIPBOARD_IO(){} + +STRING_FORMATTER* CLIPBOARD_IO::GetFormatter() +{ + return &m_formatter; +} + +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() ); + + if( aSelected.Empty() ) + return; + + writeHeader( 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 ) ) ); + wxTheClipboard->Close(); + } +} + +void CLIPBOARD_IO::Save( const wxString& aFileName, BOARD* aBoard, + const PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( aProperties ); + + m_board = aBoard; // after init() + + // Prepare net mapping that assures that net codes saved in a file are consecutive integers + m_mapping->SetBoard( aBoard ); + + STRING_FORMATTER formatter; + + m_out = &formatter; + + m_out->Print( 0, "(kicad_pcb (version %d) (host pcbnew %s)\n", SEXPR_BOARD_FILE_VERSION, + formatter.Quotew( GetBuildVersion() ).c_str() ); + + Format( aBoard, 1 ); + + m_out->Print( 0, ")\n" ); + + if( wxTheClipboard->Open() ) + { + wxTheClipboard->SetData( new wxTextDataObject( wxString( m_formatter.GetString().c_str(), wxConvUTF8 ) ) ); + wxTheClipboard->Close(); + } + +} + +BOARD* CLIPBOARD_IO::Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) +{ + std::string result; + + if( wxTheClipboard->Open() ) + { + if( wxTheClipboard->IsSupported( wxDF_TEXT ) ) + { + wxTextDataObject data; + wxTheClipboard->GetData( data ); + + result = data.GetText().mb_str(); + } + + wxTheClipboard->Close(); + } + + STRING_LINE_READER reader(result, wxT( "clipboard" ) ); + + init( aProperties ); + + m_parser->SetLineReader( &reader ); + m_parser->SetBoard( aAppendToMe ); + + BOARD* board; + + try + { + board = dynamic_cast( m_parser->Parse() ); + } + catch( const FUTURE_FORMAT_ERROR& ) + { + // Don't wrap a FUTURE_FORMAT_ERROR in another + throw; + } + catch( const PARSE_ERROR& parse_error ) + { + if( m_parser->IsTooRecent() ) + throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() ); + else + throw; + } + + if( !board ) + { + // 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() ); + } + + // Give the filename to the board if it's new + if( !aAppendToMe ) + board->SetFileName( aFileName ); + + return board; +} + diff --git a/pcbnew/kicad_clipboard.h b/pcbnew/kicad_clipboard.h new file mode 100644 index 0000000000..7c9badbe74 --- /dev/null +++ b/pcbnew/kicad_clipboard.h @@ -0,0 +1,81 @@ +/** + * @file kicad_clipboard.h + * @brief + * @author Kristoffer Ödmark + * @version 1.0 + * @date 2017-05-03 + * + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef KICAD_CLIPBOARD_H +#define KICAD_CLIPBOARD_H + +#include +#include +#include +#include +#include + +#include + +class CLIPBOARD_PARSER : public PCB_PARSER +{ +public: + CLIPBOARD_PARSER( LINE_READER* aReader = NULL ): PCB_PARSER( aReader ) {}; + + MODULE* parseMODULE( wxArrayString* aInitialComments ) + throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR ) override + { + MODULE* mod = PCB_PARSER::parseMODULE(aInitialComments); + mod->SetPath( wxT( "" ) ); + return mod; + } +}; + +class CLIPBOARD_IO : public PCB_IO +{ + STRING_FORMATTER m_formatter; + CLIPBOARD_PARSER* m_parser; + +public: + /* Saves the entire board to the clipboard formatted using the PCB_IO formatting */ + void Save( const wxString& aFileName, BOARD* aBoard, + const PROPERTIES* aProperties = NULL ) override; + /* Writes all the settings of the BOARD* set by setBoard() and then adds all + * the BOARD_ITEM* found in selection formatted by PCB_IO to clipboard as a text + */ + void SaveSelection( SELECTION& selected ); + + BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties = NULL ) override; + CLIPBOARD_IO(); + ~CLIPBOARD_IO(); + + void setBoard(BOARD* aBoard); + void writeHeader(BOARD* aBoard); + STRING_FORMATTER* GetFormatter(); + + +}; + + +#endif /* KICAD_CLIPBOARD_H */ diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index bdc632d184..e90cb8757c 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -527,9 +527,9 @@ void PCB_IO::formatLayer( const BOARD_ITEM* aItem ) const m_out->Print( 0, " (layer %s)", m_out->Quotew( aItem->GetLayerName() ).c_str() ); } - -void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const +void PCB_IO::formatHeader( BOARD* aBoard, int aNestLevel ) const throw(IO_ERROR) { + const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings(); m_out->Print( 0, "\n" ); @@ -745,6 +745,12 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const filterNetClass( *aBoard, netclass ); // Remove empty nets (from a copy of a netclass) netclass.Format( m_out, aNestLevel, m_ctl ); } +} + +void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const + throw( IO_ERROR ) +{ + formatHeader( aBoard ); // Save the modules. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 6ec6c7c1c4..9182d5479e 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -109,7 +109,7 @@ public: return wxT( "kicad_pcb" ); } - void Save( const wxString& aFileName, BOARD* aBoard, + virtual void Save( const wxString& aFileName, BOARD* aBoard, const PROPERTIES* aProperties = NULL ) override; BOARD* Load( const wxString& aFileName, BOARD* aAppendToMe, @@ -188,6 +188,10 @@ protected: void init( const PROPERTIES* aProperties ); + /// writes everything that comes before the board_items, like settings and layers etc + void formatHeader( BOARD* aBoard, int aNestLevel = 0 ) const + throw( IO_ERROR ); + private: void format( BOARD* aBoard, int aNestLevel = 0 ) const; diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h index c03303c979..ab7b1bc602 100644 --- a/pcbnew/pcb_parser.h +++ b/pcbnew/pcb_parser.h @@ -117,7 +117,6 @@ class PCB_PARSER : public PCB_LEXER DRAWSEGMENT* parseDRAWSEGMENT(); TEXTE_PCB* parseTEXTE_PCB(); DIMENSION* parseDIMENSION(); - /** * Function parseMODULE * @param aInitialComments may be a pointer to a heap allocated initial comment block @@ -327,4 +326,5 @@ public: }; + #endif // _PCBNEW_PARSER_H_ diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index b82f14293a..1a622660a1 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -82,6 +82,9 @@ public: /// Filters the items in the current selection (invokes dialog) static TOOL_ACTION filterSelection; + /// Copy selected items to clipboard + static TOOL_ACTION selectionToClipboard; + // Edit Tool /// Activation of the edit tool static TOOL_ACTION editActivate; @@ -365,6 +368,7 @@ public: static TOOL_ACTION drillOrigin; static TOOL_ACTION crossProbeSchToPcb; static TOOL_ACTION appendBoard; + static TOOL_ACTION appendClipboard; static TOOL_ACTION showHelp; static TOOL_ACTION showLocalRatsnest; static TOOL_ACTION toBeDone; diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index 438d52135e..e5d57ccc03 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -142,6 +142,10 @@ TOOL_ACTION PCB_ACTIONS::appendBoard( "pcbnew.EditorControl.appendBoard", AS_GLOBAL, 0, "", "" ); +TOOL_ACTION PCB_ACTIONS::appendClipboard( "pcbnew.EditorControl.appendClipboard", + AS_GLOBAL, MD_CTRL + MD_SHIFT + int( 'V' ), + "", "" ); + TOOL_ACTION PCB_ACTIONS::highlightNet( "pcbnew.EditorControl.highlightNet", AS_GLOBAL, 0, "", "" ); diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp index 3ef2490bce..98ebb63583 100644 --- a/pcbnew/tools/pcbnew_control.cpp +++ b/pcbnew/tools/pcbnew_control.cpp @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include @@ -727,9 +729,14 @@ 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 ); +} - -int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent ) +int PCBNEW_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent ) { int open_ctl; wxString fileName; @@ -746,6 +753,13 @@ int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent ) 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 ); // 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 @@ -778,7 +792,7 @@ int PCBNEW_CONTROL::AppendBoard( const TOOL_EVENT& aEvent ) props["page_height"] = ybuf; editFrame->GetDesignSettings().m_NetClasses.Clear(); - pi->Load( fileName, board, &props ); + pi.Load( fileName, board, &props ); } catch( const IO_ERROR& ioe ) { @@ -945,9 +959,13 @@ void PCBNEW_CONTROL::setTransitions() Go( &PCBNEW_CONTROL::SwitchCursor, PCB_ACTIONS::switchCursor.MakeEvent() ); Go( &PCBNEW_CONTROL::SwitchUnits, PCB_ACTIONS::switchUnits.MakeEvent() ); Go( &PCBNEW_CONTROL::DeleteItemCursor, PCB_ACTIONS::deleteItemCursor.MakeEvent() ); - Go( &PCBNEW_CONTROL::AppendBoard, PCB_ACTIONS::appendBoard.MakeEvent() ); Go( &PCBNEW_CONTROL::ShowHelp, PCB_ACTIONS::showHelp.MakeEvent() ); Go( &PCBNEW_CONTROL::ToBeDone, PCB_ACTIONS::toBeDone.MakeEvent() ); + + // Append control + Go( &PCBNEW_CONTROL::AppendBoardFromFile,PCB_ACTIONS::appendBoard.MakeEvent() ); + Go( &PCBNEW_CONTROL::AppendBoardFromClipboard + ,PCB_ACTIONS::appendClipboard.MakeEvent() ); } diff --git a/pcbnew/tools/pcbnew_control.h b/pcbnew/tools/pcbnew_control.h index 11131a28fb..154fde2877 100644 --- a/pcbnew/tools/pcbnew_control.h +++ b/pcbnew/tools/pcbnew_control.h @@ -26,6 +26,7 @@ #define PCBNEW_CONTROL_H #include +#include #include namespace KIGFX { @@ -79,7 +80,9 @@ public: int SwitchCursor( const TOOL_EVENT& aEvent ); int SwitchUnits( const TOOL_EVENT& aEvent ); int DeleteItemCursor( const TOOL_EVENT& aEvent ); - int AppendBoard( const TOOL_EVENT& aEvent ); + int AppendBoardFromClipboard( const TOOL_EVENT& aEvent ); + int AppendBoardFromFile( const TOOL_EVENT& aEvent ); + int AppendBoard( PLUGIN& pi, wxString& fileName ); int ShowHelp( const TOOL_EVENT& aEvent ); int ToBeDone( const TOOL_EVENT& aEvent ); diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 4042925915..f5cde57247 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -58,6 +58,9 @@ using namespace std::placeholders; #include "pcb_bright_box.h" #include "pcb_actions.h" +#include "kicad_plugin.h" +#include "kicad_clipboard.h" + // Selection tool actions TOOL_ACTION PCB_ACTIONS::selectionActivate( "pcbnew.InteractiveSelection", AS_GLOBAL, 0, @@ -120,6 +123,11 @@ TOOL_ACTION PCB_ACTIONS::filterSelection( "pcbnew.InteractiveSelection.FilterSel _( "Filter Selection" ), _( "Filter the types of items in the selection" ), nullptr ); +TOOL_ACTION PCB_ACTIONS::selectionToClipboard( "pcbnew.InteractiveSelection.CopyToClipboard", + AS_GLOBAL, MD_CTRL + MD_SHIFT + int( 'C' ), + _( "Copy to Clipboard" ), _( "Copy selected content to clipboard" ), + nullptr ); + class SELECT_MENU: public CONTEXT_MENU @@ -609,11 +617,26 @@ void SELECTION_TOOL::setTransitions() Go( &SELECTION_TOOL::selectCopper, PCB_ACTIONS::selectCopper.MakeEvent() ); Go( &SELECTION_TOOL::selectNet, PCB_ACTIONS::selectNet.MakeEvent() ); Go( &SELECTION_TOOL::selectSameSheet, PCB_ACTIONS::selectSameSheet.MakeEvent() ); + + Go( &SELECTION_TOOL::selectionToClipboard, PCB_ACTIONS::selectionToClipboard.MakeEvent() ); + Go( &SELECTION_TOOL::selectOnSheetFromEeschema, PCB_ACTIONS::selectOnSheetFromEeschema.MakeEvent() ); Go( &SELECTION_TOOL::updateSelection, PCB_ACTIONS::selectionModified.MakeEvent() ); } +int SELECTION_TOOL::selectionToClipboard( const TOOL_EVENT& aEvent ) +{ + CLIPBOARD_IO io; + BOARD* board = getModel(); + + io.setBoard( board ); + auto& selection = RequestSelection( SELECTION_DELETABLE | SELECTION_SANITIZE_PADS ); + io.SaveSelection( selection ); + + return 0; +} + SELECTION_LOCK_FLAGS SELECTION_TOOL::CheckLock() { if( !m_locked || m_editModules ) diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h index 994a93031d..16a8cab578 100644 --- a/pcbnew/tools/selection_tool.h +++ b/pcbnew/tools/selection_tool.h @@ -299,6 +299,14 @@ private: */ void unselectVisually( BOARD_ITEM* aItem ); + /** + * Function selectionToClipboard() + * Sends the current selection to the clipboard by formatting it as a fake pcb + * see AppendBoardFromClipboard for importing + * @return True if it was sent succesfully + */ + int selectionToClipboard( const TOOL_EVENT& aEvent ); + /** * Function selectionContains() * Checks if the given point is placed within any of selected items' bounding box.