diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index fe75d5dc25..6fc2c9fa43 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -187,6 +187,7 @@ set( PCBNEW_CLASS_SRCS pcbframe.cpp pcb_base_edit_frame.cpp append_board_to_current.cpp + array_creator.cpp attribut.cpp board_items_to_polygon_shape_transform.cpp board_undo_redo.cpp diff --git a/pcbnew/array_creator.cpp b/pcbnew/array_creator.cpp new file mode 100644 index 0000000000..dee23633a3 --- /dev/null +++ b/pcbnew/array_creator.cpp @@ -0,0 +1,131 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Created on: 11 Mar 2016, author John Beard + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.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 + */ + +/** + * @file array_creator.cpp + */ + +#include "array_creator.h" + +#include + +#include + + +void ARRAY_CREATOR::Invoke() +{ + const int numItems = getNumberOfItemsToArray(); + + // bail out if no items + if( numItems == 0 ) + return; + + MODULE* const module = getModule(); + const bool isModuleEditor = module != NULL; + + const bool enableArrayNumbering = isModuleEditor; + const wxPoint rotPoint = getRotationCentre(); + + DIALOG_CREATE_ARRAY dialog( &m_parent, enableArrayNumbering, rotPoint ); + int ret = dialog.ShowModal(); + + DIALOG_CREATE_ARRAY::ARRAY_OPTIONS* const array_opts = dialog.GetArrayOptions(); + + if( ret == wxID_OK && array_opts != NULL ) + { + PICKED_ITEMS_LIST newItemsList; + + if( isModuleEditor ) + { + // modedit saves everything upfront + m_parent.SaveCopyInUndoList( getBoard()->m_Modules, UR_MODEDIT ); + } + + for ( int i = 0; i < numItems; ++i ) + { + BOARD_ITEM* item = getNthItemToArray( i ); + + if( item->Type() == PCB_PAD_T && !isModuleEditor ) + { + // If it is not the module editor, then duplicate the parent module instead + item = static_cast( item )->GetParent(); + } + + // The first item in list is the original item. We do not modify it + for( int ptN = 1; ptN < array_opts->GetArraySize(); ptN++ ) + { + BOARD_ITEM* new_item; + + if( isModuleEditor ) + { + // increment pad numbers if do any renumbering + // (we will number again later according to the numbering scheme if set) + new_item = module->DuplicateAndAddItem( + item, array_opts->ShouldNumberItems() ); + } + else + { + // PCB items keep the same numbering + new_item = getBoard()->DuplicateAndAddItem( item, false ); + + // @TODO: we should merge zones. This is a bit tricky, because + // the undo command needs saving old area, if it is merged. + } + + if( new_item ) + { + array_opts->TransformItem( ptN, new_item, rotPoint ); + + prePushAction( new_item ); + + newItemsList.PushItem( new_item ); // For undo list + + postPushAction( new_item ); + } + + // attempt to renumber items if the array parameters define + // a complete numbering scheme to number by (as opposed to + // implicit numbering by incrementing the items during creation + if( new_item && array_opts->NumberingStartIsSpecified() ) + { + // Renumber pads. Only new pad number renumbering has meaning, + // in the footprint editor. + if( new_item->Type() == PCB_PAD_T ) + { + const wxString padName = array_opts->GetItemNumber( ptN ); + static_cast( new_item )->SetPadName( padName ); + } + } + } + } + + if( !isModuleEditor ) + { + // Add all items as a single undo point for PCB editors + m_parent.SaveCopyInUndoList( newItemsList, UR_NEW ); + } + + finalise(); + } +} diff --git a/pcbnew/array_creator.h b/pcbnew/array_creator.h new file mode 100644 index 0000000000..c02539b2c3 --- /dev/null +++ b/pcbnew/array_creator.h @@ -0,0 +1,114 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Created on: 11 Mar 2016, author John Beard + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.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 + */ + +/** + * @file array_creator.h + */ + +#ifndef PCBNEW_ARRAY_CREATOR_H_ +#define PCBNEW_ARRAY_CREATOR_H_ + +#include + +#include +#include +#include + +/*! + * Class that performs array creation by producing a dialog to gather + * parameters and then creating and laying out the items. + * + * This is a template class which needs to be implemented by the relevant + * edit tooling, since the details of how the document is manipulated + * varies between edit modes (e.g. legacy or GAL) + */ +class ARRAY_CREATOR +{ +public: + ARRAY_CREATOR(PCB_BASE_FRAME& parent): + m_parent( parent ) + {} + + /*! + * Open the dialog, gather parameters and create the array + */ + void Invoke(); + +protected: + virtual ~ARRAY_CREATOR() {} + + PCB_BASE_FRAME& m_parent; + +private: + + /*! + * Get the BOARD that is currently being edited. + */ + virtual BOARD* getBoard() const = 0; + + /*! + * If editing a footprint, returns the relevant MODULE, else NULL + */ + virtual MODULE* getModule() const = 0; + + /*! + * @return number of original items to put into an array (eg size of the + * selection) + */ + virtual int getNumberOfItemsToArray() const = 0; + + /*! + * @return the n'th original item to be arrayed + */ + virtual BOARD_ITEM* getNthItemToArray( int n ) const = 0; + + /*! + * @return the rotation centre of all the items to be arrayed, when taken + * together + */ + virtual wxPoint getRotationCentre() const = 0; + + /*! + * Perform any relevant action before pushing a newly created array item + * to the BOARD + */ + virtual void prePushAction( BOARD_ITEM* new_item ) + {} + + /*! + * Perform any actions needed after pushing an item to the BOARD + */ + virtual void postPushAction( BOARD_ITEM* new_item ) + {} + + /*! + * Actions to perform after the array process is complete + */ + virtual void finalise() = 0; +}; + + + + +#endif /* PCBNEW_ARRAY_CREATOR_H_ */ diff --git a/pcbnew/dialogs/dialog_create_array.cpp b/pcbnew/dialogs/dialog_create_array.cpp index ee635208f2..cf5d7cd55a 100644 --- a/pcbnew/dialogs/dialog_create_array.cpp +++ b/pcbnew/dialogs/dialog_create_array.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -37,12 +38,14 @@ DIALOG_CREATE_ARRAY::CREATE_ARRAY_DIALOG_ENTRIES DIALOG_CREATE_ARRAY::m_options; -DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, wxPoint aOrigPos, - ARRAY_OPTIONS** aSettings ) : +DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, + bool enableNumbering, + wxPoint aOrigPos ) : DIALOG_CREATE_ARRAY_BASE( aParent ), CONFIG_SAVE_RESTORE_WINDOW( m_options.m_optionsSet ), - m_settings( aSettings ), - m_originalItemPosition( aOrigPos ) + m_settings( NULL ), + m_originalItemPosition( aOrigPos ), + m_numberingEnabled(enableNumbering) { // Set up numbering scheme drop downs // @@ -91,9 +94,6 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, wxPoint aOrig Add( m_entryGridPriNumberingOffset, m_options.m_gridPriNumberingOffset ); Add( m_entryGridSecNumberingOffset, m_options.m_gridSecNumberingOffset ); - Add( m_rbGridStartNumberingOpt, m_options.m_gridNumberingScheme ); - Add( m_rbCircStartNumberingOpt, m_options.m_circNumberingScheme ); - RestoreConfigToControls(); // Load units into labels @@ -118,6 +118,13 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, wxPoint aOrig } +DIALOG_CREATE_ARRAY::~DIALOG_CREATE_ARRAY() +{ + if( m_settings != NULL ) + delete m_settings; +} + + void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event ) { setControlEnablement(); @@ -125,13 +132,13 @@ void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event ) } -static const std::string& alphabetFromNumberingScheme( +static const wxString& alphabetFromNumberingScheme( DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type ) { - static const std::string alphaNumeric = "0123456789"; - static const std::string alphaHex = "0123456789ABCDEF"; - static const std::string alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - static const std::string alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY"; + static const wxString alphaNumeric = "0123456789"; + static const wxString alphaHex = "0123456789ABCDEF"; + static const wxString alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY"; switch( type ) { @@ -164,18 +171,18 @@ static bool schemeNonUnitColsStartAt0( DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE } -static bool getNumberingOffset( const std::string& str, +static bool getNumberingOffset( const wxString& str, DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type, int& offsetToFill ) { - const std::string alphabet = alphabetFromNumberingScheme( type ); + const wxString alphabet = alphabetFromNumberingScheme( type ); int offset = 0; const int radix = alphabet.length(); for( unsigned i = 0; i < str.length(); i++ ) { - int chIndex = alphabet.find( str[i], 0 ); + int chIndex = alphabet.Find( str[i], false ); if( chIndex == wxNOT_FOUND ) return false; @@ -195,10 +202,91 @@ static bool getNumberingOffset( const std::string& str, } +/** + * Validates and saves (if valid) the type and offset of an array axis numbering + * + * @param offsetEntry the entry of the offset (text) + * @param typeEntry the entry of the axis nmbering scheme (choice) + * @param type the destination of the type if valid + * @param offset the destination of the offset if valid + * @param errors error string accumulator + * @return if all valid + */ +static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry, + const wxChoice& typeEntry, + DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T& type, + int& offset, + wxArrayString& errors ) +{ + const int typeVal = typeEntry.GetSelection(); + // mind undefined casts to enums (should not be able to happen) + bool ok = typeVal <= DIALOG_CREATE_ARRAY::NUMBERING_TYPE_MAX; + + if( ok ) + { + type = (DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T) typeVal; + } + else + { + wxString err; + err.Printf( _("Unrecognised numbering scheme: %d"), typeVal ); + errors.Add( err ); + // we can't proceed - we don't know the numbering type + return false; + } + + const wxString text = offsetEntry.GetValue(); + ok = getNumberingOffset( text, type, offset ); + + if( !ok ) + { + const wxString& alphabet = alphabetFromNumberingScheme( type ); + + wxString err; + err.Printf( _( "Could not determine numbering start from \"%s\": " + "expected value consistent with alphabet \"%s\"" ), + text, alphabet ); + errors.Add(err); + } + + return ok; +} + + +/** + * Validate and save a long integer entry + * + * @param entry the text entry to read from + * @param dest the value destination + * @param description description of the field (used if the value is not OK) + * @param errors a list of errors to add any error to + * @return valid + */ +static bool validateLongEntry( const wxTextEntry& entry, + long& dest, + const wxString description, + wxArrayString& errors ) +{ + bool ok = true; + + if( !entry.GetValue().ToLong( &dest ) ) + { + wxString err; + err.Printf( _("Bad integral value for %s: %s"), description, entry.GetValue() ); + errors.Add( err ); + ok = false; + } + + return ok; +} + + void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event ) { ARRAY_OPTIONS* newSettings = NULL; + wxArrayString errorStrs; + const wxWindow* page = m_gridTypeNotebook->GetCurrentPage(); if( page == m_gridPanel ) @@ -207,8 +295,11 @@ void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event ) bool ok = true; // ints - ok = ok && m_entryNx->GetValue().ToLong( &newGrid->m_nx ); - ok = ok && m_entryNy->GetValue().ToLong( &newGrid->m_ny ); + ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), + errorStrs); + ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), + errorStrs); + newGrid->m_delta.x = DoubleValueFromString( g_UserUnit, m_entryDx->GetValue() ); newGrid->m_delta.y = DoubleValueFromString( g_UserUnit, m_entryDy->GetValue() ); @@ -216,40 +307,38 @@ void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event ) newGrid->m_offset.x = DoubleValueFromString( g_UserUnit, m_entryOffsetX->GetValue() ); newGrid->m_offset.y = DoubleValueFromString( g_UserUnit, m_entryOffsetY->GetValue() ); - ok = ok && m_entryStagger->GetValue().ToLong( &newGrid->m_stagger ); + ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), + errorStrs); newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0; newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0; newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue(); - newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0; + newGrid->m_shouldNumber = m_numberingEnabled; - // this is only correct if you set the choice up according to the enum size and order - ok = ok && m_choicePriAxisNumbering->GetSelection() <= NUMBERING_TYPE_MAX - && m_choiceSecAxisNumbering->GetSelection() <= NUMBERING_TYPE_MAX; - - // mind undefined casts to enums (should not be able to happen) - if( ok ) + if ( m_numberingEnabled ) { - newGrid->m_priAxisNumType = - (ARRAY_NUMBERING_TYPE_T) m_choicePriAxisNumbering->GetSelection(); - newGrid->m_secAxisNumType = - (ARRAY_NUMBERING_TYPE_T) m_choiceSecAxisNumbering->GetSelection(); + newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0; + + bool numOk = validateNumberingTypeAndOffset( + *m_entryGridPriNumberingOffset, *m_choicePriAxisNumbering, + newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX, + errorStrs ); + + if( newGrid->m_2dArrayNumbering ) + { + numOk = validateNumberingTypeAndOffset( + *m_entryGridSecNumberingOffset, *m_choiceSecAxisNumbering, + newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY, + errorStrs ) && numOk; + } + + ok = ok && numOk; + + newGrid->m_numberingStartIsSpecified = m_rbGridStartNumberingOpt->GetSelection() == 1; } - // Work out the offsets for the numbering - ok = ok && getNumberingOffset( - m_entryGridPriNumberingOffset->GetValue().ToStdString(), - newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX ); - - if( newGrid->m_2dArrayNumbering ) - ok = ok && getNumberingOffset( - m_entryGridSecNumberingOffset->GetValue().ToStdString(), - newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY ); - - newGrid->m_shouldRenumber = m_rbGridStartNumberingOpt->GetSelection() == 1; - // Only use settings if all values are good if( ok ) newSettings = newGrid; @@ -265,13 +354,23 @@ void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event ) newCirc->m_centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() ); newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() ); - ok = ok && m_entryCircCount->GetValue().ToLong( &newCirc->m_nPts ); + + ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, + _("point count"), errorStrs); newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue(); - newCirc->m_shouldRenumber = m_rbCircStartNumberingOpt->GetSelection() == 1; - newCirc->m_numberingType = NUMBERING_NUMERIC; - ok = ok && m_entryCircNumberingStart->GetValue().ToLong( &newCirc->m_numberingOffset ); + newCirc->m_shouldNumber = m_numberingEnabled; + + if ( m_numberingEnabled ) + { + newCirc->m_numberingStartIsSpecified = m_rbCircStartNumberingOpt->GetSelection() == 1; + newCirc->m_numberingType = NUMBERING_NUMERIC; + + ok = ok && validateLongEntry(*m_entryCircNumberingStart, + newCirc->m_numberingOffset, + _("numbering start"), errorStrs); + } // Only use settings if all values are good if( ok ) @@ -283,43 +382,70 @@ void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event ) // If we got good settings, send them out and finish if( newSettings ) { - delete *m_settings; + delete m_settings; // assign pointer and ownership here - *m_settings = newSettings; + m_settings = newSettings; ReadConfigFromControls(); EndModal( wxID_OK ); } - else - wxMessageBox( _("Bad parameters" ) ); + { + wxString errorStr; + + if( errorStrs.IsEmpty() ) + errorStr = _("Bad parameters"); + else + errorStr = boost::algorithm::join( errorStrs, "\n" ); + + wxMessageBox( errorStr ); + } } void DIALOG_CREATE_ARRAY::setControlEnablement() { - const bool renumber = m_rbGridStartNumberingOpt->GetSelection() == 1; + if ( m_numberingEnabled ) + { + const bool renumber = m_rbGridStartNumberingOpt->GetSelection() == 1; - // If we're not renumbering, we can't set the numbering scheme - // or axis numbering types - m_radioBoxGridNumberingScheme->Enable( renumber ); - m_labelPriAxisNumbering->Enable( renumber ); - m_choicePriAxisNumbering->Enable( renumber ); + // If we're not renumbering, we can't set the numbering scheme + // or axis numbering types + m_radioBoxGridNumberingScheme->Enable( renumber ); + m_labelPriAxisNumbering->Enable( renumber ); + m_choicePriAxisNumbering->Enable( renumber ); - // Disable the secondary axis numbering option if the - // numbering scheme doesn't have two axes - const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0; + // Disable the secondary axis numbering option if the + // numbering scheme doesn't have two axes + const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0; - m_labelSecAxisNumbering->Enable( renumber && num2d ); - m_choiceSecAxisNumbering->Enable( renumber && num2d ); + m_labelSecAxisNumbering->Enable( renumber && num2d ); + m_choiceSecAxisNumbering->Enable( renumber && num2d ); - // We can only set an offset if we renumber - m_labelGridNumberingOffset->Enable( renumber ); - m_entryGridPriNumberingOffset->Enable( renumber ); - m_entryGridSecNumberingOffset->Enable( renumber && num2d ); + // We can only set an offset if we renumber + m_labelGridNumberingOffset->Enable( renumber ); + m_entryGridPriNumberingOffset->Enable( renumber ); + m_entryGridSecNumberingOffset->Enable( renumber && num2d ); - m_entryCircNumberingStart->Enable( m_rbCircStartNumberingOpt->GetSelection() == 1 ); + m_entryCircNumberingStart->Enable( m_rbCircStartNumberingOpt->GetSelection() == 1 ); + } + else + { + // grid + m_rbGridStartNumberingOpt->Enable( false ); + m_checkBoxGridReverseNumbering->Enable( false ); + m_radioBoxGridNumberingAxis->Enable( false ); + m_radioBoxGridNumberingScheme->Enable( false ); + m_choiceSecAxisNumbering->Enable( false ); + m_choicePriAxisNumbering->Enable( false ); + m_entryGridPriNumberingOffset->Enable( false ); + m_entryGridSecNumberingOffset->Enable( false ); + + // circular + m_rbCircStartNumberingOpt->Enable( false ); + m_entryCircNumberingStart->Enable( false ); + } } @@ -340,16 +466,16 @@ void DIALOG_CREATE_ARRAY::calculateCircularArrayProperties() // ARRAY OPTION implementation functions -------------------------------------- -std::string DIALOG_CREATE_ARRAY::ARRAY_OPTIONS::getCoordinateNumber( int n, +wxString DIALOG_CREATE_ARRAY::ARRAY_OPTIONS::getCoordinateNumber( int n, ARRAY_NUMBERING_TYPE_T type ) { - std::string itemNum; - const std::string& alphabet = alphabetFromNumberingScheme( type ); + wxString itemNum; + const wxString& alphabet = alphabetFromNumberingScheme( type ); const bool nonUnitColsStartAt0 = schemeNonUnitColsStartAt0( type ); bool firstRound = true; - int radix = alphabet.length(); + int radix = alphabet.Length(); do { int modN = n % radix; @@ -480,9 +606,5 @@ void DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::TransformItem( int n, BOARD_IT wxString DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::GetItemNumber( int aN ) const { - // The first new pad has aN number == 1, not 0 - if( m_shouldRenumber ) // numbering pad from initial user value - return getCoordinateNumber( aN - 1 + m_numberingOffset, m_numberingType ); - else // numbering pad from inital pad number - return getCoordinateNumber( aN + m_numberingOffset, m_numberingType ); + return getCoordinateNumber( aN + m_numberingOffset, m_numberingType ); } diff --git a/pcbnew/dialogs/dialog_create_array.h b/pcbnew/dialogs/dialog_create_array.h index 11aa83f7f1..4ff244926e 100644 --- a/pcbnew/dialogs/dialog_create_array.h +++ b/pcbnew/dialogs/dialog_create_array.h @@ -28,6 +28,9 @@ // Include the wxFormBuider header base: #include +#include +#include + #include class CONFIG_SAVE_RESTORE_WINDOW @@ -72,7 +75,7 @@ protected: ctrls.push_back( ctrlInfo ); } - void Add( wxTextCtrl* ctrl, std::string& dest ) + void Add( wxTextCtrl* ctrl, wxString& dest ) { CONFIG_CTRL_T ctrlInfo = { ctrl, CFG_CTRL_TEXT, (void*) &dest }; @@ -105,7 +108,7 @@ protected: break; case CFG_CTRL_TEXT: - *(std::string*) iter->dest = static_cast( iter->control )->GetValue(); + *(wxString*) iter->dest = static_cast( iter->control )->GetValue(); break; case CFG_CTRL_CHOICE: @@ -144,7 +147,7 @@ protected: break; case CFG_CTRL_TEXT: - static_cast( iter->control )->SetValue( *(std::string*) iter->dest ); + static_cast( iter->control )->SetValue( *(wxString*) iter->dest ); break; case CFG_CTRL_CHOICE: @@ -201,13 +204,13 @@ public: { ARRAY_OPTIONS( ARRAY_TYPE_T aType ) : m_type( aType ), - m_shouldRenumber( false ) + m_shouldNumber( false ), + m_numberingStartIsSpecified( false ) {} virtual ~ARRAY_OPTIONS() {}; ARRAY_TYPE_T m_type; - bool m_shouldRenumber; /*! * Function GetArrayPositions @@ -222,13 +225,37 @@ public: virtual wxString GetItemNumber( int n ) const = 0; virtual wxString InterpolateNumberIntoString( int n, const wxString& pattern ) const; - bool ShouldRenumberItems() const + /*! + * @return are the items in this array numberred, or are all the + * items numbered the same + */ + bool ShouldNumberItems() const { - return m_shouldRenumber; + return m_shouldNumber; } -protected: - static std::string getCoordinateNumber( int n, ARRAY_NUMBERING_TYPE_T type ); + /*! + * @return is the numbering is enabled and should start at a point + * specified in these options or is it implicit according to the calling + * code? + */ + bool NumberingStartIsSpecified() const + { + return m_shouldNumber && m_numberingStartIsSpecified; + } + + protected: + static wxString getCoordinateNumber( int n, ARRAY_NUMBERING_TYPE_T type ); + + // allow the dialog to set directly + friend class DIALOG_CREATE_ARRAY; + + /// True if this array numbers the new items + bool m_shouldNumber; + + /// True if this array's number starts from the preset point + /// False if the array numbering starts from some externally provided point + bool m_numberingStartIsSpecified; }; struct ARRAY_GRID_OPTIONS : public ARRAY_OPTIONS @@ -289,16 +316,27 @@ private: }; // Constructor and destructor - DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, wxPoint aOrigPos, ARRAY_OPTIONS** settings ); - virtual ~DIALOG_CREATE_ARRAY() {}; + DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, bool enableNumbering, + wxPoint aOrigPos ); + + ~DIALOG_CREATE_ARRAY(); + + /*! + * @return the array options set by this dialogue, or NULL if they were + * not set, or could not be set + */ + ARRAY_OPTIONS* GetArrayOptions() const + { + return m_settings; + } private: /** * The settings object returned to the caller. - * We update the caller's object and never have ownership + * We retain ownership of this */ - ARRAY_OPTIONS** m_settings; + ARRAY_OPTIONS* m_settings; /* * The position of the original item(s), used for finding radius, etc @@ -329,27 +367,28 @@ private: bool m_optionsSet; - std::string m_gridNx, m_gridNy, - m_gridDx, m_gridDy, - m_gridOffsetX, m_gridOffsetY, - m_gridStagger; + wxString m_gridNx, m_gridNy, + m_gridDx, m_gridDy, + m_gridOffsetX, m_gridOffsetY, + m_gridStagger; - int m_gridStaggerType, m_gridNumberingAxis; + int m_gridStaggerType, m_gridNumberingAxis; bool m_gridNumberingReverseAlternate; int m_grid2dArrayNumbering; int m_gridPriAxisNumScheme, m_gridSecAxisNumScheme; - std::string m_gridPriNumberingOffset, m_gridSecNumberingOffset; + wxString m_gridPriNumberingOffset, m_gridSecNumberingOffset; - std::string m_circCentreX, m_circCentreY, - m_circAngle, m_circCount, m_circNumberingOffset; + wxString m_circCentreX, m_circCentreY, + m_circAngle, m_circCount, m_circNumberingOffset; bool m_circRotate; int m_arrayTypeTab; - int m_gridNumberingScheme; - int m_circNumberingScheme; }; - static CREATE_ARRAY_DIALOG_ENTRIES m_options; + // some uses of arrays might not allow component renumbering + bool m_numberingEnabled; + // saved array options + static CREATE_ARRAY_DIALOG_ENTRIES m_options; }; #endif // __DIALOG_CREATE_ARRAY__ diff --git a/pcbnew/dialogs/dialog_create_array_base.cpp b/pcbnew/dialogs/dialog_create_array_base.cpp index f9e7b43e54..67d5e6ef9e 100644 --- a/pcbnew/dialogs/dialog_create_array_base.cpp +++ b/pcbnew/dialogs/dialog_create_array_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Jan 1 2016) +// C++ code generated with wxFormBuilder (version Mar 9 2016) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -100,51 +100,50 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID bSizer2->Add( gbSizer1, 1, wxEXPAND, 5 ); - wxBoxSizer* bSizer3; - bSizer3 = new wxBoxSizer( wxVERTICAL ); + m_gridPadNumberingSizer = new wxBoxSizer( wxVERTICAL ); wxString m_radioBoxGridNumberingAxisChoices[] = { _("Horizontal, then vertical"), _("Vertical, then horizontal") }; int m_radioBoxGridNumberingAxisNChoices = sizeof( m_radioBoxGridNumberingAxisChoices ) / sizeof( wxString ); m_radioBoxGridNumberingAxis = new wxRadioBox( m_gridPanel, wxID_ANY, _("Pad Numbering Direction"), wxDefaultPosition, wxDefaultSize, m_radioBoxGridNumberingAxisNChoices, m_radioBoxGridNumberingAxisChoices, 1, wxRA_SPECIFY_COLS ); m_radioBoxGridNumberingAxis->SetSelection( 0 ); - bSizer3->Add( m_radioBoxGridNumberingAxis, 0, wxALL|wxEXPAND, 5 ); + m_gridPadNumberingSizer->Add( m_radioBoxGridNumberingAxis, 0, wxALL|wxEXPAND, 5 ); m_checkBoxGridReverseNumbering = new wxCheckBox( m_gridPanel, wxID_ANY, _("Reverse pad numbering on alternate rows or columns"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer3->Add( m_checkBoxGridReverseNumbering, 0, wxALL, 5 ); + m_gridPadNumberingSizer->Add( m_checkBoxGridReverseNumbering, 0, wxALL, 5 ); wxString m_rbGridStartNumberingOptChoices[] = { _("Use first free number"), _("From start value") }; int m_rbGridStartNumberingOptNChoices = sizeof( m_rbGridStartNumberingOptChoices ) / sizeof( wxString ); m_rbGridStartNumberingOpt = new wxRadioBox( m_gridPanel, wxID_ANY, _("Initial pad number"), wxDefaultPosition, wxDefaultSize, m_rbGridStartNumberingOptNChoices, m_rbGridStartNumberingOptChoices, 1, wxRA_SPECIFY_COLS ); m_rbGridStartNumberingOpt->SetSelection( 1 ); - bSizer3->Add( m_rbGridStartNumberingOpt, 0, wxALL|wxEXPAND, 5 ); + m_gridPadNumberingSizer->Add( m_rbGridStartNumberingOpt, 0, wxALL|wxEXPAND, 5 ); wxString m_radioBoxGridNumberingSchemeChoices[] = { _("Continuous (1, 2, 3...)"), _("Coordinate (A1, A2, ... B1, ...)") }; int m_radioBoxGridNumberingSchemeNChoices = sizeof( m_radioBoxGridNumberingSchemeChoices ) / sizeof( wxString ); m_radioBoxGridNumberingScheme = new wxRadioBox( m_gridPanel, wxID_ANY, _("Pad Numbering Scheme"), wxDefaultPosition, wxDefaultSize, m_radioBoxGridNumberingSchemeNChoices, m_radioBoxGridNumberingSchemeChoices, 1, wxRA_SPECIFY_COLS ); m_radioBoxGridNumberingScheme->SetSelection( 1 ); - bSizer3->Add( m_radioBoxGridNumberingScheme, 0, wxALL|wxEXPAND, 5 ); + m_gridPadNumberingSizer->Add( m_radioBoxGridNumberingScheme, 0, wxALL|wxEXPAND, 5 ); m_labelPriAxisNumbering = new wxStaticText( m_gridPanel, wxID_ANY, _("Primary axis numbering:"), wxDefaultPosition, wxDefaultSize, 0 ); m_labelPriAxisNumbering->Wrap( -1 ); - bSizer3->Add( m_labelPriAxisNumbering, 0, wxLEFT|wxRIGHT|wxTOP, 5 ); + m_gridPadNumberingSizer->Add( m_labelPriAxisNumbering, 0, wxLEFT|wxRIGHT|wxTOP, 5 ); wxArrayString m_choicePriAxisNumberingChoices; m_choicePriAxisNumbering = new wxChoice( m_gridPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choicePriAxisNumberingChoices, 0 ); m_choicePriAxisNumbering->SetSelection( 0 ); - bSizer3->Add( m_choicePriAxisNumbering, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + m_gridPadNumberingSizer->Add( m_choicePriAxisNumbering, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); m_labelSecAxisNumbering = new wxStaticText( m_gridPanel, wxID_ANY, _("Secondary axis numbering:"), wxDefaultPosition, wxDefaultSize, 0 ); m_labelSecAxisNumbering->Wrap( -1 ); m_labelSecAxisNumbering->Enable( false ); - bSizer3->Add( m_labelSecAxisNumbering, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + m_gridPadNumberingSizer->Add( m_labelSecAxisNumbering, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); wxArrayString m_choiceSecAxisNumberingChoices; m_choiceSecAxisNumbering = new wxChoice( m_gridPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceSecAxisNumberingChoices, 0 ); m_choiceSecAxisNumbering->SetSelection( 0 ); m_choiceSecAxisNumbering->Enable( false ); - bSizer3->Add( m_choiceSecAxisNumbering, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + m_gridPadNumberingSizer->Add( m_choiceSecAxisNumbering, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); wxBoxSizer* bSizer5; bSizer5 = new wxBoxSizer( wxHORIZONTAL ); @@ -160,10 +159,10 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID bSizer5->Add( m_entryGridSecNumberingOffset, 0, wxALL, 5 ); - bSizer3->Add( bSizer5, 0, wxEXPAND, 5 ); + m_gridPadNumberingSizer->Add( bSizer5, 0, wxEXPAND, 5 ); - bSizer2->Add( bSizer3, 0, wxALL|wxEXPAND, 5 ); + bSizer2->Add( m_gridPadNumberingSizer, 0, wxALL|wxEXPAND, 5 ); m_gridPanel->SetSizer( bSizer2 ); @@ -244,30 +243,29 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID bSizer4->Add( gbSizer2, 0, wxALL|wxEXPAND, 5 ); - wxStaticBoxSizer* sbcircPadNumberingSizer; - sbcircPadNumberingSizer = new wxStaticBoxSizer( new wxStaticBox( m_circularPanel, wxID_ANY, _("Pad Numbering Options") ), wxVERTICAL ); + m_circPadNumberingSizer = new wxStaticBoxSizer( new wxStaticBox( m_circularPanel, wxID_ANY, _("Pad Numbering Options") ), wxVERTICAL ); wxString m_rbCircStartNumberingOptChoices[] = { _("Use first free number"), _("From start value") }; int m_rbCircStartNumberingOptNChoices = sizeof( m_rbCircStartNumberingOptChoices ) / sizeof( wxString ); - m_rbCircStartNumberingOpt = new wxRadioBox( sbcircPadNumberingSizer->GetStaticBox(), wxID_ANY, _("Initial pad number"), wxDefaultPosition, wxDefaultSize, m_rbCircStartNumberingOptNChoices, m_rbCircStartNumberingOptChoices, 1, wxRA_SPECIFY_COLS ); + m_rbCircStartNumberingOpt = new wxRadioBox( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("Initial pad number"), wxDefaultPosition, wxDefaultSize, m_rbCircStartNumberingOptNChoices, m_rbCircStartNumberingOptChoices, 1, wxRA_SPECIFY_COLS ); m_rbCircStartNumberingOpt->SetSelection( 0 ); - sbcircPadNumberingSizer->Add( m_rbCircStartNumberingOpt, 0, wxALL|wxEXPAND, 5 ); + m_circPadNumberingSizer->Add( m_rbCircStartNumberingOpt, 0, wxALL|wxEXPAND, 5 ); wxBoxSizer* bSizer7; bSizer7 = new wxBoxSizer( wxHORIZONTAL ); - m_labelCircNumStart = new wxStaticText( sbcircPadNumberingSizer->GetStaticBox(), wxID_ANY, _("Pad numbering start value:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_labelCircNumStart = new wxStaticText( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("Pad numbering start value:"), wxDefaultPosition, wxDefaultSize, 0 ); m_labelCircNumStart->Wrap( -1 ); bSizer7->Add( m_labelCircNumStart, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_entryCircNumberingStart = new wxTextCtrl( sbcircPadNumberingSizer->GetStaticBox(), wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 ); + m_entryCircNumberingStart = new wxTextCtrl( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer7->Add( m_entryCircNumberingStart, 1, wxALL, 5 ); - sbcircPadNumberingSizer->Add( bSizer7, 0, wxEXPAND, 5 ); + m_circPadNumberingSizer->Add( bSizer7, 0, wxEXPAND, 5 ); - bSizer4->Add( sbcircPadNumberingSizer, 1, wxEXPAND|wxALL, 5 ); + bSizer4->Add( m_circPadNumberingSizer, 1, wxEXPAND|wxALL, 5 ); m_circularPanel->SetSizer( bSizer4 ); diff --git a/pcbnew/dialogs/dialog_create_array_base.fbp b/pcbnew/dialogs/dialog_create_array_base.fbp index f6eb1cc67e..b4af4d44b9 100644 --- a/pcbnew/dialogs/dialog_create_array_base.fbp +++ b/pcbnew/dialogs/dialog_create_array_base.fbp @@ -1978,9 +1978,9 @@ 0 - bSizer3 + m_gridPadNumberingSizer wxVERTICAL - none + protected 5 wxALL|wxEXPAND @@ -4397,10 +4397,9 @@ wxID_ANY Pad Numbering Options - sbcircPadNumberingSizer + m_circPadNumberingSizer wxVERTICAL - 1 - none + protected 5 diff --git a/pcbnew/dialogs/dialog_create_array_base.h b/pcbnew/dialogs/dialog_create_array_base.h index df1f9903bc..0e11e36291 100644 --- a/pcbnew/dialogs/dialog_create_array_base.h +++ b/pcbnew/dialogs/dialog_create_array_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Jan 1 2016) +// C++ code generated with wxFormBuilder (version Mar 9 2016) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -68,6 +68,7 @@ class DIALOG_CREATE_ARRAY_BASE : public DIALOG_SHIM wxStaticText* m_labelStagger; wxTextCtrl* m_entryStagger; wxRadioBox* m_radioBoxGridStaggerType; + wxBoxSizer* m_gridPadNumberingSizer; wxRadioBox* m_radioBoxGridNumberingAxis; wxCheckBox* m_checkBoxGridReverseNumbering; wxRadioBox* m_rbGridStartNumberingOpt; @@ -95,6 +96,7 @@ class DIALOG_CREATE_ARRAY_BASE : public DIALOG_SHIM wxTextCtrl* m_entryCircCount; wxStaticText* m_labelCircRotate; wxCheckBox* m_entryRotateItemsCb; + wxStaticBoxSizer* m_circPadNumberingSizer; wxRadioBox* m_rbCircStartNumberingOpt; wxStaticText* m_labelCircNumStart; wxTextCtrl* m_entryCircNumberingStart; diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp index c46974492a..84be12666c 100644 --- a/pcbnew/edit.cpp +++ b/pcbnew/edit.cpp @@ -53,9 +53,9 @@ #include #include #include +#include #include -#include #include #include @@ -1599,83 +1599,56 @@ void PCB_BASE_EDIT_FRAME::duplicateItem( BOARD_ITEM* aItem, bool aIncrement ) } +class LEGACY_ARRAY_CREATOR: public ARRAY_CREATOR +{ +public: + + LEGACY_ARRAY_CREATOR( PCB_BASE_EDIT_FRAME& editFrame ): + ARRAY_CREATOR( editFrame ), + m_item( m_parent.GetScreen()->GetCurItem() ) + {} + +private: + + int getNumberOfItemsToArray() const //override + { + // only handle single items + return (m_item != NULL) ? 1 : 0; + } + + BOARD_ITEM* getNthItemToArray( int n ) const //override + { + wxASSERT_MSG( n == 0, "Legacy array tool can only handle a single item" ); + return m_item; + } + + BOARD* getBoard() const //override + { + return m_parent.GetBoard(); + } + + MODULE* getModule() const //override + { + return dynamic_cast( m_item->GetParent() ); + } + + wxPoint getRotationCentre() const //override + { + return m_item->GetCenter(); + } + + void finalise() // override + { + m_parent.GetCanvas()->Refresh(); + } + + BOARD_ITEM* m_item; // only have the one +}; + + void PCB_BASE_EDIT_FRAME::createArray() { - BOARD_ITEM* item = GetScreen()->GetCurItem(); + LEGACY_ARRAY_CREATOR array_creator( *this ); - if( !item ) - return; - - // Note: original item is no more modified. - - bool editingModule = NULL != dynamic_cast( this ); - - BOARD* board = GetBoard(); - - // Remember this is valid and used only in the module editor. - // in board editor, the parent of items is usually the board. - MODULE* module = static_cast( item->GetParent() ); - - DIALOG_CREATE_ARRAY::ARRAY_OPTIONS* array_opts = NULL; - - const wxPoint rotPoint = item->GetCenter(); - - DIALOG_CREATE_ARRAY dialog( this, rotPoint, &array_opts ); - int ret = dialog.ShowModal(); - - if( ret == wxID_OK && array_opts != NULL ) - { - PICKED_ITEMS_LIST newItemsList; - - if( item->Type() == PCB_PAD_T && !editingModule ) - { - // If it is not the module editor, then duplicate the parent module instead - item = static_cast( item )->GetParent(); - } - - if( editingModule ) - { - // modedit saves everything upfront - SaveCopyInUndoList( board->m_Modules, UR_MODEDIT ); - } - - #define INCREMENT_REF false - #define INCREMENT_PADNUMBER true - - // The first item in list is the original item. We do not modify it - for( int ptN = 1; ptN < array_opts->GetArraySize(); ptN++ ) - { - BOARD_ITEM* new_item; - - if( editingModule ) - new_item = module->DuplicateAndAddItem( item, INCREMENT_PADNUMBER ); - else - new_item = board->DuplicateAndAddItem( item, INCREMENT_REF ); - - if( new_item ) - { - array_opts->TransformItem( ptN, new_item, rotPoint ); - newItemsList.PushItem( new_item ); // For undo list - } - - if( !new_item || !array_opts->ShouldRenumberItems() ) - continue; - - // Renumber pads. Only new pad number renumbering has meaning, - // in the footprint editor. - if( new_item->Type() == PCB_PAD_T ) - { - const wxString padName = array_opts->GetItemNumber( ptN ); - static_cast( new_item )->SetPadName( padName ); - } - } - - if( !editingModule ) - { - // pcbnew saves the new items like this - SaveCopyInUndoList( newItemsList, UR_NEW ); - } - - m_canvas->Refresh(); - } + array_creator.Invoke(); } diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index 02bbc78a2a..19f397e6a6 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -51,7 +52,6 @@ #include -#include #include #include @@ -794,7 +794,81 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent ) decUndoInhibit(); return 0; -} +}; + + +class GAL_ARRAY_CREATOR: public ARRAY_CREATOR +{ +public: + + GAL_ARRAY_CREATOR( PCB_BASE_FRAME& editFrame, bool editModules, + RN_DATA* ratsnest, + const SELECTION& selection ): + ARRAY_CREATOR( editFrame ), + m_editModules( editModules ), + m_ratsnest( ratsnest ), + m_selection( selection ) + {} + +private: + + int getNumberOfItemsToArray() const //override + { + // only handle single items + return m_selection.Size(); + } + + BOARD_ITEM* getNthItemToArray( int n ) const //override + { + return m_selection.Item( n ); + } + + BOARD* getBoard() const //override + { + return m_parent.GetBoard(); + } + + MODULE* getModule() const //override + { + // Remember this is valid and used only in the module editor. + // in board editor, the parent of items is usually the board. + return m_editModules ? m_parent.GetBoard()->m_Modules.GetFirst() : NULL; + } + + wxPoint getRotationCentre() const //override + { + const VECTOR2I rp = m_selection.GetCenter(); + return wxPoint( rp.x, rp.y ); + } + + void prePushAction( BOARD_ITEM* new_item ) // override + { + m_parent.GetToolManager()->RunAction( COMMON_ACTIONS::unselectItem, + true, new_item ); + } + + void postPushAction( BOARD_ITEM* new_item ) //override + { + KIGFX::VIEW* view = m_parent.GetToolManager()->GetView(); + if( new_item->Type() == PCB_MODULE_T) + { + static_cast( new_item )->RunOnChildren( + boost::bind( &KIGFX::VIEW::Add, view, _1 ) ); + } + + m_parent.GetGalCanvas()->GetView()->Add( new_item ); + m_ratsnest->Update( new_item ); + } + + void finalise() // override + { + m_ratsnest->Recalculate(); + } + + bool m_editModules; + RN_DATA* m_ratsnest; + const SELECTION& m_selection; +}; int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent ) @@ -803,113 +877,19 @@ int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent ) SELECTION_TOOL* selTool = m_toolMgr->GetTool(); const SELECTION& selection = selTool->GetSelection(); - // Be sure that there is at least one item that we can modify - if( !hoverSelection( selection ) ) - return 0; + // pick up items under the cursor if needed + hoverSelection( selection ); // we have a selection to work on now, so start the tool process PCB_BASE_FRAME* editFrame = getEditFrame(); editFrame->OnModify(); - if( m_editModules ) - { - // Module editors do their undo point upfront for the whole module - editFrame->SaveCopyInUndoList( editFrame->GetBoard()->m_Modules, UR_MODEDIT ); - } + GAL_ARRAY_CREATOR array_creator( *editFrame, m_editModules, + getModel()->GetRatsnest(), + selection ); - DIALOG_CREATE_ARRAY::ARRAY_OPTIONS* array_opts = NULL; - - VECTOR2I rp = selection.GetCenter(); - const wxPoint rotPoint( rp.x, rp.y ); - - DIALOG_CREATE_ARRAY dialog( editFrame, rotPoint, &array_opts ); - int ret = dialog.ShowModal(); - - if( ret == wxID_OK && array_opts != NULL ) - { - PICKED_ITEMS_LIST newItemList; - - for( int i = 0; i < selection.Size(); ++i ) - { - BOARD_ITEM* item = selection.Item( i ); - - if( !item ) - continue; - - // iterate across the array, laying out the item at the - // correct position - const unsigned nPoints = array_opts->GetArraySize(); - - // The first item in list is the original item. We do not modify it - for( unsigned ptN = 1; ptN < nPoints; ++ptN ) - { - BOARD_ITEM* newItem = NULL; - - // Some items cannot be duplicated - // i.e. the ref and value fields of a footprint or zones - // therefore newItem can be null - - #define INCREMENT_REF false - #define INCREMENT_PADNUMBER true - - if( m_editModules ) - newItem = editFrame->GetBoard()->m_Modules->DuplicateAndAddItem( - item, INCREMENT_PADNUMBER ); - else - { -#if 0 - // @TODO: see if we allow zone duplication here - // Duplicate zones is especially tricky (overlaping zones must be merged) - // so zones are not duplicated - if( item->Type() == PCB_ZONE_AREA_T ) - newItem = NULL; - else -#endif - newItem = editFrame->GetBoard()->DuplicateAndAddItem( - item, INCREMENT_REF ); - // @TODO: we should merge zones. This is a bit tricky, because - // the undo command needs saving old area, if it is merged. - } - - if( newItem ) - { - array_opts->TransformItem( ptN, newItem, rotPoint ); - - m_toolMgr->RunAction( COMMON_ACTIONS::unselectItem, true, newItem ); - - newItemList.PushItem( newItem ); - - if( newItem->Type() == PCB_MODULE_T) - { - static_cast( newItem )->RunOnChildren( boost::bind( &KIGFX::VIEW::Add, - getView(), _1 ) ); - } - - editFrame->GetGalCanvas()->GetView()->Add( newItem ); - getModel()->GetRatsnest()->Update( newItem ); - } - - // Only renumbering pads has meaning: - if( newItem && array_opts->ShouldRenumberItems() ) - { - if( newItem->Type() == PCB_PAD_T ) - { - const wxString padName = array_opts->GetItemNumber( ptN ); - static_cast( newItem )->SetPadName( padName ); - } - } - } - } - - if( !m_editModules ) - { - // Add all items as a single undo point for PCB editors - editFrame->SaveCopyInUndoList( newItemList, UR_NEW ); - } - } - - getModel()->GetRatsnest()->Recalculate(); + array_creator.Invoke(); return 0; }