1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004-2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 #include <base_screen.h>
27 #include <bitmaps.h>
28 #include <confirm.h>
29 #include <core/arraydim.h>
30 #include <dialog_shim.h>
31 #include <eda_draw_frame.h>
32 #include <filehistory.h>
33 #include <id.h>
34 #include <kiface_base.h>
35 #include <lockfile.h>
36 #include <macros.h>
37 #include <page_info.h>
38 #include <paths.h>
39 #include <pgm_base.h>
40 #include <render_settings.h>
41 #include <settings/app_settings.h>
42 #include <settings/color_settings.h>
43 #include <settings/common_settings.h>
44 #include <settings/settings_manager.h>
45 #include <title_block.h>
46 #include <tool/actions.h>
47 #include <tool/common_tools.h>
48 #include <tool/grid_menu.h>
49 #include <tool/selection_conditions.h>
50 #include <tool/tool_dispatcher.h>
51 #include <tool/tool_manager.h>
52 #include <tool/tool_menu.h>
53 #include <tool/zoom_menu.h>
54 #include <trace_helpers.h>
55 #include <view/view.h>
56 #include <drawing_sheet/ds_draw_item.h>
57 #include <widgets/msgpanel.h>
58 #include <wx/event.h>
59 #include <wx/snglinst.h>
60 #include <dialogs/dialog_grid_settings.h>
61 #include <widgets/ui_common.h>
62 #include <wx/dirdlg.h>
63 #include <wx/filedlg.h>
64 #include <wx/msgdlg.h>
65 #include <wx/socket.h>
66 
67 #include <wx/snglinst.h>
68 #include <wx/fdrepdlg.h>
69 
70 #define FR_HISTORY_LIST_CNT     10   ///< Maximum size of the find/replace history stacks.
71 
72 
BEGIN_EVENT_TABLE(EDA_DRAW_FRAME,KIWAY_PLAYER)73 BEGIN_EVENT_TABLE( EDA_DRAW_FRAME, KIWAY_PLAYER )
74     EVT_UPDATE_UI( ID_ON_GRID_SELECT, EDA_DRAW_FRAME::OnUpdateSelectGrid )
75     EVT_UPDATE_UI( ID_ON_ZOOM_SELECT, EDA_DRAW_FRAME::OnUpdateSelectZoom )
76 
77     EVT_ACTIVATE( EDA_DRAW_FRAME::onActivate )
78 END_EVENT_TABLE()
79 
80 
81 EDA_DRAW_FRAME::EDA_DRAW_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrameType,
82                                 const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
83                                 long aStyle, const wxString & aFrameName ) :
84     KIWAY_PLAYER( aKiway, aParent, aFrameType, aTitle, aPos, aSize, aStyle, aFrameName )
85 {
86     m_socketServer        = nullptr;
87     m_mainToolBar         = nullptr;
88     m_drawToolBar         = nullptr;
89     m_optionsToolBar      = nullptr;
90     m_auxiliaryToolBar    = nullptr;
91     m_gridSelectBox       = nullptr;
92     m_zoomSelectBox       = nullptr;
93     m_firstRunDialogSetting = 0;
94     m_undoRedoCountMax    = DEFAULT_MAX_UNDO_ITEMS;
95 
96     m_canvasType          = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
97     m_canvas              = nullptr;
98     m_toolDispatcher      = nullptr;
99     m_messagePanel        = nullptr;
100     m_currentScreen       = nullptr;
101     m_showBorderAndTitleBlock = false;  // true to display reference sheet.
102     m_gridColor           = COLOR4D( DARKGRAY );   // Default grid color
103     m_showPageLimits      = false;
104     m_drawBgColor         = COLOR4D( BLACK );   // the background color of the draw canvas:
105                                                 // BLACK for Pcbnew, BLACK or WHITE for Eeschema
106     m_colorSettings       = nullptr;
107     m_msgFrameHeight      = EDA_MSG_PANEL::GetRequiredHeight( this );
108     m_userUnits           = EDA_UNITS::MILLIMETRES;
109     m_polarCoords         = false;
110     m_findReplaceData     = new wxFindReplaceData( wxFR_DOWN );
111 
112     m_auimgr.SetFlags( wxAUI_MGR_DEFAULT );
113 
114     CreateStatusBar( 8 );
115 
116     // set the size of the status bar subwindows:
117 
118     wxWindow* stsbar = GetStatusBar();
119     int       spacer = KIUI::GetTextSize( wxT( "M" ), stsbar ).x * 2;
120 
121     int dims[] = {
122 
123         // remainder of status bar on far left is set to a default or whatever is left over.
124         -1,
125 
126         // When using GetTextSize() remember the width of character '1' is not the same
127         // as the width of '0' unless the font is fixed width, and it usually won't be.
128 
129         // zoom:
130         KIUI::GetTextSize( wxT( "Z 762000" ), stsbar ).x + spacer,
131 
132         // cursor coords
133         KIUI::GetTextSize( wxT( "X 1234.1234  Y 1234.1234" ), stsbar ).x + spacer,
134 
135         // delta distances
136         KIUI::GetTextSize( wxT( "dx 1234.1234  dy 1234.1234  dist 1234.1234" ), stsbar ).x + spacer,
137 
138         // grid size
139         KIUI::GetTextSize( wxT( "grid X 1234.1234  Y 1234.1234" ), stsbar ).x + spacer,
140 
141         // units display, Inches is bigger than mm
142         KIUI::GetTextSize( _( "Inches" ), stsbar ).x + spacer,
143 
144         // Size for the "Current Tool" panel; longest string from SetTool()
145         KIUI::GetTextSize( wxT( "Add layer alignment target" ), stsbar ).x + spacer,
146 
147         // constraint mode
148         KIUI::GetTextSize( _( "Constrain to H, V, 45" ), stsbar ).x + spacer
149     };
150 
151     SetStatusWidths( arrayDim( dims ), dims );
152     stsbar->SetFont( KIUI::GetStatusFont( this ) );
153 
154     // Create child subwindows.
155     GetClientSize( &m_frameSize.x, &m_frameSize.y );
156     m_framePos.x   = m_framePos.y = 0;
157     m_frameSize.y -= m_msgFrameHeight;
158 
159     m_messagePanel  = new EDA_MSG_PANEL( this, -1, wxPoint( 0, m_frameSize.y ),
160                                          wxSize( m_frameSize.x, m_msgFrameHeight ) );
161 
162     m_messagePanel->SetBackgroundColour( COLOR4D( LIGHTGRAY ).ToColour() );
163 
164 #if wxCHECK_VERSION( 3, 1, 3 )
165     Bind( wxEVT_DPI_CHANGED,
166           [&]( wxDPIChangedEvent& )
167           {
168               wxMoveEvent dummy;
169               OnMove( dummy );
170           } );
171 #endif
172 }
173 
174 
~EDA_DRAW_FRAME()175 EDA_DRAW_FRAME::~EDA_DRAW_FRAME()
176 {
177     delete m_socketServer;
178 
179     for( auto socket : m_sockets )
180     {
181         socket->Shutdown();
182         socket->Destroy();
183     }
184 
185     saveCanvasTypeSetting( m_canvasType );
186 
187     delete m_actions;
188     delete m_toolManager;
189     delete m_toolDispatcher;
190     delete m_canvas;
191 
192     delete m_currentScreen;
193     m_currentScreen = nullptr;
194 
195     delete m_findReplaceData;
196 
197     m_auimgr.UnInit();
198 
199     ReleaseFile();
200 }
201 
202 
ReleaseFile()203 void EDA_DRAW_FRAME::ReleaseFile()
204 {
205     m_file_checker = nullptr;
206 }
207 
208 
LockFile(const wxString & aFileName)209 bool EDA_DRAW_FRAME::LockFile( const wxString& aFileName )
210 {
211     m_file_checker = ::LockFile( aFileName );
212 
213     return m_file_checker && !m_file_checker->IsAnotherRunning();
214 }
215 
216 
ScriptingConsoleEnableDisable()217 void EDA_DRAW_FRAME::ScriptingConsoleEnableDisable()
218 {
219     KIWAY_PLAYER* frame = Kiway().Player( FRAME_PYTHON, false );
220 
221     wxRect  rect = GetScreenRect();
222     wxPoint center = rect.GetPosition() + rect.GetSize() / 2;
223 
224     if( !frame )
225     {
226         frame = Kiway().Player( FRAME_PYTHON, true, Kiway().GetTop() );
227 
228         // If we received an error in the CTOR due to Python-ness, don't crash
229         if( !frame )
230             return;
231 
232         if( !frame->IsVisible() )
233             frame->Show( true );
234 
235         // On Windows, Raise() does not bring the window on screen, when iconized
236         if( frame->IsIconized() )
237             frame->Iconize( false );
238 
239         frame->Raise();
240         frame->SetPosition( center - frame->GetSize() / 2 );
241 
242         return;
243     }
244 
245     frame->Show( !frame->IsVisible() );
246     frame->SetPosition( center - frame->GetSize() / 2 );
247 }
248 
249 
IsScriptingConsoleVisible()250 bool EDA_DRAW_FRAME::IsScriptingConsoleVisible()
251 {
252     KIWAY_PLAYER* frame = Kiway().Player( FRAME_PYTHON, false );
253     return frame && frame->IsVisible();
254 }
255 
256 
unitsChangeRefresh()257 void EDA_DRAW_FRAME::unitsChangeRefresh()
258 {
259     // Notify all tools the units have changed
260     if( m_toolManager )
261         m_toolManager->RunAction( ACTIONS::updateUnits, true );
262 
263     UpdateStatusBar();
264     UpdateMsgPanel();
265 }
266 
267 
ToggleUserUnits()268 void EDA_DRAW_FRAME::ToggleUserUnits()
269 {
270     SetUserUnits( m_userUnits == EDA_UNITS::INCHES ? EDA_UNITS::MILLIMETRES : EDA_UNITS::INCHES );
271     unitsChangeRefresh();
272 
273     wxCommandEvent e( UNITS_CHANGED );
274     ProcessEventLocally( e );
275 }
276 
277 
CommonSettingsChanged(bool aEnvVarsChanged,bool aTextVarsChanged)278 void EDA_DRAW_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
279 {
280     EDA_BASE_FRAME::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
281 
282     COMMON_SETTINGS*      settings = Pgm().GetCommonSettings();
283     KIGFX::VIEW_CONTROLS* viewControls = GetCanvas()->GetViewControls();
284 
285     SetAutoSaveInterval( settings->m_System.autosave_interval );
286 
287     viewControls->LoadSettings();
288 
289     m_galDisplayOptions.ReadCommonConfig( *settings, this );
290 
291     // Notify all tools the preferences have changed
292     if( m_toolManager )
293         m_toolManager->RunAction( ACTIONS::updatePreferences, true );
294 }
295 
296 
EraseMsgBox()297 void EDA_DRAW_FRAME::EraseMsgBox()
298 {
299     if( m_messagePanel )
300         m_messagePanel->EraseMsgBox();
301 }
302 
303 
UpdateGridSelectBox()304 void EDA_DRAW_FRAME::UpdateGridSelectBox()
305 {
306     UpdateStatusBar();
307     DisplayUnitsMsg();
308 
309     if( m_gridSelectBox == nullptr )
310         return;
311 
312     // Update grid values with the current units setting.
313     m_gridSelectBox->Clear();
314     wxArrayString gridsList;
315 
316     GRID_MENU::BuildChoiceList( &gridsList, config(), this );
317 
318     for( const wxString& grid : gridsList )
319         m_gridSelectBox->Append( grid );
320 
321     m_gridSelectBox->Append( wxT( "---" ) );
322     m_gridSelectBox->Append( _( "Edit User Grid..." ) );
323 
324     m_gridSelectBox->SetSelection( config()->m_Window.grid.last_size_idx );
325 }
326 
327 
OnUpdateSelectGrid(wxUpdateUIEvent & aEvent)328 void EDA_DRAW_FRAME::OnUpdateSelectGrid( wxUpdateUIEvent& aEvent )
329 {
330     // No need to update the grid select box if it doesn't exist or the grid setting change
331     // was made using the select box.
332     if( m_gridSelectBox == nullptr )
333         return;
334 
335     int idx = config()->m_Window.grid.last_size_idx;
336     idx = std::max( 0, std::min( idx, (int) m_gridSelectBox->GetCount() - 1 ) );
337 
338     if( idx != m_gridSelectBox->GetSelection() )
339         m_gridSelectBox->SetSelection( idx );
340 }
341 
342 
PrintPage(const RENDER_SETTINGS * aSettings)343 void EDA_DRAW_FRAME::PrintPage( const RENDER_SETTINGS* aSettings )
344 {
345     wxMessageBox( wxT("EDA_DRAW_FRAME::PrintPage() error") );
346 }
347 
348 
OnSelectGrid(wxCommandEvent & event)349 void EDA_DRAW_FRAME::OnSelectGrid( wxCommandEvent& event )
350 {
351     wxCHECK_RET( m_gridSelectBox, "m_gridSelectBox uninitialized" );
352 
353     int idx = m_gridSelectBox->GetCurrentSelection();
354 
355     if( idx == int( m_gridSelectBox->GetCount() ) - 2 )
356     {
357         // wxWidgets will check the separator, which we don't want.
358         // Re-check the current grid.
359         wxUpdateUIEvent dummy;
360         OnUpdateSelectGrid( dummy );
361     }
362     else if( idx == int( m_gridSelectBox->GetCount() ) - 1 )
363     {
364         // wxWidgets will check the Grid Settings... entry, which we don't want.
365         // Re-check the current grid.
366         wxUpdateUIEvent dummy;
367         OnUpdateSelectGrid( dummy );
368 
369         // Give a time-slice to close the menu before opening the dialog.
370         // (Only matters on some versions of GTK.)
371         wxSafeYield();
372 
373         m_toolManager->RunAction( ACTIONS::gridProperties, true );
374     }
375     else
376     {
377         m_toolManager->RunAction( ACTIONS::gridPreset, true, static_cast<intptr_t>( idx ) );
378     }
379 
380     UpdateStatusBar();
381     m_canvas->Refresh();
382 }
383 
384 
OnGridSettings(wxCommandEvent & aEvent)385 void EDA_DRAW_FRAME::OnGridSettings( wxCommandEvent& aEvent )
386 {
387     DIALOG_GRID_SETTINGS dlg( this );
388 
389     if( dlg.ShowModal() == wxID_OK )
390     {
391         UpdateStatusBar();
392         GetCanvas()->Refresh();
393     }
394 }
395 
396 
IsGridVisible() const397 bool EDA_DRAW_FRAME::IsGridVisible() const
398 {
399     return config()->m_Window.grid.show;
400 }
401 
402 
SetGridVisibility(bool aVisible)403 void EDA_DRAW_FRAME::SetGridVisibility( bool aVisible )
404 {
405     config()->m_Window.grid.show = aVisible;
406 
407     // Update the display with the new grid
408     if( GetCanvas() )
409     {
410         // Check to ensure these exist, since this function could be called before
411         // the GAL and View have been created
412         if( GetCanvas()->GetGAL() )
413             GetCanvas()->GetGAL()->SetGridVisibility( aVisible );
414 
415         if( GetCanvas()->GetView() )
416             GetCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
417 
418         GetCanvas()->Refresh();
419     }
420 }
421 
422 
UpdateZoomSelectBox()423 void EDA_DRAW_FRAME::UpdateZoomSelectBox()
424 {
425     if( m_zoomSelectBox == nullptr )
426         return;
427 
428     double zoom = m_canvas->GetGAL()->GetZoomFactor();
429 
430     m_zoomSelectBox->Clear();
431     m_zoomSelectBox->Append( _( "Zoom Auto" ) );
432     m_zoomSelectBox->SetSelection( 0 );
433 
434     for( unsigned i = 0;  i < config()->m_Window.zoom_factors.size();  ++i )
435     {
436         double current = config()->m_Window.zoom_factors[i];
437 
438         m_zoomSelectBox->Append( wxString::Format( _( "Zoom %.2f" ), current ) );
439 
440         if( zoom == current )
441             m_zoomSelectBox->SetSelection( i + 1 );
442     }
443 }
444 
445 
OnUpdateSelectZoom(wxUpdateUIEvent & aEvent)446 void EDA_DRAW_FRAME::OnUpdateSelectZoom( wxUpdateUIEvent& aEvent )
447 {
448     if( m_zoomSelectBox == nullptr || m_zoomSelectBox->GetParent() == nullptr )
449         return;
450 
451     int current = 0;    // display Auto if no match found
452 
453     // check for a match within 1%
454     double zoom = GetCanvas()->GetGAL()->GetZoomFactor();
455 
456     for( unsigned i = 0; i < config()->m_Window.zoom_factors.size(); i++ )
457     {
458         if( std::fabs( zoom - config()->m_Window.zoom_factors[i] ) < ( zoom / 100.0 ) )
459         {
460             current = i + 1;
461             break;
462         }
463     }
464 
465     if( current != m_zoomSelectBox->GetSelection() )
466         m_zoomSelectBox->SetSelection( current );
467 }
468 
469 
OnSelectZoom(wxCommandEvent & event)470 void EDA_DRAW_FRAME::OnSelectZoom( wxCommandEvent& event )
471 {
472     wxCHECK_RET( m_zoomSelectBox, "m_zoomSelectBox uninitialized" );
473 
474     int id = m_zoomSelectBox->GetCurrentSelection();
475 
476     if( id < 0 || !( id < (int)m_zoomSelectBox->GetCount() ) )
477         return;
478 
479     m_toolManager->RunAction( ACTIONS::zoomPreset, true, static_cast<intptr_t>( id ) );
480     UpdateStatusBar();
481     m_canvas->Refresh();
482 }
483 
484 
OnMove(wxMoveEvent & aEvent)485 void EDA_DRAW_FRAME::OnMove( wxMoveEvent& aEvent )
486 {
487     // If the window is moved to a different display, the scaling factor may change
488     double oldFactor = m_galDisplayOptions.m_scaleFactor;
489     m_galDisplayOptions.UpdateScaleFactor();
490 
491     if( oldFactor != m_galDisplayOptions.m_scaleFactor && m_canvas )
492     {
493         wxSize clientSize = GetClientSize();
494         GetCanvas()->GetGAL()->ResizeScreen( clientSize.x, clientSize.y );
495         GetCanvas()->GetView()->MarkDirty();
496     }
497 
498     aEvent.Skip();
499 }
500 
501 
AddStandardSubMenus(TOOL_MENU & aToolMenu)502 void EDA_DRAW_FRAME::AddStandardSubMenus( TOOL_MENU& aToolMenu )
503 {
504     COMMON_TOOLS*     commonTools = m_toolManager->GetTool<COMMON_TOOLS>();
505     CONDITIONAL_MENU& aMenu = aToolMenu.GetMenu();
506 
507     aMenu.AddSeparator( 1000 );
508 
509     auto zoomMenu = std::make_shared<ZOOM_MENU>( this );
510     zoomMenu->SetTool( commonTools );
511     aToolMenu.AddSubMenu( zoomMenu );
512 
513     auto gridMenu = std::make_shared<GRID_MENU>( this );
514     gridMenu->SetTool( commonTools );
515     aToolMenu.AddSubMenu( gridMenu );
516 
517     aMenu.AddMenu( zoomMenu.get(),   SELECTION_CONDITIONS::ShowAlways, 1000 );
518     aMenu.AddMenu( gridMenu.get(), SELECTION_CONDITIONS::ShowAlways, 1000 );
519 }
520 
521 
DisplayToolMsg(const wxString & msg)522 void EDA_DRAW_FRAME::DisplayToolMsg( const wxString& msg )
523 {
524     SetStatusText( msg, 6 );
525 }
526 
527 
DisplayConstraintsMsg(const wxString & msg)528 void EDA_DRAW_FRAME::DisplayConstraintsMsg( const wxString& msg )
529 {
530     SetStatusText( msg, 7 );
531 }
532 
533 
DisplayGridMsg()534 void EDA_DRAW_FRAME::DisplayGridMsg()
535 {
536     wxString line;
537 
538     line.Printf( "grid %s",
539                  MessageTextFromValue( GetUserUnits(), GetCanvas()->GetGAL()->GetGridSize().x,
540                                        false ) );
541 
542     SetStatusText( line, 4 );
543 }
544 
545 
DisplayUnitsMsg()546 void EDA_DRAW_FRAME::DisplayUnitsMsg()
547 {
548     wxString msg;
549 
550     switch( m_userUnits )
551     {
552     case EDA_UNITS::INCHES:      msg = _( "inches" ); break;
553     case EDA_UNITS::MILS:        msg = _( "mils" );   break;
554     case EDA_UNITS::MILLIMETRES: msg = _( "mm" );     break;
555     default:                     msg = _( "Units" );  break;
556     }
557 
558     SetStatusText( msg, 5 );
559 }
560 
561 
OnSize(wxSizeEvent & SizeEv)562 void EDA_DRAW_FRAME::OnSize( wxSizeEvent& SizeEv )
563 {
564     EDA_BASE_FRAME::OnSize( SizeEv );
565 
566     m_frameSize = GetClientSize( );
567 
568     SizeEv.Skip();
569 }
570 
571 
UpdateStatusBar()572 void EDA_DRAW_FRAME::UpdateStatusBar()
573 {
574     SetStatusText( GetZoomLevelIndicator(), 1 );
575 
576     // Absolute and relative cursor positions are handled by overloading this function and
577     // handling the internal to user units conversion at the appropriate level.
578 
579     // refresh units display
580     DisplayUnitsMsg();
581 }
582 
583 
GetZoomLevelIndicator() const584 const wxString EDA_DRAW_FRAME::GetZoomLevelIndicator() const
585 {
586     // returns a human readable value which can be displayed as zoom
587     // level indicator in dialogs.
588     double zoom = m_canvas->GetGAL()->GetZoomFactor();
589     return wxString::Format( wxT( "Z %.2f" ), zoom );
590 }
591 
592 
LoadSettings(APP_SETTINGS_BASE * aCfg)593 void EDA_DRAW_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
594 {
595     EDA_BASE_FRAME::LoadSettings( aCfg );
596 
597     COMMON_SETTINGS* cmnCfg = Pgm().GetCommonSettings();
598     WINDOW_SETTINGS* window = GetWindowSettings( aCfg );
599 
600     // Read units used in dialogs and toolbars
601     SetUserUnits( static_cast<EDA_UNITS>( aCfg->m_System.units ) );
602 
603     m_undoRedoCountMax = aCfg->m_System.max_undo_items;
604     m_firstRunDialogSetting = aCfg->m_System.first_run_shown;
605 
606     m_galDisplayOptions.ReadConfig( *cmnCfg, *window, this );
607 
608     m_findReplaceData->SetFlags( aCfg->m_FindReplace.flags );
609     m_findReplaceData->SetFindString( aCfg->m_FindReplace.find_string );
610     m_findReplaceData->SetReplaceString( aCfg->m_FindReplace.replace_string );
611 
612     for( auto& s : aCfg->m_FindReplace.find_history )
613         m_findStringHistoryList.Add( s );
614 
615     for( auto& s : aCfg->m_FindReplace.replace_history )
616         m_replaceStringHistoryList.Add( s );
617 }
618 
619 
SaveSettings(APP_SETTINGS_BASE * aCfg)620 void EDA_DRAW_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
621 {
622     EDA_BASE_FRAME::SaveSettings( aCfg );
623 
624     WINDOW_SETTINGS* window = GetWindowSettings( aCfg );
625 
626     aCfg->m_System.units = static_cast<int>( m_userUnits );
627     aCfg->m_System.first_run_shown = m_firstRunDialogSetting;
628     aCfg->m_System.max_undo_items = GetMaxUndoItems();
629 
630     m_galDisplayOptions.WriteConfig( *window );
631 
632     aCfg->m_FindReplace.flags = m_findReplaceData->GetFlags();
633     aCfg->m_FindReplace.find_string = m_findReplaceData->GetFindString();
634     aCfg->m_FindReplace.replace_string = m_findReplaceData->GetReplaceString();
635 
636     aCfg->m_FindReplace.find_history.clear();
637     aCfg->m_FindReplace.replace_history.clear();
638 
639     for( size_t i = 0; i < m_findStringHistoryList.GetCount() && i < FR_HISTORY_LIST_CNT; i++ )
640     {
641         aCfg->m_FindReplace.find_history.push_back( m_findStringHistoryList[ i ].ToStdString() );
642     }
643 
644     for( size_t i = 0; i < m_replaceStringHistoryList.GetCount() && i < FR_HISTORY_LIST_CNT; i++ )
645     {
646         aCfg->m_FindReplace.replace_history.push_back(
647                 m_replaceStringHistoryList[ i ].ToStdString() );
648     }
649 
650     // Save the units used in this frame
651     if( m_toolManager )
652     {
653         if( COMMON_TOOLS* cmnTool = m_toolManager->GetTool<COMMON_TOOLS>() )
654         {
655             aCfg->m_System.last_imperial_units = static_cast<int>( cmnTool->GetLastImperialUnits() );
656             aCfg->m_System.last_metric_units   = static_cast<int>( cmnTool->GetLastMetricUnits() );
657         }
658     }
659 }
660 
661 
AppendMsgPanel(const wxString & aTextUpper,const wxString & aTextLower,int aPadding)662 void EDA_DRAW_FRAME::AppendMsgPanel( const wxString& aTextUpper, const wxString& aTextLower,
663                                      int aPadding )
664 {
665     if( m_messagePanel )
666         m_messagePanel->AppendMessage( aTextUpper, aTextLower, aPadding );
667 }
668 
669 
ClearMsgPanel()670 void EDA_DRAW_FRAME::ClearMsgPanel()
671 {
672     if( m_messagePanel )
673         m_messagePanel->EraseMsgBox();
674 }
675 
676 
SetMsgPanel(const std::vector<MSG_PANEL_ITEM> & aList)677 void EDA_DRAW_FRAME::SetMsgPanel( const std::vector<MSG_PANEL_ITEM>& aList )
678 {
679     if( m_messagePanel )
680     {
681         m_messagePanel->EraseMsgBox();
682 
683         for( const MSG_PANEL_ITEM& item : aList )
684             m_messagePanel->AppendMessage( item );
685     }
686 }
687 
688 
SetMsgPanel(const wxString & aTextUpper,const wxString & aTextLower,int aPadding)689 void EDA_DRAW_FRAME::SetMsgPanel( const wxString& aTextUpper, const wxString& aTextLower,
690                                   int aPadding )
691 {
692     if( m_messagePanel )
693     {
694         m_messagePanel->EraseMsgBox();
695 
696         m_messagePanel->AppendMessage( aTextUpper, aTextLower, aPadding );
697     }
698 }
699 
700 
SetMsgPanel(EDA_ITEM * aItem)701 void EDA_DRAW_FRAME::SetMsgPanel( EDA_ITEM* aItem )
702 {
703     wxCHECK_RET( aItem, wxT( "Invalid EDA_ITEM pointer.  Bad programmer." ) );
704 
705     std::vector<MSG_PANEL_ITEM> items;
706     aItem->GetMsgPanelInfo( this, items );
707     SetMsgPanel( items );
708 }
709 
710 
UpdateMsgPanel()711 void EDA_DRAW_FRAME::UpdateMsgPanel()
712 {
713     GetToolManager()->PostEvent( EVENTS::SelectedItemsModified );
714 }
715 
716 
ActivateGalCanvas()717 void EDA_DRAW_FRAME::ActivateGalCanvas()
718 {
719     GetCanvas()->SetEvtHandlerEnabled( true );
720     GetCanvas()->StartDrawing();
721 }
722 
723 
SwitchCanvas(EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType)724 void EDA_DRAW_FRAME::SwitchCanvas( EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType )
725 {
726     GetCanvas()->SwitchBackend( aCanvasType );
727     m_canvasType = GetCanvas()->GetBackend();
728 
729     ActivateGalCanvas();
730 }
731 
732 
loadCanvasTypeSetting()733 EDA_DRAW_PANEL_GAL::GAL_TYPE EDA_DRAW_FRAME::loadCanvasTypeSetting()
734 {
735 #ifdef __WXMAC__
736     // Cairo renderer doesn't handle Retina displays so there's really only one game
737     // in town for Mac
738     return EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
739 #endif
740 
741     EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
742     APP_SETTINGS_BASE* cfg = Kiface().KifaceSettings();
743 
744     if( cfg )
745         canvasType = static_cast<EDA_DRAW_PANEL_GAL::GAL_TYPE>( cfg->m_Graphics.canvas_type );
746 
747     if( canvasType < EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE
748             || canvasType >= EDA_DRAW_PANEL_GAL::GAL_TYPE_LAST )
749     {
750         wxASSERT( false );
751         canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE;
752     }
753 
754     // Legacy canvas no longer supported.  Switch to OpenGL, falls back to Cairo on failure
755     if( canvasType == EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE )
756         canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
757 
758     return canvasType;
759 }
760 
761 
saveCanvasTypeSetting(EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType)762 bool EDA_DRAW_FRAME::saveCanvasTypeSetting( EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType )
763 {
764     // Not all classes derived from EDA_DRAW_FRAME can save the canvas type, because some
765     // have a fixed type, or do not have a option to set the canvas type (they inherit from
766     // a parent frame)
767     FRAME_T allowed_frames[] =
768     {
769         FRAME_SCH, FRAME_SCH_SYMBOL_EDITOR,
770         FRAME_PCB_EDITOR, FRAME_FOOTPRINT_EDITOR,
771         FRAME_GERBER,
772         FRAME_PL_EDITOR
773     };
774 
775     bool allow_save = false;
776 
777     for( unsigned ii = 0; ii < arrayDim( allowed_frames ); ii++ )
778     {
779         if( m_ident == allowed_frames[ii] )
780         {
781             allow_save = true;
782             break;
783         }
784     }
785 
786     if( !allow_save )
787         return false;
788 
789     if( aCanvasType < EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE
790             || aCanvasType >= EDA_DRAW_PANEL_GAL::GAL_TYPE_LAST )
791     {
792         wxASSERT( false );
793         return false;
794     }
795 
796     APP_SETTINGS_BASE* cfg = Kiface().KifaceSettings();
797 
798     if( cfg )
799         cfg->m_Graphics.canvas_type = static_cast<int>( aCanvasType );
800 
801     return false;
802 }
803 
804 
GetNearestGridPosition(const wxPoint & aPosition) const805 wxPoint EDA_DRAW_FRAME::GetNearestGridPosition( const wxPoint& aPosition ) const
806 {
807     const wxPoint& gridOrigin = GetGridOrigin();
808     VECTOR2D       gridSize = GetCanvas()->GetGAL()->GetGridSize();
809 
810     double xOffset = fmod( gridOrigin.x, gridSize.x );
811     int    x = KiROUND( (aPosition.x - xOffset) / gridSize.x );
812     double yOffset = fmod( gridOrigin.y, gridSize.y );
813     int    y = KiROUND( (aPosition.y - yOffset) / gridSize.y );
814 
815     return wxPoint( KiROUND( x * gridSize.x + xOffset ), KiROUND( y * gridSize.y + yOffset ) );
816 }
817 
818 
GetNearestHalfGridPosition(const wxPoint & aPosition) const819 wxPoint EDA_DRAW_FRAME::GetNearestHalfGridPosition( const wxPoint& aPosition ) const
820 {
821     const wxPoint& gridOrigin = GetGridOrigin();
822     VECTOR2D       gridSize = GetCanvas()->GetGAL()->GetGridSize() / 2.0;
823 
824     double xOffset = fmod( gridOrigin.x, gridSize.x );
825     int    x = KiROUND( (aPosition.x - xOffset) / gridSize.x );
826     double yOffset = fmod( gridOrigin.y, gridSize.y );
827     int    y = KiROUND( (aPosition.y - yOffset) / gridSize.y );
828 
829     return wxPoint( KiROUND( x * gridSize.x + xOffset ), KiROUND( y * gridSize.y + yOffset ) );
830 }
831 
832 
GetDocumentExtents(bool aIncludeAllVisible) const833 const BOX2I EDA_DRAW_FRAME::GetDocumentExtents( bool aIncludeAllVisible ) const
834 {
835     return BOX2I();
836 }
837 
838 
HardRedraw()839 void EDA_DRAW_FRAME::HardRedraw()
840 {
841     // To be implemented by subclasses.
842 }
843 
844 
Zoom_Automatique(bool aWarpPointer)845 void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer )
846 {
847     m_toolManager->RunAction( ACTIONS::zoomFitScreen, true );
848 }
849 
850 
851 // Find the first child dialog.
findDialogs()852 std::vector<wxWindow*> EDA_DRAW_FRAME::findDialogs()
853 {
854     std::vector<wxWindow*> dialogs;
855 
856     for( wxWindow* window : GetChildren() )
857     {
858         if( dynamic_cast<DIALOG_SHIM*>( window ) )
859             dialogs.push_back( window );
860     }
861 
862     return dialogs;
863 }
864 
865 
FocusOnLocation(const wxPoint & aPos)866 void EDA_DRAW_FRAME::FocusOnLocation( const wxPoint& aPos )
867 {
868     bool  centerView = false;
869     BOX2D r = GetCanvas()->GetView()->GetViewport();
870 
871     // Center if we're off the current view, or within 10% of its edge
872     r.Inflate( - (int) r.GetWidth() / 10 );
873 
874     if( !r.Contains( aPos ) )
875         centerView = true;
876 
877     std::vector<BOX2D> dialogScreenRects;
878 
879     for( wxWindow* dialog : findDialogs() )
880     {
881         dialogScreenRects.emplace_back( GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ),
882                                         dialog->GetSize() );
883     }
884 
885     // Center if we're behind an obscuring dialog, or within 10% of its edge
886     for( BOX2D rect : dialogScreenRects )
887     {
888         rect.Inflate( rect.GetWidth() / 10 );
889 
890         if( rect.Contains( GetCanvas()->GetView()->ToScreen( aPos ) ) )
891             centerView = true;
892     }
893 
894     if( centerView )
895     {
896         try
897         {
898             GetCanvas()->GetView()->SetCenter( aPos, dialogScreenRects );
899         }
900         catch( const ClipperLib::clipperException& exc )
901         {
902             wxLogError( wxT( "Clipper library error '%s' occurred centering object." ),
903                         exc.what() );
904         }
905     }
906 
907     GetCanvas()->GetViewControls()->SetCrossHairCursorPosition( aPos );
908 }
909 
910 
911 static const wxString productName = wxT( "KiCad E.D.A.  " );
912 
913 
PrintDrawingSheet(const RENDER_SETTINGS * aSettings,const PAGE_INFO & aPageInfo,const wxString & aFullSheetName,const wxString & aFileName,const TITLE_BLOCK & aTitleBlock,int aSheetCount,const wxString & aPageNumber,double aMils2Iu,const PROJECT * aProject,const wxString & aSheetLayer,bool aIsFirstPage)914 void PrintDrawingSheet( const RENDER_SETTINGS* aSettings, const PAGE_INFO& aPageInfo,
915                         const wxString& aFullSheetName, const wxString& aFileName,
916                         const TITLE_BLOCK& aTitleBlock, int aSheetCount,
917                         const wxString& aPageNumber, double aMils2Iu, const PROJECT* aProject,
918                         const wxString& aSheetLayer, bool aIsFirstPage )
919 {
920     DS_DRAW_ITEM_LIST drawList;
921 
922     drawList.SetDefaultPenSize( aSettings->GetDefaultPenWidth() );
923     drawList.SetMilsToIUfactor( aMils2Iu );
924     drawList.SetPageNumber( aPageNumber );
925     drawList.SetSheetCount( aSheetCount );
926     drawList.SetFileName( aFileName );
927     drawList.SetSheetName( aFullSheetName );
928     drawList.SetSheetLayer( aSheetLayer );
929     drawList.SetProject( aProject );
930     drawList.SetIsFirstPage( aIsFirstPage );
931 
932     drawList.BuildDrawItemsList( aPageInfo, aTitleBlock );
933 
934     // Draw item list
935     drawList.Print( aSettings );
936 }
937 
938 
PrintDrawingSheet(const RENDER_SETTINGS * aSettings,BASE_SCREEN * aScreen,double aMils2Iu,const wxString & aFilename,const wxString & aSheetLayer)939 void EDA_DRAW_FRAME::PrintDrawingSheet( const RENDER_SETTINGS* aSettings, BASE_SCREEN* aScreen,
940                                         double aMils2Iu, const wxString &aFilename,
941                                         const wxString &aSheetLayer )
942 {
943     if( !m_showBorderAndTitleBlock )
944         return;
945 
946     wxDC*   DC = aSettings->GetPrintDC();
947     wxPoint origin = DC->GetDeviceOrigin();
948 
949     if( origin.y > 0 )
950     {
951         DC->SetDeviceOrigin( 0, 0 );
952         DC->SetAxisOrientation( true, false );
953     }
954 
955     ::PrintDrawingSheet( aSettings, GetPageSettings(), GetScreenDesc(), aFilename, GetTitleBlock(),
956                          aScreen->GetPageCount(), aScreen->GetPageNumber(), aMils2Iu, &Prj(),
957                          aSheetLayer, aScreen->GetVirtualPageNumber() == 1 );
958 
959     if( origin.y > 0 )
960     {
961         DC->SetDeviceOrigin( origin.x, origin.y );
962         DC->SetAxisOrientation( true, true );
963     }
964 }
965 
966 
GetScreenDesc() const967 wxString EDA_DRAW_FRAME::GetScreenDesc() const
968 {
969     // Virtual function. Base class implementation returns an empty string.
970     return wxEmptyString;
971 }
972 
973 
LibraryFileBrowser(bool doOpen,wxFileName & aFilename,const wxString & wildcard,const wxString & ext,bool isDirectory,bool aIsGlobal,const wxString & aGlobalPath)974 bool EDA_DRAW_FRAME::LibraryFileBrowser( bool doOpen, wxFileName& aFilename,
975                                          const wxString& wildcard, const wxString& ext,
976                                          bool isDirectory, bool aIsGlobal,
977                                          const wxString& aGlobalPath )
978 {
979     wxString prompt = doOpen ? _( "Select Library" ) : _( "New Library" );
980     aFilename.SetExt( ext );
981 
982     wxString dir = aGlobalPath;
983 
984 
985     if( isDirectory && doOpen )
986     {
987         if( !aIsGlobal )
988         {
989             dir = Prj().GetProjectPath();
990         }
991 
992         wxDirDialog dlg( this, prompt, dir,
993                          wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
994 
995         if( dlg.ShowModal() == wxID_CANCEL )
996             return false;
997 
998         aFilename = dlg.GetPath();
999         aFilename.SetExt( ext );
1000     }
1001     else
1002     {
1003         // Ensure the file has a dummy name, otherwise GTK will display the regex from the filter
1004         if( aFilename.GetName().empty() )
1005             aFilename.SetName( "Library" );
1006 
1007         if( !aIsGlobal )
1008         {
1009             dir = Prj().IsNullProject() ? aFilename.GetFullPath() : Prj().GetProjectPath();
1010         }
1011 
1012         wxFileDialog dlg( this, prompt, dir, aFilename.GetFullName(),
1013                           wildcard, doOpen ? wxFD_OPEN | wxFD_FILE_MUST_EXIST
1014                                            : wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT );
1015 
1016         if( dlg.ShowModal() == wxID_CANCEL )
1017             return false;
1018 
1019         aFilename = dlg.GetPath();
1020         aFilename.SetExt( ext );
1021     }
1022 
1023     return true;
1024 }
1025 
1026 
RecreateToolbars()1027 void EDA_DRAW_FRAME::RecreateToolbars()
1028 {
1029     // Rebuild all toolbars, and update the checked state of check tools
1030     if( m_mainToolBar )
1031         ReCreateHToolbar();
1032 
1033     if( m_drawToolBar )         // Drawing tools (typically on right edge of window)
1034         ReCreateVToolbar();
1035 
1036     if( m_optionsToolBar )      // Options (typically on left edge of window)
1037         ReCreateOptToolbar();
1038 
1039     if( m_auxiliaryToolBar )    // Additional tools under main toolbar
1040        ReCreateAuxiliaryToolbar();
1041 }
1042 
1043 
GetColorSettings() const1044 COLOR_SETTINGS* EDA_DRAW_FRAME::GetColorSettings() const
1045 {
1046     if( !m_colorSettings )
1047     {
1048         COLOR_SETTINGS* colorSettings = Pgm().GetSettingsManager().GetColorSettings();
1049 
1050         const_cast<EDA_DRAW_FRAME*>( this )->m_colorSettings = colorSettings;
1051     }
1052 
1053     return m_colorSettings;
1054 }
1055 
1056 
setupUnits(APP_SETTINGS_BASE * aCfg)1057 void EDA_DRAW_FRAME::setupUnits( APP_SETTINGS_BASE* aCfg )
1058 {
1059     COMMON_TOOLS* cmnTool = m_toolManager->GetTool<COMMON_TOOLS>();
1060 
1061     if( cmnTool )
1062     {
1063         // Tell the tool what the units used last session
1064         cmnTool->SetLastUnits( static_cast<EDA_UNITS>( aCfg->m_System.last_imperial_units ) );
1065         cmnTool->SetLastUnits( static_cast<EDA_UNITS>( aCfg->m_System.last_metric_units ) );
1066     }
1067 
1068     // Tell the tool what units the frame is currently using
1069     switch( static_cast<EDA_UNITS>( aCfg->m_System.units ) )
1070     {
1071     default:
1072     case EDA_UNITS::MILLIMETRES: m_toolManager->RunAction( ACTIONS::millimetersUnits ); break;
1073     case EDA_UNITS::INCHES:      m_toolManager->RunAction( ACTIONS::inchesUnits );      break;
1074     case EDA_UNITS::MILS:        m_toolManager->RunAction( ACTIONS::milsUnits );        break;
1075     }
1076 }
1077 
1078 
GetUnitPair(EDA_UNITS & aPrimaryUnit,EDA_UNITS & aSecondaryUnits)1079 void EDA_DRAW_FRAME::GetUnitPair( EDA_UNITS& aPrimaryUnit, EDA_UNITS& aSecondaryUnits )
1080 {
1081     COMMON_TOOLS* cmnTool = m_toolManager->GetTool<COMMON_TOOLS>();
1082 
1083     aPrimaryUnit    = GetUserUnits();
1084     aSecondaryUnits = EDA_UNITS::MILS;
1085 
1086     if( EDA_UNIT_UTILS::IsImperialUnit( aPrimaryUnit ) )
1087     {
1088         if( cmnTool )
1089             aSecondaryUnits = cmnTool->GetLastMetricUnits();
1090         else
1091             aSecondaryUnits = EDA_UNITS::MILLIMETRES;
1092     }
1093     else
1094     {
1095         if( cmnTool )
1096             aSecondaryUnits = cmnTool->GetLastImperialUnits();
1097         else
1098             aSecondaryUnits = EDA_UNITS::MILS;
1099     }
1100 }
1101 
1102 
resolveCanvasType()1103 void EDA_DRAW_FRAME::resolveCanvasType()
1104 {
1105     m_canvasType = loadCanvasTypeSetting();
1106 
1107     // Nudge user to switch to OpenGL if they are on legacy or Cairo
1108     if( m_firstRunDialogSetting < 1 )
1109     {
1110         if( m_canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL )
1111         {
1112             // Save Cairo as default in case OpenGL crashes
1113             saveCanvasTypeSetting( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO );
1114 
1115             // Switch to OpenGL, which will save the new setting if successful
1116             SwitchCanvas( EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL );
1117 
1118             // Switch back to Cairo if OpenGL is not supported
1119             if( GetCanvas()->GetBackend() == EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE )
1120                 SwitchCanvas( EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO );
1121 
1122             HardRedraw();
1123         }
1124 
1125         m_firstRunDialogSetting = 1;
1126         SaveSettings( config() );
1127     }
1128 }
1129 
1130 
handleActivateEvent(wxActivateEvent & aEvent)1131 void EDA_DRAW_FRAME::handleActivateEvent( wxActivateEvent& aEvent )
1132 {
1133     // Force a refresh of the message panel to ensure that the text is the right color
1134     // when the window activates
1135     if( !IsIconized() )
1136         m_messagePanel->Refresh();
1137 }
1138 
1139 
onActivate(wxActivateEvent & aEvent)1140 void EDA_DRAW_FRAME::onActivate( wxActivateEvent& aEvent )
1141 {
1142     handleActivateEvent( aEvent );
1143 
1144     aEvent.Skip();
1145 }
1146