2020-10-08 22:59:16 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Jon Evans <jon@craftyjon.com>
|
2021-07-16 20:13:26 +00:00
|
|
|
* Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
2020-10-08 22:59:16 +00:00
|
|
|
*
|
|
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2020-10-14 01:06:53 +00:00
|
|
|
#include <eda_item.h>
|
2020-10-08 22:59:16 +00:00
|
|
|
#include <bitmaps.h>
|
|
|
|
#include <class_draw_panel_gal.h>
|
|
|
|
#include <dialogs/dialog_layers_select_to_pcb.h>
|
|
|
|
#include <gestfich.h>
|
|
|
|
#include <gerber_file_image.h>
|
|
|
|
#include <gerbview_id.h>
|
|
|
|
#include "gerbview_inspection_tool.h"
|
|
|
|
#include "gerbview_actions.h"
|
|
|
|
#include <painter.h>
|
2021-06-06 22:56:20 +00:00
|
|
|
#include <pgm_base.h>
|
2020-10-08 22:59:16 +00:00
|
|
|
#include <preview_items/ruler_item.h>
|
|
|
|
#include <preview_items/selection_area.h>
|
|
|
|
#include <tool/tool_event.h>
|
|
|
|
#include <tool/tool_manager.h>
|
|
|
|
#include <view/view.h>
|
|
|
|
#include <view/view_controls.h>
|
|
|
|
#include <view/view_group.h>
|
2021-05-01 07:50:29 +00:00
|
|
|
#include <wx/msgdlg.h>
|
|
|
|
#include <wx/textdlg.h>
|
|
|
|
#include <wx/choicdlg.h>
|
2020-10-08 22:59:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
GERBVIEW_INSPECTION_TOOL::GERBVIEW_INSPECTION_TOOL() :
|
|
|
|
TOOL_INTERACTIVE( "gerbview.Inspection" ),
|
|
|
|
m_frame( nullptr )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GERBVIEW_INSPECTION_TOOL::~GERBVIEW_INSPECTION_TOOL()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool GERBVIEW_INSPECTION_TOOL::Init()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GERBVIEW_INSPECTION_TOOL::Reset( RESET_REASON aReason )
|
|
|
|
{
|
|
|
|
m_frame = getEditFrame<GERBVIEW_FRAME>();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int GERBVIEW_INSPECTION_TOOL::ShowDCodes( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
int ii, jj;
|
|
|
|
wxString Line;
|
|
|
|
wxArrayString list;
|
|
|
|
int curr_layer = m_frame->GetActiveLayer();
|
|
|
|
|
|
|
|
double scale = 1.0;
|
|
|
|
wxString units;
|
|
|
|
|
|
|
|
switch( m_frame->GetUserUnits() )
|
|
|
|
{
|
2021-07-16 20:13:26 +00:00
|
|
|
case EDA_UNITS::MILLIMETRES:
|
2022-09-17 00:45:14 +00:00
|
|
|
scale = gerbIUScale.IU_PER_MM;
|
2022-02-05 19:31:22 +00:00
|
|
|
units = wxT( "mm" );
|
2021-07-16 20:13:26 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EDA_UNITS::INCHES:
|
2022-09-16 23:25:07 +00:00
|
|
|
scale = gerbIUScale.IU_PER_MILS * 1000;
|
2022-02-05 19:31:22 +00:00
|
|
|
units = wxT( "in" );
|
2021-07-16 20:13:26 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EDA_UNITS::MILS:
|
2022-09-16 23:25:07 +00:00
|
|
|
scale = gerbIUScale.IU_PER_MILS;
|
2022-02-05 19:31:22 +00:00
|
|
|
units = wxT( "mil" );
|
2021-07-16 20:13:26 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2022-02-05 19:31:22 +00:00
|
|
|
wxASSERT_MSG( false, wxT( "Invalid units" ) );
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for( unsigned int layer = 0; layer < m_frame->ImagesMaxCount(); ++layer )
|
|
|
|
{
|
|
|
|
GERBER_FILE_IMAGE* gerber = m_frame->GetGbrImage( layer );
|
|
|
|
|
|
|
|
if( !gerber )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( gerber->GetDcodesCount() == 0 )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( curr_layer == static_cast<int>( layer ) )
|
|
|
|
Line.Printf( wxT( "*** Active layer (%2.2d) ***" ), layer + 1 );
|
|
|
|
else
|
|
|
|
Line.Printf( wxT( "*** layer %2.2d ***" ), layer + 1 );
|
|
|
|
|
|
|
|
list.Add( Line );
|
|
|
|
|
|
|
|
for( ii = 0, jj = 1; ii < TOOLS_MAX_COUNT; ii++ )
|
|
|
|
{
|
|
|
|
D_CODE* pt_D_code = gerber->GetDCODE( ii + FIRST_DCODE );
|
|
|
|
|
2021-07-16 20:13:26 +00:00
|
|
|
if( pt_D_code == nullptr )
|
2020-10-08 22:59:16 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if( !pt_D_code->m_InUse && !pt_D_code->m_Defined )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Line.Printf( wxT( "tool %2.2d: D%2.2d V %.4f %s H %.4f %s %s attribute '%s'" ),
|
|
|
|
jj,
|
|
|
|
pt_D_code->m_Num_Dcode,
|
|
|
|
pt_D_code->m_Size.y / scale, units,
|
|
|
|
pt_D_code->m_Size.x / scale, units,
|
2023-02-10 10:18:09 +00:00
|
|
|
D_CODE::ShowApertureType( pt_D_code->m_ApertType ),
|
2020-10-16 00:39:55 +00:00
|
|
|
pt_D_code->m_AperFunction.IsEmpty()? wxT( "none" ) : pt_D_code->m_AperFunction
|
2020-10-08 22:59:16 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if( !pt_D_code->m_Defined )
|
|
|
|
Line += wxT( " (not defined)" );
|
|
|
|
|
|
|
|
if( pt_D_code->m_InUse )
|
|
|
|
Line += wxT( " (in use)" );
|
|
|
|
|
|
|
|
list.Add( Line );
|
|
|
|
jj++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-16 20:13:26 +00:00
|
|
|
wxSingleChoiceDialog dlg( m_frame, wxEmptyString, _( "D Codes" ), list, (void**) nullptr,
|
2020-10-08 22:59:16 +00:00
|
|
|
wxCHOICEDLG_STYLE & ~wxCANCEL );
|
|
|
|
|
|
|
|
dlg.ShowModal();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int GERBVIEW_INSPECTION_TOOL::ShowSource( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
int layer = m_frame->GetActiveLayer();
|
|
|
|
GERBER_FILE_IMAGE* gerber_layer = m_frame->GetGbrImage( layer );
|
|
|
|
|
|
|
|
if( gerber_layer )
|
|
|
|
{
|
2021-09-19 13:45:27 +00:00
|
|
|
wxString editorname = Pgm().GetTextEditor();
|
2020-10-08 22:59:16 +00:00
|
|
|
|
|
|
|
if( !editorname.IsEmpty() )
|
|
|
|
{
|
|
|
|
wxFileName fn( gerber_layer->m_FileName );
|
|
|
|
|
|
|
|
// Call the editor only if the Gerber/drill source file is available.
|
|
|
|
// This is not always the case, because it can be a temporary file
|
|
|
|
// if it comes from a zip archive.
|
|
|
|
if( !fn.FileExists() )
|
|
|
|
{
|
|
|
|
wxString msg;
|
2021-06-16 22:35:00 +00:00
|
|
|
msg.Printf( _( "Source file '%s' not found." ), fn.GetFullPath() );
|
2020-10-08 22:59:16 +00:00
|
|
|
wxMessageBox( msg );
|
|
|
|
}
|
|
|
|
else
|
2020-12-25 23:37:17 +00:00
|
|
|
{
|
2021-10-30 10:56:32 +00:00
|
|
|
ExecuteFile( editorname, fn.GetFullPath() );
|
2020-12-25 23:37:17 +00:00
|
|
|
}
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
|
|
|
else
|
2020-12-25 23:37:17 +00:00
|
|
|
{
|
2021-09-19 13:45:27 +00:00
|
|
|
wxMessageBox( _( "No text editor selected in KiCad. Please choose one." ) );
|
2020-12-25 23:37:17 +00:00
|
|
|
}
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wxString msg;
|
2021-06-16 22:35:00 +00:00
|
|
|
msg.Printf( _( "No file loaded on the active layer %d." ), layer + 1 );
|
2020-10-08 22:59:16 +00:00
|
|
|
wxMessageBox( msg );
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-13 12:23:10 +00:00
|
|
|
using KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER;
|
2020-10-08 22:59:16 +00:00
|
|
|
|
|
|
|
|
2021-09-13 12:23:10 +00:00
|
|
|
int GERBVIEW_INSPECTION_TOOL::MeasureTool( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
KIGFX::VIEW_CONTROLS& controls = *getViewControls();
|
|
|
|
bool originSet = false;
|
|
|
|
TWO_POINT_GEOMETRY_MANAGER twoPtMgr;
|
2020-10-08 22:59:16 +00:00
|
|
|
EDA_UNITS units = m_frame->GetUserUnits();
|
2022-09-16 04:38:10 +00:00
|
|
|
KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, gerbIUScale, units, false, false );
|
2020-10-08 22:59:16 +00:00
|
|
|
|
2022-09-14 17:31:56 +00:00
|
|
|
m_frame->PushTool( aEvent );
|
2020-10-08 22:59:16 +00:00
|
|
|
|
|
|
|
auto setCursor =
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
|
|
|
|
};
|
|
|
|
|
2021-09-13 12:23:10 +00:00
|
|
|
auto cleanup =
|
|
|
|
[&] ()
|
|
|
|
{
|
|
|
|
getView()->SetVisible( &ruler, false );
|
|
|
|
controls.SetAutoPan( false );
|
|
|
|
controls.CaptureCursor( false );
|
|
|
|
originSet = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
Activate();
|
|
|
|
// Must be done after Activate() so that it gets set into the correct context
|
|
|
|
controls.ShowCursor( true );
|
2020-10-08 22:59:16 +00:00
|
|
|
// Set initial cursor
|
|
|
|
setCursor();
|
|
|
|
|
2021-09-13 12:23:10 +00:00
|
|
|
getView()->Add( &ruler );
|
|
|
|
getView()->SetVisible( &ruler, false );
|
|
|
|
|
2020-10-08 22:59:16 +00:00
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
|
|
{
|
|
|
|
setCursor();
|
|
|
|
const VECTOR2I cursorPos = controls.GetCursorPosition();
|
|
|
|
|
|
|
|
if( evt->IsCancelInteractive() )
|
|
|
|
{
|
|
|
|
if( originSet )
|
|
|
|
{
|
2021-09-13 12:23:10 +00:00
|
|
|
cleanup();
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-14 17:31:56 +00:00
|
|
|
m_frame->PopTool( aEvent );
|
2020-10-08 22:59:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( evt->IsActivate() )
|
|
|
|
{
|
|
|
|
if( originSet )
|
2021-09-13 12:23:10 +00:00
|
|
|
cleanup();
|
2020-10-08 22:59:16 +00:00
|
|
|
|
|
|
|
if( evt->IsMoveTool() )
|
|
|
|
{
|
|
|
|
// leave ourselves on the stack so we come back after the move
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-14 17:31:56 +00:00
|
|
|
m_frame->PopTool( aEvent );
|
2020-10-08 22:59:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
|
|
|
|
{
|
2021-07-16 20:13:26 +00:00
|
|
|
// click or drag starts
|
2020-10-08 22:59:16 +00:00
|
|
|
twoPtMgr.SetOrigin( cursorPos );
|
|
|
|
twoPtMgr.SetEnd( cursorPos );
|
|
|
|
|
|
|
|
controls.CaptureCursor( true );
|
|
|
|
controls.SetAutoPan( true );
|
|
|
|
|
|
|
|
originSet = true;
|
|
|
|
}
|
|
|
|
else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
|
|
|
|
{
|
2021-07-16 20:13:26 +00:00
|
|
|
// second click or mouse up after drag ends
|
2020-10-08 22:59:16 +00:00
|
|
|
originSet = false;
|
|
|
|
|
|
|
|
controls.SetAutoPan( false );
|
|
|
|
controls.CaptureCursor( false );
|
|
|
|
}
|
|
|
|
else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
|
|
|
|
{
|
2021-07-16 20:13:26 +00:00
|
|
|
// move or drag when origin set updates rules
|
2021-05-09 19:17:01 +00:00
|
|
|
twoPtMgr.SetAngleSnap( evt->Modifier( MD_SHIFT ) );
|
2020-10-08 22:59:16 +00:00
|
|
|
twoPtMgr.SetEnd( cursorPos );
|
|
|
|
|
2021-09-13 12:23:10 +00:00
|
|
|
getView()->SetVisible( &ruler, true );
|
|
|
|
getView()->Update( &ruler, KIGFX::GEOMETRY );
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
2020-11-04 14:08:06 +00:00
|
|
|
else if( evt->IsAction( &ACTIONS::updateUnits ) )
|
2020-10-08 22:59:16 +00:00
|
|
|
{
|
|
|
|
if( m_frame->GetUserUnits() != units )
|
|
|
|
{
|
|
|
|
units = m_frame->GetUserUnits();
|
|
|
|
ruler.SwitchUnits( units );
|
2021-09-13 12:23:10 +00:00
|
|
|
getView()->Update( &ruler, KIGFX::GEOMETRY );
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
2020-11-04 14:08:06 +00:00
|
|
|
evt->SetPassEvent();
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
|
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
|
|
{
|
|
|
|
m_menu.ShowContextMenu( m_frame->GetCurrentSelection() );
|
|
|
|
}
|
|
|
|
else
|
2020-11-04 14:08:06 +00:00
|
|
|
{
|
2020-10-08 22:59:16 +00:00
|
|
|
evt->SetPassEvent();
|
2020-11-04 14:08:06 +00:00
|
|
|
}
|
2020-10-08 22:59:16 +00:00
|
|
|
}
|
|
|
|
|
2021-09-13 12:23:10 +00:00
|
|
|
getView()->SetVisible( &ruler, false );
|
|
|
|
getView()->Remove( &ruler );
|
2020-11-19 20:08:58 +00:00
|
|
|
|
|
|
|
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
|
2020-10-08 22:59:16 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GERBVIEW_INSPECTION_TOOL::setTransitions()
|
|
|
|
{
|
|
|
|
Go( &GERBVIEW_INSPECTION_TOOL::ShowSource, GERBVIEW_ACTIONS::showSource.MakeEvent() );
|
|
|
|
Go( &GERBVIEW_INSPECTION_TOOL::ShowDCodes, GERBVIEW_ACTIONS::showDCodes.MakeEvent() );
|
|
|
|
Go( &GERBVIEW_INSPECTION_TOOL::MeasureTool, ACTIONS::measureTool.MakeEvent() );
|
|
|
|
}
|