1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 1992-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 <bitmaps.h>
27 #include <bitmap_store.h>
28 #include <dialog_shim.h>
29 #include <dialogs/panel_common_settings.h>
30 #include <dialogs/panel_mouse_settings.h>
31 #include <eda_dde.h>
32 #include <filehistory.h>
33 #include <id.h>
34 #include <kiface_base.h>
35 #include <menus_helpers.h>
36 #include <panel_hotkeys_editor.h>
37 #include <paths.h>
38 #include <confirm.h>
39 #include <pgm_base.h>
40 #include <settings/app_settings.h>
41 #include <settings/common_settings.h>
42 #include <settings/settings_manager.h>
43 #include <project/project_local_settings.h>
44 #include <tool/action_manager.h>
45 #include <tool/action_menu.h>
46 #include <tool/action_toolbar.h>
47 #include <tool/actions.h>
48 #include <tool/common_control.h>
49 #include <tool/tool_manager.h>
50 #include <tool/tool_dispatcher.h>
51 #include <trace_helpers.h>
52 #include <widgets/paged_dialog.h>
53 #include <widgets/infobar.h>
54 #include <widgets/wx_aui_art_providers.h>
55 #include <wx/app.h>
56 #include <wx/config.h>
57 #include <wx/display.h>
58 #include <wx/stdpaths.h>
59 #include <wx/string.h>
60 #include <wx/treebook.h>
61 #include <kiplatform/app.h>
62 #include <kiplatform/ui.h>
63 
64 #include <functional>
65 
66 wxDEFINE_EVENT( UNITS_CHANGED, wxCommandEvent );
67 
68 
69 // Minimum window size
minSize(FRAME_T aFrameType)70 static const wxSize minSize( FRAME_T aFrameType )
71 {
72     switch( aFrameType )
73     {
74     case KICAD_MAIN_FRAME_T:
75         return wxSize( 406, 354 );
76 
77     default:
78         return wxSize( 500, 400 );
79     }
80 }
81 
82 
defaultSize(FRAME_T aFrameType)83 static const wxSize defaultSize( FRAME_T aFrameType )
84 {
85     switch( aFrameType )
86     {
87     case KICAD_MAIN_FRAME_T:
88         return wxSize( 850, 540 );
89 
90     default:
91         return wxSize( 1280, 720 );
92     }
93 }
94 
95 
BEGIN_EVENT_TABLE(EDA_BASE_FRAME,wxFrame)96 BEGIN_EVENT_TABLE( EDA_BASE_FRAME, wxFrame )
97     EVT_MENU( wxID_ABOUT, EDA_BASE_FRAME::OnKicadAbout )
98     EVT_MENU( wxID_PREFERENCES, EDA_BASE_FRAME::OnPreferences )
99 
100     EVT_CHAR_HOOK( EDA_BASE_FRAME::OnCharHook )
101     EVT_MENU_OPEN( EDA_BASE_FRAME::OnMenuEvent )
102     EVT_MENU_CLOSE( EDA_BASE_FRAME::OnMenuEvent )
103     EVT_MENU_HIGHLIGHT_ALL( EDA_BASE_FRAME::OnMenuEvent )
104     EVT_MOVE( EDA_BASE_FRAME::OnMove )
105     EVT_SIZE( EDA_BASE_FRAME::OnSize )
106     EVT_MAXIMIZE( EDA_BASE_FRAME::OnMaximize )
107 
108     EVT_SYS_COLOUR_CHANGED( EDA_BASE_FRAME::onSystemColorChange )
109 END_EVENT_TABLE()
110 
111 
112 void EDA_BASE_FRAME::commonInit( FRAME_T aFrameType )
113 {
114     m_ident             = aFrameType;
115     m_maximizeByDefault = false;
116     m_infoBar           = nullptr;
117     m_settingsManager   = nullptr;
118     m_fileHistory       = nullptr;
119     m_hasAutoSave       = false;
120     m_autoSaveState     = false;
121     m_autoSaveInterval  = -1;
122     m_undoRedoCountMax  = DEFAULT_MAX_UNDO_ITEMS;
123     m_userUnits         = EDA_UNITS::MILLIMETRES;
124     m_isClosing         = false;
125     m_isNonUserClose    = false;
126     m_autoSaveTimer     = new wxTimer( this, ID_AUTO_SAVE_TIMER );
127     m_mruPath           = PATHS::GetDefaultUserProjectsPath();
128     m_frameSize         = defaultSize( aFrameType );
129     m_displayIndex      = -1;
130 
131     m_auimgr.SetArtProvider( new WX_AUI_DOCK_ART() );
132 
133     m_settingsManager = &Pgm().GetSettingsManager();
134 
135     // Set a reasonable minimal size for the frame
136     SetSizeHints( minSize( aFrameType ).x, minSize( aFrameType ).y, -1, -1, -1, -1 );
137 
138     // Store dimensions of the user area of the main window.
139     GetClientSize( &m_frameSize.x, &m_frameSize.y );
140 
141     Connect( ID_AUTO_SAVE_TIMER, wxEVT_TIMER,
142              wxTimerEventHandler( EDA_BASE_FRAME::onAutoSaveTimer ) );
143 
144     // hook wxEVT_CLOSE_WINDOW so we can call SaveSettings().  This function seems
145     // to be called before any other hook for wxCloseEvent, which is necessary.
146     Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_BASE_FRAME::windowClosing ) );
147 
148     initExitKey();
149 }
150 
151 
EDA_BASE_FRAME(FRAME_T aFrameType,KIWAY * aKiway)152 EDA_BASE_FRAME::EDA_BASE_FRAME( FRAME_T aFrameType, KIWAY* aKiway ) :
153         wxFrame(),
154         TOOLS_HOLDER(),
155         KIWAY_HOLDER( aKiway, KIWAY_HOLDER::FRAME )
156 {
157     commonInit( aFrameType );
158 }
159 
160 
EDA_BASE_FRAME(wxWindow * aParent,FRAME_T aFrameType,const wxString & aTitle,const wxPoint & aPos,const wxSize & aSize,long aStyle,const wxString & aFrameName,KIWAY * aKiway)161 EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType,
162                                 const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
163                                 long aStyle, const wxString& aFrameName, KIWAY* aKiway ) :
164         wxFrame( aParent, wxID_ANY, aTitle, aPos, aSize, aStyle, aFrameName ),
165         TOOLS_HOLDER(),
166         KIWAY_HOLDER( aKiway, KIWAY_HOLDER::FRAME )
167 {
168     commonInit( aFrameType );
169 }
170 
171 
findQuasiModalDialog()172 wxWindow* EDA_BASE_FRAME::findQuasiModalDialog()
173 {
174     for( wxWindow* iter : GetChildren() )
175     {
176         DIALOG_SHIM* dlg = dynamic_cast<DIALOG_SHIM*>( iter );
177 
178         if( dlg && dlg->IsQuasiModal() )
179             return dlg;
180     }
181 
182     // FIXME: CvPcb is currently implemented on top of KIWAY_PLAYER rather than DIALOG_SHIM,
183     // so we have to look for it separately.
184     if( m_ident == FRAME_SCH )
185     {
186         wxWindow* cvpcb = wxWindow::FindWindowByName( "CvpcbFrame" );
187 
188         if( cvpcb )
189             return cvpcb;
190     }
191 
192     return nullptr;
193 }
194 
195 
windowClosing(wxCloseEvent & event)196 void EDA_BASE_FRAME::windowClosing( wxCloseEvent& event )
197 {
198     // Don't allow closing when a quasi-modal is open.
199     wxWindow* quasiModal = findQuasiModalDialog();
200 
201     if( quasiModal )
202     {
203         // Raise and notify; don't give the user a warning regarding "quasi-modal dialogs"
204         // when they have no idea what those are.
205         quasiModal->Raise();
206         wxBell();
207 
208         if( event.CanVeto() )
209             event.Veto();
210 
211         return;
212     }
213 
214 
215     if( event.GetId() == wxEVT_QUERY_END_SESSION
216         || event.GetId() == wxEVT_END_SESSION )
217     {
218         // End session means the OS is going to terminate us
219         m_isNonUserClose = true;
220     }
221 
222     if( canCloseWindow( event ) )
223     {
224         m_isClosing = true;
225         APP_SETTINGS_BASE* cfg = config();
226 
227         if( cfg )
228             SaveSettings( cfg );    // virtual, wxFrame specific
229 
230         doCloseWindow();
231 
232         // Destroy (safe delete frame) this frame only in non modal mode.
233         // In modal mode, the caller will call Destroy().
234         if( !IsModal() )
235             Destroy();
236     }
237     else
238     {
239         if( event.CanVeto() )
240             event.Veto();
241     }
242 }
243 
244 
~EDA_BASE_FRAME()245 EDA_BASE_FRAME::~EDA_BASE_FRAME()
246 {
247     delete m_autoSaveTimer;
248     delete m_fileHistory;
249 
250     ClearUndoRedoList();
251 
252     SocketCleanup();
253 
254     KIPLATFORM::APP::RemoveShutdownBlockReason( this );
255 }
256 
257 
ProcessEvent(wxEvent & aEvent)258 bool EDA_BASE_FRAME::ProcessEvent( wxEvent& aEvent )
259 {
260 #ifdef  __WXMAC__
261     // Apple in its infinite wisdom will raise a disabled window before even passing
262     // us the event, so we have no way to stop it.  Instead, we have to catch an
263     // improperly ordered disabled window and quasi-modal dialog here and reorder
264     // them.
265     if( !IsEnabled() && IsActive() )
266     {
267         wxWindow* dlg = findQuasiModalDialog();
268         if( dlg )
269             dlg->Raise();
270     }
271 #endif
272 
273     if( !wxFrame::ProcessEvent( aEvent ) )
274         return false;
275 
276     if( Pgm().m_Quitting )
277         return true;
278 
279     if( !m_isClosing && m_hasAutoSave && IsShown() && IsActive()
280         && m_autoSaveState != isAutoSaveRequired()
281         && m_autoSaveInterval > 0 )
282     {
283         if( !m_autoSaveState )
284         {
285             wxLogTrace( traceAutoSave, wxT( "Starting auto save timer." ) );
286             m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT );
287             m_autoSaveState = true;
288         }
289         else if( m_autoSaveTimer->IsRunning() )
290         {
291             wxLogTrace( traceAutoSave, wxT( "Stopping auto save timer." ) );
292             m_autoSaveTimer->Stop();
293             m_autoSaveState = false;
294         }
295     }
296 
297     return true;
298 }
299 
300 
SetAutoSaveInterval(int aInterval)301 void EDA_BASE_FRAME::SetAutoSaveInterval( int aInterval )
302 {
303     m_autoSaveInterval = aInterval;
304 
305     if( m_autoSaveTimer->IsRunning() )
306     {
307         if( m_autoSaveInterval > 0 )
308         {
309             m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT );
310         }
311         else
312         {
313             m_autoSaveTimer->Stop();
314             m_autoSaveState = false;
315         }
316     }
317 }
318 
319 
onAutoSaveTimer(wxTimerEvent & aEvent)320 void EDA_BASE_FRAME::onAutoSaveTimer( wxTimerEvent& aEvent )
321 {
322     if( !doAutoSave() )
323         m_autoSaveTimer->Start( m_autoSaveInterval * 1000, wxTIMER_ONE_SHOT );
324 }
325 
326 
doAutoSave()327 bool EDA_BASE_FRAME::doAutoSave()
328 {
329     wxCHECK_MSG( false, true, wxT( "Auto save timer function not overridden.  Bad programmer!" ) );
330 }
331 
332 
OnCharHook(wxKeyEvent & aKeyEvent)333 void EDA_BASE_FRAME::OnCharHook( wxKeyEvent& aKeyEvent )
334 {
335     wxLogTrace( kicadTraceKeyEvent, "EDA_BASE_FRAME::OnCharHook %s", dump( aKeyEvent ) );
336 
337     // Key events can be filtered here.
338     // Currently no filtering is made.
339     aKeyEvent.Skip();
340 }
341 
342 
OnMenuEvent(wxMenuEvent & aEvent)343 void EDA_BASE_FRAME::OnMenuEvent( wxMenuEvent& aEvent )
344 {
345     if( !m_toolDispatcher )
346         aEvent.Skip();
347     else
348         m_toolDispatcher->DispatchWxEvent( aEvent );
349 }
350 
351 
RegisterUIUpdateHandler(int aID,const ACTION_CONDITIONS & aConditions)352 void EDA_BASE_FRAME::RegisterUIUpdateHandler( int aID, const ACTION_CONDITIONS& aConditions )
353 {
354     UIUpdateHandler evtFunc = std::bind( &EDA_BASE_FRAME::HandleUpdateUIEvent,
355                                          std::placeholders::_1,
356                                          this,
357                                          aConditions );
358 
359     m_uiUpdateMap[aID] = evtFunc;
360 
361     Bind( wxEVT_UPDATE_UI, evtFunc, aID );
362 }
363 
364 
UnregisterUIUpdateHandler(int aID)365 void EDA_BASE_FRAME::UnregisterUIUpdateHandler( int aID )
366 {
367     const auto it = m_uiUpdateMap.find( aID );
368 
369     if( it == m_uiUpdateMap.end() )
370         return;
371 
372     Unbind( wxEVT_UPDATE_UI, it->second, aID );
373 }
374 
375 
HandleUpdateUIEvent(wxUpdateUIEvent & aEvent,EDA_BASE_FRAME * aFrame,ACTION_CONDITIONS aCond)376 void EDA_BASE_FRAME::HandleUpdateUIEvent( wxUpdateUIEvent& aEvent, EDA_BASE_FRAME* aFrame,
377                                           ACTION_CONDITIONS aCond )
378 {
379     bool       checkRes  = false;
380     bool       enableRes = true;
381     bool       showRes   = true;
382     bool       isCut     = aEvent.GetId() == ACTIONS::cut.GetUIId();
383     bool       isCopy    = aEvent.GetId() == ACTIONS::copy.GetUIId();
384     bool       isPaste   = aEvent.GetId() == ACTIONS::paste.GetUIId();
385     SELECTION& selection = aFrame->GetCurrentSelection();
386 
387     try
388     {
389         checkRes  = aCond.checkCondition( selection );
390         enableRes = aCond.enableCondition( selection );
391         showRes   = aCond.showCondition( selection );
392     }
393     catch( std::exception& )
394     {
395         // Something broke with the conditions, just skip the event.
396         aEvent.Skip();
397         return;
398     }
399 
400     if( isCut || isCopy || isPaste )
401     {
402         wxWindow*    focus = wxWindow::FindFocus();
403         wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( focus );
404 
405         if( textEntry && isCut && textEntry->CanCut() )
406             enableRes = true;
407         else if( textEntry && isCopy && textEntry->CanCopy() )
408             enableRes = true;
409         else if( textEntry && isPaste && textEntry->CanPaste() )
410             enableRes = true;
411     }
412 
413     aEvent.Enable( enableRes );
414     aEvent.Show( showRes );
415 
416     // wxWidgets 3.1.5+ includes a field in the event that says if the event supports being
417     // checked, since wxMenuItems don't want to be checked unless they actually are checkable
418 #if wxCHECK_VERSION( 3, 1, 5 )
419     if( aEvent.IsCheckable() )
420         aEvent.Check( checkRes );
421 #else
422     bool canCheck = true;
423 
424     // wxMenuItems don't want to be checked unless they actually are checkable, so we have to
425     // check to see if they can be and can't just universally apply a check in this event.
426     if( auto menu = dynamic_cast<wxMenu*>( aEvent.GetEventObject() ) )
427         canCheck = menu->FindItem( aEvent.GetId() )->IsCheckable();
428 
429     if( canCheck )
430         aEvent.Check( checkRes );
431 #endif
432 }
433 
434 
setupUIConditions()435 void EDA_BASE_FRAME::setupUIConditions()
436 {
437     // Setup the conditions to check a language menu item
438     auto isCurrentLang =
439         [] ( const SELECTION& aSel, int aLangIdentifier )
440         {
441             return Pgm().GetSelectedLanguageIdentifier() == aLangIdentifier;
442         };
443 
444     for( unsigned ii = 0;  LanguagesList[ii].m_KI_Lang_Identifier != 0; ii++ )
445     {
446         ACTION_CONDITIONS cond;
447         cond.Check( std::bind( isCurrentLang, std::placeholders::_1,
448                                LanguagesList[ii].m_WX_Lang_Identifier ) );
449 
450         RegisterUIUpdateHandler( LanguagesList[ii].m_KI_Lang_Identifier, cond );
451     }
452 }
453 
454 
ReCreateMenuBar()455 void EDA_BASE_FRAME::ReCreateMenuBar()
456 {
457 }
458 
459 
AddStandardHelpMenu(wxMenuBar * aMenuBar)460 void EDA_BASE_FRAME::AddStandardHelpMenu( wxMenuBar* aMenuBar )
461 {
462     COMMON_CONTROL* commonControl = m_toolManager->GetTool<COMMON_CONTROL>();
463     ACTION_MENU*    helpMenu = new ACTION_MENU( false, commonControl );
464 
465     helpMenu->Add( ACTIONS::help );
466     helpMenu->Add( ACTIONS::gettingStarted );
467     helpMenu->Add( ACTIONS::listHotKeys );
468     helpMenu->Add( ACTIONS::getInvolved );
469     helpMenu->Add( ACTIONS::donate );
470     helpMenu->Add( ACTIONS::reportBug );
471 
472     helpMenu->AppendSeparator();
473     helpMenu->Add( _( "&About KiCad" ), "", wxID_ABOUT, BITMAPS::about );
474 
475     // Trailing space keeps OSX from hijacking our menu (and disabling everything in it).
476     aMenuBar->Append( helpMenu, _( "&Help" ) + wxS( " " ) );
477 }
478 
479 
ShowChangedLanguage()480 void EDA_BASE_FRAME::ShowChangedLanguage()
481 {
482     TOOLS_HOLDER::ShowChangedLanguage();
483 
484     if( GetMenuBar() )
485     {
486         ReCreateMenuBar();
487         GetMenuBar()->Refresh();
488     }
489 }
490 
491 
CommonSettingsChanged(bool aEnvVarsChanged,bool aTextVarsChanged)492 void EDA_BASE_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
493 {
494     TOOLS_HOLDER::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
495 
496     COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
497 
498     if( m_fileHistory )
499     {
500         int historySize = settings->m_System.file_history_size;
501         m_fileHistory->SetMaxFiles( (unsigned) std::max( 0, historySize ) );
502     }
503 
504     GetBitmapStore()->ThemeChanged();
505     ThemeChanged();
506 
507     if( GetMenuBar() )
508     {
509         // For icons in menus, icon scaling & hotkeys
510         ReCreateMenuBar();
511         GetMenuBar()->Refresh();
512     }
513 }
514 
515 
ThemeChanged()516 void EDA_BASE_FRAME::ThemeChanged()
517 {
518     ClearScaledBitmapCache();
519 
520     // Update all the toolbars to have new icons
521     wxAuiPaneInfoArray panes = m_auimgr.GetAllPanes();
522 
523     for( size_t i = 0; i < panes.GetCount(); ++i )
524     {
525         if( ACTION_TOOLBAR* toolbar = dynamic_cast<ACTION_TOOLBAR*>( panes[i].window ) )
526             toolbar->RefreshBitmaps();
527     }
528 }
529 
530 
OnSize(wxSizeEvent & aEvent)531 void EDA_BASE_FRAME::OnSize( wxSizeEvent& aEvent )
532 {
533 #ifdef __WXMAC__
534     int currentDisplay = wxDisplay::GetFromWindow( this );
535 
536     if( m_displayIndex >= 0 && currentDisplay >= 0 && currentDisplay != m_displayIndex )
537     {
538         wxLogTrace( traceDisplayLocation, "OnSize: current display changed %d to %d",
539                     m_displayIndex, currentDisplay );
540         m_displayIndex = currentDisplay;
541         ensureWindowIsOnScreen();
542     }
543 #endif
544 
545     aEvent.Skip();
546 }
547 
548 
LoadWindowState(const wxString & aFileName)549 void EDA_BASE_FRAME::LoadWindowState( const wxString& aFileName )
550 {
551     if( !Pgm().GetCommonSettings()->m_Session.remember_open_files )
552         return;
553 
554     const PROJECT_FILE_STATE* state = Prj().GetLocalSettings().GetFileState( aFileName );
555 
556     if( state != nullptr )
557     {
558         LoadWindowState( state->window );
559     }
560 }
561 
562 
LoadWindowState(const WINDOW_STATE & aState)563 void EDA_BASE_FRAME::LoadWindowState( const WINDOW_STATE& aState )
564 {
565     bool wasDefault = false;
566 
567     m_framePos.x  = aState.pos_x;
568     m_framePos.y  = aState.pos_y;
569     m_frameSize.x = aState.size_x;
570     m_frameSize.y = aState.size_y;
571 
572     wxLogTrace( traceDisplayLocation, "Config position (%d, %d) with size (%d, %d)",
573                 m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
574 
575     // Ensure minimum size is set if the stored config was zero-initialized
576     if( m_frameSize.x < minSize( m_ident ).x || m_frameSize.y < minSize( m_ident ).y )
577     {
578         m_frameSize = defaultSize( m_ident );
579         wasDefault  = true;
580 
581         wxLogTrace( traceDisplayLocation, "Using minimum size (%d, %d)",
582                     m_frameSize.x, m_frameSize.y );
583     }
584 
585     wxLogTrace( traceDisplayLocation, "Number of displays: %d", wxDisplay::GetCount() );
586 
587     if( aState.display >= wxDisplay::GetCount() )
588     {
589         wxLogTrace( traceDisplayLocation, "Previous display not found" );
590 
591         // If it isn't attached, use the first display
592         // Warning wxDisplay has 2 ctor variants. the parameter needs a type:
593         const unsigned int index = 0;
594         wxDisplay display( index );
595         wxRect    clientSize = display.GetGeometry();
596 
597         m_framePos = wxDefaultPosition;
598 
599         // Ensure the window fits on the display, since the other one could have been larger
600         if( m_frameSize.x > clientSize.width )
601             m_frameSize.x = clientSize.width;
602 
603         if( m_frameSize.y > clientSize.height )
604             m_frameSize.y = clientSize.height;
605     }
606     else
607     {
608         wxPoint upperRight( m_framePos.x + m_frameSize.x, m_framePos.y );
609         wxPoint upperLeft( m_framePos.x, m_framePos.y );
610 
611         wxDisplay display( aState.display );
612         wxRect clientSize = display.GetClientArea();
613 
614         int yLimTop   = clientSize.y;
615         int yLimBottom = clientSize.y + clientSize.height;
616         int xLimLeft  = clientSize.x;
617         int xLimRight = clientSize.x + clientSize.width;
618 
619         if( upperLeft.x  > xLimRight ||  // Upper left corner too close to right edge of screen
620             upperRight.x < xLimLeft  ||  // Upper right corner too close to left edge of screen
621             upperLeft.y < yLimTop ||   // Upper corner too close to the bottom of the screen
622             upperLeft.y > yLimBottom )
623         {
624             m_framePos = wxDefaultPosition;
625             wxLogTrace( traceDisplayLocation, "Resetting to default position" );
626         }
627     }
628 
629     wxLogTrace( traceDisplayLocation, "Final window position (%d, %d) with size (%d, %d)",
630                 m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
631 
632     SetSize( m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
633 
634     // Center the window if we reset to default
635     if( m_framePos.x == -1 )
636     {
637         wxLogTrace( traceDisplayLocation, "Centering window" );
638         Center();
639         m_framePos = GetPosition();
640     }
641 
642     // Record the frame sizes in an un-maximized state
643     m_normalFrameSize = m_frameSize;
644     m_normalFramePos  = m_framePos;
645 
646     // Maximize if we were maximized before
647     if( aState.maximized || ( wasDefault && m_maximizeByDefault ) )
648     {
649         wxLogTrace( traceDisplayLocation, "Maximizing window" );
650         Maximize();
651     }
652 
653     m_displayIndex = wxDisplay::GetFromWindow( this );
654 }
655 
656 
ensureWindowIsOnScreen()657 void EDA_BASE_FRAME::ensureWindowIsOnScreen()
658 {
659     wxDisplay display( wxDisplay::GetFromWindow( this ) );
660     wxRect    clientSize = display.GetClientArea();
661     wxPoint   pos        = GetPosition();
662     wxSize    size       = GetWindowSize();
663 
664     wxLogTrace( traceDisplayLocation,
665                 "ensureWindowIsOnScreen: clientArea (%d, %d) w %d h %d", clientSize.x, clientSize.y,
666                 clientSize.width, clientSize.height );
667 
668     if( pos.y < clientSize.y )
669     {
670         wxLogTrace( traceDisplayLocation,
671                     "ensureWindowIsOnScreen: y pos %d below minimum, setting to %d", pos.y,
672                     clientSize.y );
673         pos.y = clientSize.y;
674     }
675 
676     if( pos.x < clientSize.x )
677     {
678         wxLogTrace( traceDisplayLocation,
679                     "ensureWindowIsOnScreen: x pos %d is off the client rect, setting to %d", pos.x,
680                     clientSize.x );
681         pos.x = clientSize.x;
682     }
683 
684     if( pos.x + size.x - clientSize.x > clientSize.width )
685     {
686         int newWidth = clientSize.width - ( pos.x - clientSize.x );
687         wxLogTrace( traceDisplayLocation,
688                     "ensureWindowIsOnScreen: effective width %d above available %d, setting to %d",
689                     pos.x + size.x, clientSize.width, newWidth );
690         size.x = newWidth;
691     }
692 
693     if( pos.y + size.y - clientSize.y > clientSize.height )
694     {
695         int newHeight = clientSize.height - ( pos.y - clientSize.y );
696         wxLogTrace( traceDisplayLocation,
697                     "ensureWindowIsOnScreen: effective height %d above available %d, setting to %d",
698                     pos.y + size.y, clientSize.height, newHeight );
699         size.y = newHeight;
700     }
701 
702     wxLogTrace( traceDisplayLocation, "Updating window position (%d, %d) with size (%d, %d)",
703                 pos.x, pos.y, size.x, size.y );
704 
705     SetSize( pos.x, pos.y, size.x, size.y );
706 }
707 
708 
LoadWindowSettings(const WINDOW_SETTINGS * aCfg)709 void EDA_BASE_FRAME::LoadWindowSettings( const WINDOW_SETTINGS* aCfg )
710 {
711     LoadWindowState( aCfg->state );
712 
713     if( m_hasAutoSave )
714         m_autoSaveInterval = Pgm().GetCommonSettings()->m_System.autosave_interval;
715 
716     m_perspective = aCfg->perspective;
717     m_mruPath = aCfg->mru_path;
718 
719     TOOLS_HOLDER::CommonSettingsChanged( false, false );
720 }
721 
722 
SaveWindowSettings(WINDOW_SETTINGS * aCfg)723 void EDA_BASE_FRAME::SaveWindowSettings( WINDOW_SETTINGS* aCfg )
724 {
725     wxString        text;
726 
727     if( IsIconized() )
728         return;
729 
730     wxString baseCfgName = ConfigBaseName();
731 
732     // If the window is maximized, we use the saved window size from before it was maximized
733     if( IsMaximized() )
734     {
735         m_framePos  = m_normalFramePos;
736         m_frameSize = m_normalFrameSize;
737     }
738     else
739     {
740         m_frameSize = GetWindowSize();
741         m_framePos  = GetPosition();
742     }
743 
744     aCfg->state.pos_x     = m_framePos.x;
745     aCfg->state.pos_y     = m_framePos.y;
746     aCfg->state.size_x    = m_frameSize.x;
747     aCfg->state.size_y    = m_frameSize.y;
748     aCfg->state.maximized = IsMaximized();
749     aCfg->state.display   = wxDisplay::GetFromWindow( this );
750 
751     wxLogTrace( traceDisplayLocation, "Saving window maximized: %s",
752                 IsMaximized() ? "true" : "false" );
753     wxLogTrace( traceDisplayLocation, "Saving config position (%d, %d) with size (%d, %d)",
754                 m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
755 
756     // TODO(JE) should auto-save in common settings be overwritten by every app?
757     if( m_hasAutoSave )
758         Pgm().GetCommonSettings()->m_System.autosave_interval = m_autoSaveInterval;
759 
760     // Once this is fully implemented, wxAuiManager will be used to maintain
761     // the persistence of the main frame and all it's managed windows and
762     // all of the legacy frame persistence position code can be removed.
763     aCfg->perspective = m_auimgr.SavePerspective().ToStdString();
764 
765     aCfg->mru_path = m_mruPath;
766 }
767 
768 
LoadSettings(APP_SETTINGS_BASE * aCfg)769 void EDA_BASE_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
770 {
771     LoadWindowSettings( GetWindowSettings( aCfg ) );
772 
773     // Get file history size from common settings
774     int fileHistorySize = Pgm().GetCommonSettings()->m_System.file_history_size;
775 
776     // Load the recently used files into the history menu
777     m_fileHistory = new FILE_HISTORY( (unsigned) std::max( 0, fileHistorySize ),
778                                       ID_FILE1, ID_FILE_LIST_CLEAR );
779     m_fileHistory->Load( *aCfg );
780 }
781 
782 
SaveSettings(APP_SETTINGS_BASE * aCfg)783 void EDA_BASE_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
784 {
785     SaveWindowSettings( GetWindowSettings( aCfg ) );
786 
787     bool fileOpen = m_isClosing && m_isNonUserClose;
788 
789     wxString currentlyOpenedFile = GetCurrentFileName();
790 
791     if( Pgm().GetCommonSettings()->m_Session.remember_open_files && !currentlyOpenedFile.IsEmpty() )
792     {
793         wxFileName rfn( currentlyOpenedFile );
794         rfn.MakeRelativeTo( Prj().GetProjectPath() );
795         Prj().GetLocalSettings().SaveFileState( rfn.GetFullPath(), &aCfg->m_Window, fileOpen );
796     }
797 
798     // Save the recently used files list
799     if( m_fileHistory )
800     {
801         // Save the currently opened file in the file history
802         if( !currentlyOpenedFile.IsEmpty() )
803             UpdateFileHistory( currentlyOpenedFile );
804 
805         m_fileHistory->Save( *aCfg );
806     }
807 }
808 
809 
GetWindowSettings(APP_SETTINGS_BASE * aCfg)810 WINDOW_SETTINGS* EDA_BASE_FRAME::GetWindowSettings( APP_SETTINGS_BASE* aCfg )
811 {
812     return &aCfg->m_Window;
813 }
814 
815 
config() const816 APP_SETTINGS_BASE* EDA_BASE_FRAME::config() const
817 {
818     // KICAD_MANAGER_FRAME overrides this
819     return Kiface().KifaceSettings();
820 }
821 
822 
sys_search()823 const SEARCH_STACK& EDA_BASE_FRAME::sys_search()
824 {
825     return Kiface().KifaceSearch();
826 }
827 
828 
help_name()829 wxString EDA_BASE_FRAME::help_name()
830 {
831     return Kiface().GetHelpFileName();
832 }
833 
834 
PrintMsg(const wxString & text)835 void EDA_BASE_FRAME::PrintMsg( const wxString& text )
836 {
837     SetStatusText( text );
838 }
839 
840 
CreateInfoBar()841 void EDA_BASE_FRAME::CreateInfoBar()
842 {
843 #if defined( __WXOSX_MAC__ )
844     m_infoBar = new WX_INFOBAR( GetToolCanvas() );
845 #else
846     m_infoBar = new WX_INFOBAR( this, &m_auimgr );
847 
848     m_auimgr.AddPane( m_infoBar, EDA_PANE().InfoBar().Name( "InfoBar" ).Top().Layer(1) );
849 #endif
850 }
851 
852 
FinishAUIInitialization()853 void EDA_BASE_FRAME::FinishAUIInitialization()
854 {
855 #if defined( __WXOSX_MAC__ )
856     m_auimgr.Update();
857 #else
858     // Call Update() to fix all pane default sizes, especially the "InfoBar" pane before
859     // hiding it.
860     m_auimgr.Update();
861 
862     // We don't want the infobar displayed right away
863     m_auimgr.GetPane( "InfoBar" ).Hide();
864     m_auimgr.Update();
865 #endif
866 }
867 
868 
ShowInfoBarError(const wxString & aErrorMsg,bool aShowCloseButton,WX_INFOBAR::MESSAGE_TYPE aType)869 void EDA_BASE_FRAME::ShowInfoBarError( const wxString& aErrorMsg, bool aShowCloseButton,
870                                        WX_INFOBAR::MESSAGE_TYPE aType )
871 {
872     m_infoBar->RemoveAllButtons();
873 
874     if( aShowCloseButton )
875         m_infoBar->AddCloseButton();
876 
877     GetInfoBar()->ShowMessageFor( aErrorMsg, 8000, wxICON_ERROR, aType );
878 }
879 
880 
ShowInfoBarError(const wxString & aErrorMsg,bool aShowCloseButton,std::function<void (void)> aCallback)881 void EDA_BASE_FRAME::ShowInfoBarError( const wxString& aErrorMsg, bool aShowCloseButton,
882                                        std::function<void(void)> aCallback )
883 {
884     m_infoBar->RemoveAllButtons();
885 
886     if( aShowCloseButton )
887         m_infoBar->AddCloseButton();
888 
889     if( aCallback )
890         m_infoBar->SetCallback( aCallback );
891 
892     GetInfoBar()->ShowMessageFor( aErrorMsg, 6000, wxICON_ERROR );
893 }
894 
895 
ShowInfoBarWarning(const wxString & aWarningMsg,bool aShowCloseButton)896 void EDA_BASE_FRAME::ShowInfoBarWarning( const wxString& aWarningMsg, bool aShowCloseButton )
897 {
898     m_infoBar->RemoveAllButtons();
899 
900     if( aShowCloseButton )
901         m_infoBar->AddCloseButton();
902 
903     GetInfoBar()->ShowMessageFor( aWarningMsg, 6000, wxICON_WARNING );
904 }
905 
906 
ShowInfoBarMsg(const wxString & aMsg,bool aShowCloseButton)907 void EDA_BASE_FRAME::ShowInfoBarMsg( const wxString& aMsg, bool aShowCloseButton )
908 {
909     m_infoBar->RemoveAllButtons();
910 
911     if( aShowCloseButton )
912         m_infoBar->AddCloseButton();
913 
914     GetInfoBar()->ShowMessageFor( aMsg, 8000, wxICON_INFORMATION );
915 }
916 
917 
UpdateFileHistory(const wxString & FullFileName,FILE_HISTORY * aFileHistory)918 void EDA_BASE_FRAME::UpdateFileHistory( const wxString& FullFileName, FILE_HISTORY* aFileHistory )
919 {
920     if( !aFileHistory )
921         aFileHistory = m_fileHistory;
922 
923     wxASSERT( aFileHistory );
924 
925     aFileHistory->AddFileToHistory( FullFileName );
926 
927     // Update the menubar to update the file history menu
928     if( !m_isClosing && GetMenuBar() )
929     {
930         ReCreateMenuBar();
931         GetMenuBar()->Refresh();
932     }
933 }
934 
935 
GetFileFromHistory(int cmdId,const wxString & type,FILE_HISTORY * aFileHistory)936 wxString EDA_BASE_FRAME::GetFileFromHistory( int cmdId, const wxString& type,
937                                              FILE_HISTORY* aFileHistory )
938 {
939     if( !aFileHistory )
940         aFileHistory = m_fileHistory;
941 
942     wxASSERT( aFileHistory );
943 
944     int baseId = aFileHistory->GetBaseId();
945 
946     wxASSERT( cmdId >= baseId && cmdId < baseId + (int) aFileHistory->GetCount() );
947 
948     unsigned i = cmdId - baseId;
949 
950     if( i < aFileHistory->GetCount() )
951     {
952         wxString fn = aFileHistory->GetHistoryFile( i );
953 
954         if( wxFileName::FileExists( fn ) )
955         {
956             return fn;
957         }
958         else
959         {
960             DisplayErrorMessage( this, wxString::Format( _( "File '%s' was not found." ), fn ) );
961             aFileHistory->RemoveFileFromHistory( i );
962         }
963     }
964 
965     // Update the menubar to update the file history menu
966     if( GetMenuBar() )
967     {
968         ReCreateMenuBar();
969         GetMenuBar()->Refresh();
970     }
971 
972     return wxEmptyString;
973 }
974 
975 
ClearFileHistory(FILE_HISTORY * aFileHistory)976 void EDA_BASE_FRAME::ClearFileHistory( FILE_HISTORY* aFileHistory )
977 {
978     if( !aFileHistory )
979         aFileHistory = m_fileHistory;
980 
981     wxASSERT( aFileHistory );
982 
983     aFileHistory->ClearFileHistory();
984 
985     // Update the menubar to update the file history menu
986     if( GetMenuBar() )
987     {
988         ReCreateMenuBar();
989         GetMenuBar()->Refresh();
990     }
991 }
992 
993 
OnKicadAbout(wxCommandEvent & event)994 void EDA_BASE_FRAME::OnKicadAbout( wxCommandEvent& event )
995 {
996     void ShowAboutDialog( EDA_BASE_FRAME * aParent ); // See AboutDialog_main.cpp
997     ShowAboutDialog( this );
998 }
999 
1000 
OnPreferences(wxCommandEvent & event)1001 void EDA_BASE_FRAME::OnPreferences( wxCommandEvent& event )
1002 {
1003     PAGED_DIALOG dlg( this, _( "Preferences" ), true );
1004     wxTreebook* book = dlg.GetTreebook();
1005 
1006     book->AddPage( new PANEL_COMMON_SETTINGS( &dlg, book ), _( "Common" ) );
1007 
1008     book->AddPage( new PANEL_MOUSE_SETTINGS( &dlg, book ), _( "Mouse and Touchpad" ) );
1009 
1010     PANEL_HOTKEYS_EDITOR* hotkeysPanel = new PANEL_HOTKEYS_EDITOR( this, book, false );
1011     book->AddPage( hotkeysPanel, _( "Hotkeys" ) );
1012 
1013     wxWindow* viewer3D = nullptr;
1014 
1015     for( unsigned i = 0; i < KIWAY_PLAYER_COUNT;  ++i )
1016     {
1017         KIWAY_PLAYER* frame = dlg.Kiway().Player( (FRAME_T) i, false );
1018 
1019         if( frame )
1020         {
1021             frame->InstallPreferences( &dlg, hotkeysPanel );
1022 
1023             if( !viewer3D )
1024                 viewer3D = wxFindWindowByName( QUALIFIED_VIEWER3D_FRAMENAME( frame ) );
1025         }
1026     }
1027 
1028     if( viewer3D )
1029         static_cast<EDA_BASE_FRAME*>( viewer3D )->InstallPreferences( &dlg, hotkeysPanel );
1030 
1031     // The Kicad manager frame is not a player so we have to add it by hand
1032     wxWindow* manager = wxFindWindowByName( KICAD_MANAGER_FRAME_NAME );
1033 
1034     if( manager )
1035         static_cast<EDA_BASE_FRAME*>( manager )->InstallPreferences( &dlg, hotkeysPanel );
1036 
1037     for( size_t i = 0; i < book->GetPageCount(); ++i )
1038         book->GetPage( i )->Layout();
1039 
1040     if( dlg.ShowModal() == wxID_OK )
1041         dlg.Kiway().CommonSettingsChanged( false, false );
1042 }
1043 
1044 
IsWritable(const wxFileName & aFileName,bool aVerbose)1045 bool EDA_BASE_FRAME::IsWritable( const wxFileName& aFileName, bool aVerbose )
1046 {
1047     wxString msg;
1048     wxFileName fn = aFileName;
1049 
1050     // Check for absence of a file path with a file name.  Unfortunately KiCad
1051     // uses paths relative to the current project path without the ./ part which
1052     // confuses wxFileName. Making the file name path absolute may be less than
1053     // elegant but it solves the problem.
1054     if( fn.GetPath().IsEmpty() && fn.HasName() )
1055         fn.MakeAbsolute();
1056 
1057     wxCHECK_MSG( fn.IsOk(), false,
1058                  wxT( "File name object is invalid.  Bad programmer!" ) );
1059     wxCHECK_MSG( !fn.GetPath().IsEmpty(), false,
1060                  wxT( "File name object path <" ) + fn.GetFullPath() +
1061                  wxT( "> is not set.  Bad programmer!" ) );
1062 
1063     if( fn.IsDir() && !fn.IsDirWritable() )
1064     {
1065         msg.Printf( _( "Insufficient permissions to folder '%s'." ),
1066                     fn.GetPath() );
1067     }
1068     else if( !fn.FileExists() && !fn.IsDirWritable() )
1069     {
1070         msg.Printf( _( "Insufficient permissions to save file '%s'." ),
1071                     fn.GetFullName(), fn.GetPath() );
1072     }
1073     else if( fn.FileExists() && !fn.IsFileWritable() )
1074     {
1075         msg.Printf( _( "Insufficient permissions to save file '%s'." ),
1076                     fn.GetFullPath() );
1077     }
1078 
1079     if( !msg.IsEmpty() )
1080     {
1081         if( aVerbose )
1082             wxMessageBox( msg );
1083 
1084         return false;
1085     }
1086 
1087     return true;
1088 }
1089 
1090 
CheckForAutoSaveFile(const wxFileName & aFileName)1091 void EDA_BASE_FRAME::CheckForAutoSaveFile( const wxFileName& aFileName )
1092 {
1093     wxCHECK_RET( aFileName.IsOk(), wxT( "Invalid file name!" ) );
1094 
1095     wxFileName autoSaveFileName = aFileName;
1096 
1097     // Check for auto save file.
1098     autoSaveFileName.SetName( GetAutoSaveFilePrefix() + aFileName.GetName() );
1099 
1100     wxLogTrace( traceAutoSave,
1101                 wxT( "Checking for auto save file " ) + autoSaveFileName.GetFullPath() );
1102 
1103     if( !autoSaveFileName.FileExists() )
1104         return;
1105 
1106     wxString msg = wxString::Format( _(
1107             "Well this is potentially embarrassing!\n"
1108             "It appears that the last time you were editing the file\n"
1109             "%s\n"
1110             "it was not saved properly.  Do you wish to restore the last saved edits you made?" ),
1111             aFileName.GetFullName()
1112         );
1113 
1114     int response = wxMessageBox( msg, Pgm().App().GetAppDisplayName(), wxYES_NO | wxICON_QUESTION,
1115                                  this );
1116 
1117     // Make a backup of the current file, delete the file, and rename the auto save file to
1118     // the file name.
1119     if( response == wxYES )
1120     {
1121         if( !wxRenameFile( autoSaveFileName.GetFullPath(), aFileName.GetFullPath() ) )
1122         {
1123             wxMessageBox( _( "The auto save file could not be renamed to the board file name." ),
1124                           Pgm().App().GetAppDisplayName(), wxOK | wxICON_EXCLAMATION, this );
1125         }
1126     }
1127     else
1128     {
1129         wxLogTrace( traceAutoSave,
1130                     wxT( "Removing auto save file " ) + autoSaveFileName.GetFullPath() );
1131 
1132         // Remove the auto save file when using the previous file as is.
1133         wxRemoveFile( autoSaveFileName.GetFullPath() );
1134     }
1135 }
1136 
1137 
IsContentModified() const1138 bool EDA_BASE_FRAME::IsContentModified() const
1139 {
1140     // This function should be overridden in child classes
1141     return false;
1142 }
1143 
1144 
initExitKey()1145 void EDA_BASE_FRAME::initExitKey()
1146 {
1147     wxAcceleratorEntry entries[1];
1148     entries[0].Set( wxACCEL_CTRL, int( 'Q' ), wxID_EXIT );
1149     wxAcceleratorTable accel( 1, entries );
1150     SetAcceleratorTable( accel );
1151 }
1152 
1153 
ClearUndoRedoList()1154 void EDA_BASE_FRAME::ClearUndoRedoList()
1155 {
1156     ClearUndoORRedoList( UNDO_LIST );
1157     ClearUndoORRedoList( REDO_LIST );
1158 }
1159 
1160 
PushCommandToUndoList(PICKED_ITEMS_LIST * aNewitem)1161 void EDA_BASE_FRAME::PushCommandToUndoList( PICKED_ITEMS_LIST* aNewitem )
1162 {
1163     m_undoList.PushCommand( aNewitem );
1164 
1165     // Delete the extra items, if count max reached
1166     if( m_undoRedoCountMax > 0 )
1167     {
1168         int extraitems = GetUndoCommandCount() - m_undoRedoCountMax;
1169 
1170         if( extraitems > 0 )
1171             ClearUndoORRedoList( UNDO_LIST, extraitems );
1172     }
1173 }
1174 
1175 
PushCommandToRedoList(PICKED_ITEMS_LIST * aNewitem)1176 void EDA_BASE_FRAME::PushCommandToRedoList( PICKED_ITEMS_LIST* aNewitem )
1177 {
1178     m_redoList.PushCommand( aNewitem );
1179 
1180     // Delete the extra items, if count max reached
1181     if( m_undoRedoCountMax > 0 )
1182     {
1183         int extraitems = GetRedoCommandCount() - m_undoRedoCountMax;
1184 
1185         if( extraitems > 0 )
1186             ClearUndoORRedoList( REDO_LIST, extraitems );
1187     }
1188 }
1189 
1190 
PopCommandFromUndoList()1191 PICKED_ITEMS_LIST* EDA_BASE_FRAME::PopCommandFromUndoList( )
1192 {
1193     return m_undoList.PopCommand();
1194 }
1195 
1196 
PopCommandFromRedoList()1197 PICKED_ITEMS_LIST* EDA_BASE_FRAME::PopCommandFromRedoList( )
1198 {
1199     return m_redoList.PopCommand();
1200 }
1201 
1202 
ChangeUserUnits(EDA_UNITS aUnits)1203 void EDA_BASE_FRAME::ChangeUserUnits( EDA_UNITS aUnits )
1204 {
1205     SetUserUnits( aUnits );
1206     unitsChangeRefresh();
1207 
1208     wxCommandEvent e( UNITS_CHANGED );
1209     ProcessEventLocally( e );
1210 }
1211 
1212 
OnMaximize(wxMaximizeEvent & aEvent)1213 void EDA_BASE_FRAME::OnMaximize( wxMaximizeEvent& aEvent )
1214 {
1215     // When we maximize the window, we want to save the old information
1216     // so that we can add it to the settings on next window load.
1217     // Contrary to the documentation, this event seems to be generated
1218     // when the window is also being unmaximized on OSX, so we only
1219     // capture the size information when we maximize the window when on OSX.
1220 #ifdef __WXOSX__
1221     if( !IsMaximized() )
1222 #endif
1223     {
1224         m_normalFrameSize = GetWindowSize();
1225         m_normalFramePos  = GetPosition();
1226         wxLogTrace( traceDisplayLocation,
1227                     "Maximizing window - Saving position (%d, %d) with size (%d, %d)",
1228                     m_normalFramePos.x, m_normalFramePos.y,
1229                     m_normalFrameSize.x, m_normalFrameSize.y );
1230     }
1231 
1232     // Skip event to actually maximize the window
1233     aEvent.Skip();
1234 }
1235 
1236 
GetWindowSize()1237 wxSize EDA_BASE_FRAME::GetWindowSize()
1238 {
1239 #ifdef __WXGTK__
1240     wxSize winSize = GetSize();
1241 
1242     // GTK includes the window decorations in the normal GetSize call,
1243     // so we have to use a GTK-specific sizing call that returns the
1244     // non-decorated window size.
1245     if( m_ident == KICAD_MAIN_FRAME_T )
1246     {
1247         int width  = 0;
1248         int height = 0;
1249         GTKDoGetSize( &width, &height );
1250 
1251         winSize.Set( width, height );
1252     }
1253 #else
1254     wxSize winSize = GetSize();
1255 #endif
1256 
1257     return winSize;
1258 }
1259 
1260 
HandleSystemColorChange()1261 void EDA_BASE_FRAME::HandleSystemColorChange()
1262 {
1263     // Update the icon theme when the system theme changes and update the toolbars
1264     GetBitmapStore()->ThemeChanged();
1265     ThemeChanged();
1266 
1267     // This isn't handled by ThemeChanged()
1268     if( GetMenuBar() )
1269     {
1270         // For icons in menus, icon scaling & hotkeys
1271         ReCreateMenuBar();
1272         GetMenuBar()->Refresh();
1273     }
1274 }
1275 
1276 
onSystemColorChange(wxSysColourChangedEvent & aEvent)1277 void EDA_BASE_FRAME::onSystemColorChange( wxSysColourChangedEvent& aEvent )
1278 {
1279     // Call the handler to update the colors used in the frame
1280     HandleSystemColorChange();
1281 
1282     // Skip the change event to ensure the rest of the window controls get it
1283     aEvent.Skip();
1284 }
1285 
1286 
1287 #ifdef _WIN32
MSWWindowProc(WXUINT message,WXWPARAM wParam,WXLPARAM lParam)1288 WXLRESULT EDA_BASE_FRAME::MSWWindowProc( WXUINT message, WXWPARAM wParam, WXLPARAM lParam )
1289 {
1290     // This will help avoid the menu keeping focus when the alt key is released
1291     // You can still trigger accelerators as long as you hold down alt
1292     if( message == WM_SYSCOMMAND )
1293         if( wParam == SC_KEYMENU && ( lParam >> 16 ) <= 0 )
1294             return 0;
1295 
1296     return wxFrame::MSWWindowProc( message, wParam, lParam );
1297 }
1298 #endif
1299