1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jon Evans <jon@craftyjon.com>
5  * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #include <eda_item.h>
23 #include <bitmaps.h>
24 #include <class_draw_panel_gal.h>
25 #include <dialogs/dialog_layers_select_to_pcb.h>
26 #include <gestfich.h>
27 #include <gerber_file_image.h>
28 #include <gerbview_id.h>
29 #include "gerbview_inspection_tool.h"
30 #include "gerbview_actions.h"
31 #include <painter.h>
32 #include <pgm_base.h>
33 #include <preview_items/ruler_item.h>
34 #include <preview_items/selection_area.h>
35 #include <tool/tool_event.h>
36 #include <tool/tool_manager.h>
37 #include <view/view.h>
38 #include <view/view_controls.h>
39 #include <view/view_group.h>
40 #include <wx/msgdlg.h>
41 #include <wx/textdlg.h>
42 #include <wx/choicdlg.h>
43 
44 
GERBVIEW_INSPECTION_TOOL()45 GERBVIEW_INSPECTION_TOOL::GERBVIEW_INSPECTION_TOOL() :
46         TOOL_INTERACTIVE( "gerbview.Inspection" ),
47         m_frame( nullptr )
48 {
49 }
50 
51 
~GERBVIEW_INSPECTION_TOOL()52 GERBVIEW_INSPECTION_TOOL::~GERBVIEW_INSPECTION_TOOL()
53 {
54 }
55 
56 
Init()57 bool GERBVIEW_INSPECTION_TOOL::Init()
58 {
59     return true;
60 }
61 
62 
Reset(RESET_REASON aReason)63 void GERBVIEW_INSPECTION_TOOL::Reset( RESET_REASON aReason )
64 {
65     m_frame = getEditFrame<GERBVIEW_FRAME>();
66 }
67 
68 
ShowDCodes(const TOOL_EVENT & aEvent)69 int GERBVIEW_INSPECTION_TOOL::ShowDCodes( const TOOL_EVENT& aEvent )
70 {
71     int             ii, jj;
72     wxString        Line;
73     wxArrayString   list;
74     int             curr_layer = m_frame->GetActiveLayer();
75 
76     double   scale = 1.0;
77     wxString units;
78 
79     switch( m_frame->GetUserUnits() )
80     {
81     case EDA_UNITS::MILLIMETRES:
82         scale = IU_PER_MM;
83         units = "mm";
84         break;
85 
86     case EDA_UNITS::INCHES:
87         scale = IU_PER_MILS * 1000;
88         units = "in";
89         break;
90 
91     case EDA_UNITS::MILS:
92         scale = IU_PER_MILS;
93         units = "mil";
94         break;
95 
96     default:
97         wxASSERT_MSG( false, "Invalid units" );
98     }
99 
100     for( unsigned int layer = 0; layer < m_frame->ImagesMaxCount(); ++layer )
101     {
102         GERBER_FILE_IMAGE* gerber = m_frame->GetGbrImage( layer );
103 
104         if( !gerber )
105             continue;
106 
107         if( gerber->GetDcodesCount() == 0 )
108             continue;
109 
110         if( curr_layer == static_cast<int>( layer ) )
111             Line.Printf( wxT( "*** Active layer (%2.2d) ***" ), layer + 1 );
112         else
113             Line.Printf( wxT( "*** layer %2.2d  ***" ), layer + 1 );
114 
115         list.Add( Line );
116 
117         for( ii = 0, jj = 1; ii < TOOLS_MAX_COUNT; ii++ )
118         {
119             D_CODE* pt_D_code = gerber->GetDCODE( ii + FIRST_DCODE );
120 
121             if( pt_D_code == nullptr )
122                 continue;
123 
124             if( !pt_D_code->m_InUse && !pt_D_code->m_Defined )
125                 continue;
126 
127             Line.Printf( wxT( "tool %2.2d:   D%2.2d   V %.4f %s  H %.4f %s   %s  attribute '%s'" ),
128                          jj,
129                          pt_D_code->m_Num_Dcode,
130                          pt_D_code->m_Size.y / scale, units,
131                          pt_D_code->m_Size.x / scale, units,
132                          D_CODE::ShowApertureType( pt_D_code->m_Shape ),
133                          pt_D_code->m_AperFunction.IsEmpty()? wxT( "none" ) : pt_D_code->m_AperFunction
134                          );
135 
136             if( !pt_D_code->m_Defined )
137                 Line += wxT( " (not defined)" );
138 
139             if( pt_D_code->m_InUse )
140                 Line += wxT( " (in use)" );
141 
142             list.Add( Line );
143             jj++;
144         }
145     }
146 
147     wxSingleChoiceDialog dlg( m_frame, wxEmptyString, _( "D Codes" ), list, (void**) nullptr,
148                               wxCHOICEDLG_STYLE & ~wxCANCEL );
149 
150     dlg.ShowModal();
151 
152     return 0;
153 }
154 
155 
ShowSource(const TOOL_EVENT & aEvent)156 int GERBVIEW_INSPECTION_TOOL::ShowSource( const TOOL_EVENT& aEvent )
157 {
158     int                layer        = m_frame->GetActiveLayer();
159     GERBER_FILE_IMAGE* gerber_layer = m_frame->GetGbrImage( layer );
160 
161     if( gerber_layer )
162     {
163         wxString editorname = Pgm().GetTextEditor();
164 
165         if( !editorname.IsEmpty() )
166         {
167             wxFileName fn( gerber_layer->m_FileName );
168 
169             // Call the editor only if the Gerber/drill source file is available.
170             // This is not always the case, because it can be a temporary file
171             // if it comes from a zip archive.
172             if( !fn.FileExists() )
173             {
174                 wxString msg;
175                 msg.Printf( _( "Source file '%s' not found." ), fn.GetFullPath() );
176                 wxMessageBox( msg );
177             }
178             else
179             {
180                 ExecuteFile( editorname, fn.GetFullPath() );
181             }
182         }
183         else
184         {
185             wxMessageBox( _( "No text editor selected in KiCad.  Please choose one." ) );
186         }
187     }
188     else
189     {
190         wxString msg;
191         msg.Printf( _( "No file loaded on the active layer %d." ), layer + 1 );
192         wxMessageBox( msg );
193     }
194 
195     return 0;
196 }
197 
198 
199 using KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER;
200 
201 
MeasureTool(const TOOL_EVENT & aEvent)202 int GERBVIEW_INSPECTION_TOOL::MeasureTool( const TOOL_EVENT& aEvent )
203 {
204     KIGFX::VIEW_CONTROLS&      controls = *getViewControls();
205     bool                       originSet = false;
206     TWO_POINT_GEOMETRY_MANAGER twoPtMgr;
207     EDA_UNITS                  units = m_frame->GetUserUnits();
208     KIGFX::PREVIEW::RULER_ITEM ruler( twoPtMgr, units, false, false );
209 
210     std::string tool = aEvent.GetCommandStr().get();
211     m_frame->PushTool( tool );
212 
213     auto setCursor =
214             [&]()
215             {
216                 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
217             };
218 
219     auto cleanup =
220             [&] ()
221             {
222                 getView()->SetVisible( &ruler, false );
223                 controls.SetAutoPan( false );
224                 controls.CaptureCursor( false );
225                 originSet = false;
226             };
227 
228     Activate();
229     // Must be done after Activate() so that it gets set into the correct context
230     controls.ShowCursor( true );
231     // Set initial cursor
232     setCursor();
233 
234     getView()->Add( &ruler );
235     getView()->SetVisible( &ruler, false );
236 
237     while( TOOL_EVENT* evt = Wait() )
238     {
239         setCursor();
240         const VECTOR2I cursorPos = controls.GetCursorPosition();
241 
242         if( evt->IsCancelInteractive() )
243         {
244             if( originSet )
245             {
246                 cleanup();
247             }
248             else
249             {
250                 m_frame->PopTool( tool );
251                 break;
252             }
253         }
254         else if( evt->IsActivate() )
255         {
256             if( originSet )
257                 cleanup();
258 
259             if( evt->IsMoveTool() )
260             {
261                 // leave ourselves on the stack so we come back after the move
262                 break;
263             }
264             else
265             {
266                 m_frame->PopTool( tool );
267                 break;
268             }
269         }
270         else if( !originSet && ( evt->IsDrag( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
271         {
272             // click or drag starts
273             twoPtMgr.SetOrigin( cursorPos );
274             twoPtMgr.SetEnd( cursorPos );
275 
276             controls.CaptureCursor( true );
277             controls.SetAutoPan( true );
278 
279             originSet = true;
280         }
281         else if( originSet && ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
282         {
283             // second click or mouse up after drag ends
284             originSet = false;
285 
286             controls.SetAutoPan( false );
287             controls.CaptureCursor( false );
288         }
289         else if( originSet && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
290         {
291             // move or drag when origin set updates rules
292             twoPtMgr.SetAngleSnap( evt->Modifier( MD_SHIFT ) );
293             twoPtMgr.SetEnd( cursorPos );
294 
295             getView()->SetVisible( &ruler, true );
296             getView()->Update( &ruler, KIGFX::GEOMETRY );
297         }
298         else if( evt->IsAction( &ACTIONS::updateUnits ) )
299         {
300             if( m_frame->GetUserUnits() != units )
301             {
302                 units = m_frame->GetUserUnits();
303                 ruler.SwitchUnits( units );
304                 getView()->Update( &ruler, KIGFX::GEOMETRY );
305             }
306             evt->SetPassEvent();
307         }
308         else if( evt->IsClick( BUT_RIGHT ) )
309         {
310             m_menu.ShowContextMenu( m_frame->GetCurrentSelection() );
311         }
312         else
313         {
314             evt->SetPassEvent();
315         }
316     }
317 
318     getView()->SetVisible( &ruler, false );
319     getView()->Remove( &ruler );
320 
321     m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
322     return 0;
323 }
324 
325 
setTransitions()326 void GERBVIEW_INSPECTION_TOOL::setTransitions()
327 {
328     Go( &GERBVIEW_INSPECTION_TOOL::ShowSource,     GERBVIEW_ACTIONS::showSource.MakeEvent() );
329     Go( &GERBVIEW_INSPECTION_TOOL::ShowDCodes,     GERBVIEW_ACTIONS::showDCodes.MakeEvent() );
330     Go( &GERBVIEW_INSPECTION_TOOL::MeasureTool,    ACTIONS::measureTool.MakeEvent() );
331 }
332