kicad-source/pcbnew/tools/module_tools.cpp
Lorenzo Marcantonio 0da38e5b62 More work on module texts, and especially on the collector
The members SetIgnoreMTextsOnCopper, SetIgnoreMTextsOnCmp,
SetIgnoreModulesOnCu, SetIgnoreModulesOnCmp in the collector guide are
renamed to the clearer SetIgnoreMTextsOnBack, SetIgnoreMTextsOnFront,
SetIgnoreModulesOnBack, SetIgnoreModulesOnFront

Modified the collector and drawing behaviour for text in modules as
discussed in the mailing list.

* Now module text on layers different than Silk should work! *

Still need UI support for setting the layer in the module editor.
2014-09-11 18:35:19 +02:00

614 lines
19 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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 "module_tools.h"
#include "selection_tool.h"
#include "common_actions.h"
#include <class_draw_panel_gal.h>
#include <view/view_controls.h>
#include <view/view_group.h>
#include <pcb_painter.h>
#include <kicad_plugin.h>
#include <pcbnew_id.h>
#include <collectors.h>
#include <confirm.h>
#include <dialogs/dialog_enum_pads.h>
#include <wxPcbStruct.h>
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <wx/defs.h>
MODULE_TOOLS::MODULE_TOOLS() :
TOOL_INTERACTIVE( "pcbnew.ModuleEditor" )
{
}
void MODULE_TOOLS::Reset( RESET_REASON aReason )
{
// Init variables used by every drawing tool
m_view = getView();
m_controls = getViewControls();
m_board = getModel<BOARD>();
m_frame = getEditFrame<PCB_EDIT_FRAME>();
}
bool MODULE_TOOLS::Init()
{
// Find the selection tool, so they can cooperate
SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
if( !selectionTool )
{
DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
return false;
}
selectionTool->AddMenuItem( COMMON_ACTIONS::enumeratePads );
setTransitions();
return true;
}
static wxString getNextPadName( MODULE* aModule )
{
std::set<int> usedNumbers;
// Create a set of used pad numbers
for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
{
wxString padName = pad->GetPadName();
int padNumber = 0;
int base = 1;
// Trim and extract the trailing numeric part
while( padName.Len() && padName.Last() >= '0' && padName.Last() <= '9' )
{
padNumber += ( padName.Last() - '0' ) * base;
padName.RemoveLast();
base *= 10;
}
usedNumbers.insert( padNumber );
}
int candidate = *usedNumbers.begin();
// Look for a gap in pad numbering
for( std::set<int>::iterator it = usedNumbers.begin(),
itEnd = usedNumbers.end(); it != itEnd; ++it )
{
if( *it - candidate > 1 )
break;
candidate = *it;
}
return wxString::Format( wxT( "%i" ), ++candidate );
}
int MODULE_TOOLS::PlacePad( TOOL_EVENT& aEvent )
{
m_frame->SetToolID( ID_MODEDIT_PAD_TOOL, wxCURSOR_PENCIL, _( "Add pads" ) );
MODULE* module = m_board->m_Modules;
assert( module );
D_PAD* pad = new D_PAD( module );
m_frame->Import_Pad_Settings( pad, false ); // use the global settings for pad
VECTOR2I cursorPos = m_controls->GetCursorPosition();
pad->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
// Add a VIEW_GROUP that serves as a preview for the new item
KIGFX::VIEW_GROUP preview( m_view );
preview.Add( pad );
m_view->Add( &preview );
m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
m_controls->ShowCursor( true );
m_controls->SetSnapping( true );
Activate();
// Main loop: keep receiving events
while( OPT_TOOL_EVENT evt = Wait() )
{
cursorPos = m_controls->GetCursorPosition();
if( evt->IsMotion() )
{
pad->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
preview.ViewUpdate();
}
else if( evt->Category() == TC_COMMAND )
{
if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
{
pad->Rotate( pad->GetPosition(), m_frame->GetRotationAngle() );
preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
{
pad->Flip( pad->GetPosition() );
preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
else if( evt->IsCancel() || evt->IsActivate() )
{
preview.Clear();
delete pad;
break;
}
}
else if( evt->IsClick( BUT_LEFT ) )
{
m_frame->OnModify();
m_frame->SaveCopyInUndoList( module, UR_MODEDIT );
m_board->m_Status_Pcb = 0; // I have no clue why, but it is done in the legacy view
module->SetLastEditTime();
module->Pads().PushBack( pad );
pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
// Set the relative pad position
// ( pad position for module orient, 0, and relative to the module position)
pad->SetLocalCoord();
/* NPTH pads take empty pad number (since they can't be connected),
* other pads get incremented from the last one edited */
wxString padName;
if( pad->GetAttribute() != PAD_HOLE_NOT_PLATED )
padName = getNextPadName( module );
pad->SetPadName( padName );
// Handle the view aspect
preview.Remove( pad );
m_view->Add( pad );
// Start placing next pad
pad = new D_PAD( module );
m_frame->Import_Pad_Settings( pad, false );
pad->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
preview.Add( pad );
}
}
m_controls->ShowCursor( false );
m_controls->SetSnapping( false );
m_controls->SetAutoPan( false );
m_view->Remove( &preview );
setTransitions();
m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
return 0;
}
int MODULE_TOOLS::EnumeratePads( TOOL_EVENT& aEvent )
{
std::list<D_PAD*> pads;
std::set<D_PAD*> allPads;
MODULE* module = m_board->m_Modules;
GENERAL_COLLECTOR collector;
const KICAD_T types[] = { PCB_PAD_T, EOT };
GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
guide.SetIgnoreMTextsMarkedNoShow( true );
guide.SetIgnoreMTextsOnBack( true );
guide.SetIgnoreMTextsOnFront( true );
guide.SetIgnoreModulesVals( true );
guide.SetIgnoreModulesRefs( true );
// Create a set containing all pads (to avoid double adding to a list)
for( D_PAD* p = module->Pads(); p; p = p->Next() )
allPads.insert( p );
DIALOG_ENUM_PADS settingsDlg( m_frame );
if( settingsDlg.ShowModal() == wxID_CANCEL )
{
setTransitions();
return 0;
}
int padNumber = settingsDlg.GetStartNumber();
wxString padPrefix = settingsDlg.GetPrefix();
m_frame->DisplayToolMsg( _( "Hold left mouse button and move cursor over pads to enumerate them" ) );
Activate();
m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
m_controls->ShowCursor( true );
while( OPT_TOOL_EVENT evt = Wait() )
{
if( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
{
// Add pads to the list according to the selection order
VECTOR2I cursorPos = m_controls->GetCursorPosition();
collector.Empty();
collector.Collect( m_board, types, wxPoint( cursorPos.x, cursorPos.y ), guide );
for( int i = 0; i < collector.GetCount(); ++i )
{
if( collector[i]->Type() == PCB_PAD_T )
{
D_PAD* pad = static_cast<D_PAD*>( collector[i] );
std::set<D_PAD*>::iterator it = allPads.find( pad );
// Add the pad to the list, if it was not selected previously..
if( it != allPads.end() )
{
allPads.erase( it );
pads.push_back( pad );
pad->SetSelected();
}
// ..or remove it from the list if it was clicked
else if( evt->IsClick( BUT_LEFT ) )
{
allPads.insert( pad );
pads.remove( pad );
pad->ClearSelected();
}
}
}
}
else if( ( evt->IsKeyPressed() && evt->KeyCode() == WXK_RETURN ) ||
evt->IsDblClick( BUT_LEFT ) )
{
// Accept changes
m_frame->OnModify();
m_frame->SaveCopyInUndoList( module, UR_MODEDIT );
BOOST_FOREACH( D_PAD* pad, pads )
pad->SetPadName( wxString::Format( wxT( "%s%d" ), padPrefix.c_str(), padNumber++ ) );
break;
}
else if( evt->IsCancel() || evt->IsActivate() )
{
break;
}
}
BOOST_FOREACH( D_PAD* pad, pads )
pad->ClearSelected();
m_frame->DisplayToolMsg( wxEmptyString );
m_controls->ShowCursor( false );
setTransitions();
return 0;
}
int MODULE_TOOLS::CopyItems( TOOL_EVENT& aEvent )
{
const SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
Activate();
m_controls->SetSnapping( true );
m_controls->ShowCursor( true );
m_controls->SetAutoPan( true );
m_frame->DisplayToolMsg( _( "Select reference point" ) );
bool cancelled = false;
VECTOR2I cursorPos = m_controls->GetCursorPosition();
while( OPT_TOOL_EVENT evt = Wait() )
{
if( evt->IsMotion() )
{
cursorPos = m_controls->GetCursorPosition();
}
else if( evt->IsClick( BUT_LEFT ) )
{
break;
}
else if( evt->IsCancel() || evt->IsActivate() )
{
cancelled = true;
break;
}
}
if( !cancelled )
{
PCB_IO io( CTL_FOR_CLIPBOARD );
// Create a temporary module that contains selected items to ease serialization
MODULE module( m_board );
for( int i = 0; i < selection.Size(); ++i )
{
BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( selection.Item<BOARD_ITEM>( i )->Clone() );
// Do not add reference/value - convert them to the common type
if( TEXTE_MODULE* text = dyn_cast<TEXTE_MODULE*>( clone ) )
text->SetType( TEXTE_MODULE::TEXT_is_DIVERS );
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() -
wxPoint( cursorPos.x, cursorPos.y );
module.MoveAnchorPosition( moveVector );
io.Format( &module, 0 );
std::string data = io.GetStringOutput( true );
m_toolMgr->SaveClipboard( data );
}
m_frame->DisplayToolMsg( wxString::Format( _( "Copied %d item(s)" ), selection.Size() ) );
m_controls->SetSnapping( false );
m_controls->ShowCursor( false );
m_controls->SetAutoPan( false );
setTransitions();
return 0;
}
int MODULE_TOOLS::PasteItems( TOOL_EVENT& aEvent )
{
// Parse clipboard
PCB_IO io( CTL_FOR_CLIPBOARD );
MODULE* currentModule = m_board->m_Modules;
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<MODULE*>( item );
}
catch( ... )
{
m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) );
setTransitions();
return 0;
}
// Placement tool part
VECTOR2I cursorPos = m_controls->GetCursorPosition();
// Add a VIEW_GROUP that serves as a preview for the new item
KIGFX::VIEW_GROUP preview( m_view );
pastedModule->SetParent( m_board );
pastedModule->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
pastedModule->RunOnChildren( boost::bind( &KIGFX::VIEW_GROUP::Add, boost::ref( preview ), _1 ) );
preview.Add( pastedModule );
m_view->Add( &preview );
m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
m_controls->ShowCursor( true );
m_controls->SetSnapping( true );
m_controls->SetAutoPan( true );
Activate();
// Main loop: keep receiving events
while( OPT_TOOL_EVENT evt = Wait() )
{
cursorPos = m_controls->GetCursorPosition();
if( evt->IsMotion() )
{
pastedModule->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
preview.ViewUpdate();
}
else if( evt->Category() == TC_COMMAND )
{
if( evt->IsAction( &COMMON_ACTIONS::rotate ) )
{
pastedModule->Rotate( pastedModule->GetPosition(), m_frame->GetRotationAngle() );
preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
else if( evt->IsAction( &COMMON_ACTIONS::flip ) )
{
pastedModule->Flip( pastedModule->GetPosition() );
preview.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
else if( evt->IsCancel() || evt->IsActivate() )
{
preview.Clear();
break;
}
}
else if( evt->IsClick( BUT_LEFT ) )
{
m_frame->OnModify();
m_frame->SaveCopyInUndoList( currentModule, UR_MODEDIT );
m_board->m_Status_Pcb = 0; // I have no clue why, but it is done in the legacy view
currentModule->SetLastEditTime();
// MODULE::RunOnChildren is infeasible here: we need to create copies of items, do not
// directly modify them
for( D_PAD* pad = pastedModule->Pads(); pad; pad = pad->Next() )
{
D_PAD* clone = static_cast<D_PAD*>( pad->Clone() );
currentModule->Add( clone );
clone->SetLocalCoord();
m_view->Add( clone );
}
for( BOARD_ITEM* drawing = pastedModule->GraphicalItems();
drawing; drawing = drawing->Next() )
{
BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( drawing->Clone() );
if( TEXTE_MODULE* text = dyn_cast<TEXTE_MODULE*>( clone ) )
{
// Do not add reference/value - convert them to the common type
text->SetType( TEXTE_MODULE::TEXT_is_DIVERS );
currentModule->Add( text );
text->SetLocalCoord();
// Whyyyyyyyyyyyyyyyyyyyyyy?! All other items conform to rotation performed
// on its parent module, but texts are so independent..
text->Rotate( text->GetPosition(), pastedModule->GetOrientation() );
}
else if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( clone ) )
{
currentModule->Add( edge );
edge->SetLocalCoord();
}
m_view->Add( clone );
}
preview.Clear();
break;
}
}
delete pastedModule;
m_controls->ShowCursor( false );
m_controls->SetSnapping( false );
m_controls->SetAutoPan( false );
m_view->Remove( &preview );
setTransitions();
return 0;
}
int MODULE_TOOLS::ModuleTextOutlines( TOOL_EVENT& aEvent )
{
KIGFX::PCB_PAINTER* painter =
static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
KIGFX::PCB_RENDER_SETTINGS* settings =
static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
const LAYER_NUM layers[] = { ITEM_GAL_LAYER( MOD_TEXT_BK_VISIBLE ),
ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ),
ITEM_GAL_LAYER( MOD_TEXT_INVISIBLE ),
ITEM_GAL_LAYER( MOD_REFERENCES_VISIBLE ),
ITEM_GAL_LAYER( MOD_VALUES_VISIBLE ) };
bool enable = !settings->GetSketchMode( layers[0] );
BOOST_FOREACH( LAYER_NUM layer, layers )
settings->SetSketchMode( layer, enable );
for( MODULE* module = getModel<BOARD>()->m_Modules; module; module = module->Next() )
{
for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item ->Next() )
{
if( item->Type() == PCB_MODULE_TEXT_T )
item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
module->Reference().ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
module->Value().ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
m_frame->GetGalCanvas()->Refresh();
setTransitions();
return 0;
}
int MODULE_TOOLS::ModuleEdgeOutlines( TOOL_EVENT& aEvent )
{
KIGFX::PCB_PAINTER* painter =
static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
KIGFX::PCB_RENDER_SETTINGS* settings =
static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
const LAYER_ID layers[] = { F_Adhes, B_Adhes, F_Paste, B_Paste,
F_SilkS, B_SilkS, F_Mask, B_Mask,
Dwgs_User, Cmts_User, Eco1_User, Eco2_User, Edge_Cuts };
bool enable = !settings->GetSketchMode( layers[0] );
BOOST_FOREACH( LAYER_NUM layer, layers )
settings->SetSketchMode( layer, enable );
for( MODULE* module = getModel<BOARD>()->m_Modules; module; module = module->Next() )
{
for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item ->Next() )
{
if( item->Type() == PCB_MODULE_EDGE_T )
item->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
}
}
m_frame->GetGalCanvas()->Refresh();
setTransitions();
return 0;
}
void MODULE_TOOLS::setTransitions()
{
Go( &MODULE_TOOLS::PlacePad, COMMON_ACTIONS::placePad.MakeEvent() );
Go( &MODULE_TOOLS::EnumeratePads, COMMON_ACTIONS::enumeratePads.MakeEvent() );
Go( &MODULE_TOOLS::CopyItems, COMMON_ACTIONS::copyItems.MakeEvent() );
Go( &MODULE_TOOLS::PasteItems, COMMON_ACTIONS::pasteItems.MakeEvent() );
Go( &MODULE_TOOLS::ModuleTextOutlines, COMMON_ACTIONS::moduleTextOutlines.MakeEvent() );
Go( &MODULE_TOOLS::ModuleEdgeOutlines, COMMON_ACTIONS::moduleEdgeOutlines.MakeEvent() );
}