/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 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 */ #include "pad_tool.h" #include #include #include #include #include #include #include #include #include #include #include #include "pcb_actions.h" #include "selection_tool.h" #include "pcb_selection_conditions.h" #include "edit_tool.h" // Pad tools TOOL_ACTION PCB_ACTIONS::copyPadSettings( "pcbnew.PadTool.CopyPadSettings", AS_GLOBAL, 0, _( "Copy Pad Properties" ), _( "Copy current pad's properties to the default pad properties" ), copy_pad_settings_xpm ); TOOL_ACTION PCB_ACTIONS::applyPadSettings( "pcbnew.PadTool.ApplyPadSettings", AS_GLOBAL, 0, _( "Apply Pad Properties" ), _( "Copy the default pad properties to the current pad" ), apply_pad_settings_xpm ); TOOL_ACTION PCB_ACTIONS::pushPadSettings( "pcbnew.PadTool.PushPadSettings", AS_GLOBAL, 0, _( "Push Pad Properties..." ), _( "Copy the current pad settings to other pads" ), push_pad_settings_xpm ); class PAD_CONTEXT_MENU : public CONTEXT_MENU { public: using SHOW_FUNCTOR = std::function; PAD_CONTEXT_MENU( bool aEditingFootprint, SHOW_FUNCTOR aHaveGlobalPadSetting ): m_editingFootprint( aEditingFootprint ), m_haveGlobalPadSettings( std::move( aHaveGlobalPadSetting ) ) { SetIcon( pad_xpm ); SetTitle( _( "Pads" ) ); Add( PCB_ACTIONS::copyPadSettings ); Add( PCB_ACTIONS::applyPadSettings ); Add( PCB_ACTIONS::pushPadSettings ); // show modedit-specific items if( m_editingFootprint ) { AppendSeparator(); Add( PCB_ACTIONS::enumeratePads ); } } protected: CONTEXT_MENU* create() const override { return new PAD_CONTEXT_MENU( m_editingFootprint, m_haveGlobalPadSettings ); } private: struct ENABLEMENTS { bool canImport; bool canExport; bool canPush; }; ENABLEMENTS getEnablements( const SELECTION& aSelection ) { using S_C = SELECTION_CONDITIONS; ENABLEMENTS enablements; auto anyPadSel = S_C::HasType( PCB_PAD_T ); auto singlePadSel = S_C::Count( 1 ) && S_C::OnlyType( PCB_PAD_T ); // Apply pads enabled when any pads selected (it applies to each one // individually), plus need a valid global pad setting enablements.canImport = m_haveGlobalPadSettings() && ( anyPadSel )( aSelection ); // Copy pads item enabled only when there is a single pad selected // (otherwise how would we know which one to copy?) enablements.canExport = ( singlePadSel )( aSelection ); // Push pads available when there is a single pad to push from enablements.canPush = ( singlePadSel )( aSelection ); return enablements; } void update() override { auto selTool = getToolManager()->GetTool(); const SELECTION& selection = selTool->GetSelection(); auto enablements = getEnablements( selection ); Enable( getMenuId( PCB_ACTIONS::applyPadSettings ), enablements.canImport ); Enable( getMenuId( PCB_ACTIONS::copyPadSettings ), enablements.canExport ); Enable( getMenuId( PCB_ACTIONS::pushPadSettings ), enablements.canPush ); } bool m_editingFootprint; SHOW_FUNCTOR m_haveGlobalPadSettings; }; PAD_TOOL::PAD_TOOL() : PCB_TOOL( "pcbnew.PadTool" ), m_padCopied( false ) { } PAD_TOOL::~PAD_TOOL() {} void PAD_TOOL::Reset( RESET_REASON aReason ) { m_padCopied = false; } bool PAD_TOOL::haveFootprints() { auto& board = *getModel(); return board.m_Modules.GetCount() > 0; } bool PAD_TOOL::Init() { auto contextMenu = std::make_shared( EditingModules(), [this]() { return m_padCopied; } ); contextMenu->SetTool( this ); SELECTION_TOOL* selTool = m_toolMgr->GetTool(); if( selTool ) { auto& toolMenu = selTool->GetToolMenu(); auto& menu = toolMenu.GetMenu(); toolMenu.AddSubMenu( contextMenu ); auto canShowMenuCond = [this, contextMenu] ( const SELECTION& aSel ) { contextMenu->UpdateAll(); return frame()->GetToolId() == ID_NO_TOOL_SELECTED && haveFootprints() && contextMenu->HasEnabledItems(); }; // show menu when there is a footprint, and the menu has any items auto showCond = canShowMenuCond && ( SELECTION_CONDITIONS::HasType( PCB_PAD_T ) || SELECTION_CONDITIONS::Count( 0 ) ); menu.AddMenu( contextMenu.get(), false, showCond, 1000 ); // we need a separator only when the selection is empty auto separatorCond = canShowMenuCond && SELECTION_CONDITIONS::Count( 0 ); menu.AddSeparator( separatorCond, 1000 ); } return true; } int PAD_TOOL::applyPadSettings( const TOOL_EVENT& aEvent ) { auto& selTool = *m_toolMgr->GetTool(); const auto& selection = selTool.GetSelection(); auto& frame = *getEditFrame(); const D_PAD& masterPad = frame.GetDesignSettings().m_Pad_Master; BOARD_COMMIT commit( &frame ); // for every selected pad, copy global settings for( auto item : selection ) { if( item->Type() == PCB_PAD_T ) { commit.Modify( item ); D_PAD& destPad = static_cast( *item ); destPad.ImportSettingsFromMaster( masterPad ); } } commit.Push( _( "Apply Pad Properties" ) ); m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, true ); frame.Refresh(); return 0; } int PAD_TOOL::copyPadSettings( const TOOL_EVENT& aEvent ) { auto& selTool = *m_toolMgr->GetTool(); const auto& selection = selTool.GetSelection(); auto& frame = *getEditFrame(); D_PAD& masterPad = frame.GetDesignSettings().m_Pad_Master; // can only copy from a single pad if( selection.Size() == 1 ) { auto item = selection[0]; if( item->Type() == PCB_PAD_T ) { const auto& selPad = static_cast( *item ); masterPad.ImportSettingsFromMaster( selPad ); m_padCopied = true; } } return 0; } static void globalChangePadSettings( BOARD& board, const D_PAD& aSrcPad, BOARD_COMMIT& commit, bool aSameFootprints, bool aPadShapeFilter, bool aPadOrientFilter, bool aPadLayerFilter ) { const MODULE* moduleRef = aSrcPad.GetParent(); // If there is no module, we can't make the comparisons to see which // pads to apply the source pad settings to if( moduleRef == nullptr ) { wxLogDebug( "globalChangePadSettings() Error: NULL module" ); return; } double pad_orient = aSrcPad.GetOrientation() - moduleRef->GetOrientation(); for( const MODULE* module = board.m_Modules; module; module = module->Next() ) { if( !aSameFootprints && ( module != moduleRef ) ) continue; if( module->GetFPID() != moduleRef->GetFPID() ) continue; for( D_PAD* pad = module->PadsList(); pad; pad = pad->Next() ) { // Filters changes prohibited. if( aPadShapeFilter && ( pad->GetShape() != aSrcPad.GetShape() ) ) continue; double currpad_orient = pad->GetOrientation() - module->GetOrientation(); if( aPadOrientFilter && ( currpad_orient != pad_orient ) ) continue; if( aPadLayerFilter && ( pad->GetLayerSet() != aSrcPad.GetLayerSet() ) ) continue; if( aPadLayerFilter && ( pad->GetLayerSet() != aSrcPad.GetLayerSet() ) ) continue; commit.Modify( pad ); // Apply source pad settings to this pad pad->ImportSettingsFromMaster( aSrcPad ); } } } int PAD_TOOL::pushPadSettings( const TOOL_EVENT& aEvent ) { auto& selTool = *m_toolMgr->GetTool(); const auto& selection = selTool.GetSelection(); auto& frame = *getEditFrame(); // not const - can be changed in the dialog D_PAD* srcPad = nullptr; // If nothing selected, use the master pad setting, // otherwise, if one pad selected, use the selected pad if( selection.Size() == 0 ) { srcPad = &frame.GetDesignSettings().m_Pad_Master; } else if( selection.Size() == 1 ) { if( selection[0]->Type() == PCB_PAD_T ) { srcPad = static_cast( selection[0] ); } } else { // multiple selected what to do? // maybe master->selection? same as apply multiple? } // no valid selection, nothing to do if( !srcPad ) { return 0; } MODULE* module = srcPad->GetParent(); if( module != nullptr ) { frame.SetMsgPanel( module ); } int dialogRet; { DIALOG_GLOBAL_PADS_EDITION dlg( &frame, srcPad ); dialogRet = dlg.ShowModal(); } // cancel if( dialogRet == -1 ) { return 0; } const bool edit_Same_Modules = (dialogRet == 1); BOARD_COMMIT commit( &frame ); globalChangePadSettings( *getModel(), *srcPad, commit, edit_Same_Modules, DIALOG_GLOBAL_PADS_EDITION::m_Pad_Shape_Filter, DIALOG_GLOBAL_PADS_EDITION::m_Pad_Orient_Filter, DIALOG_GLOBAL_PADS_EDITION::m_Pad_Layer_Filter ); commit.Push( _( "Apply Pad Properties" ) ); m_toolMgr->RunAction( PCB_ACTIONS::selectionModified, true ); frame.Refresh(); return 0; } void PAD_TOOL::setTransitions() { Go( &PAD_TOOL::applyPadSettings, PCB_ACTIONS::applyPadSettings.MakeEvent() ); Go( &PAD_TOOL::copyPadSettings, PCB_ACTIONS::copyPadSettings.MakeEvent() ); Go( &PAD_TOOL::pushPadSettings, PCB_ACTIONS::pushPadSettings.MakeEvent() ); }