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