KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eda_base_frame.cpp
Go to the documentation of this file.
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 <[email protected]>
6 * Copyright (C) 2023 CERN (www.cern.ch)
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
24#include "kicad_manager_frame.h"
25#include <eda_base_frame.h>
26#include <nlohmann/json.hpp>
27
28#include <advanced_config.h>
29#include <api/api_server.h>
30#include <bitmaps.h>
31#include <bitmap_store.h>
32#include <dialog_shim.h>
40#include <file_history.h>
41#include <id.h>
42#include <kiface_base.h>
43#include <hotkeys_basic.h>
45#include <paths.h>
46#include <local_history.h>
47#include <confirm.h>
49#include <pgm_base.h>
50#include <scoped_set_reset.h>
55#include <tool/action_manager.h>
56#include <tool/action_menu.h>
57#include <tool/action_toolbar.h>
58#include <tool/actions.h>
59#include <tool/common_control.h>
60#include <tool/tool_manager.h>
63#include <trace_helpers.h>
66#include <widgets/wx_infobar.h>
69#include <widgets/wx_grid.h>
70#include <widgets/wx_treebook.h>
71#include <wx/app.h>
72#include <wx/config.h>
73#include <wx/display.h>
74#include <wx/stdpaths.h>
75#include <wx/string.h>
76#include <wx/msgdlg.h>
77#include <wx/wupdlock.h>
78#include <kiplatform/app.h>
79#include <kiplatform/io.h>
80#include <kiplatform/ui.h>
81
82#include <nlohmann/json.hpp>
83
84#include <functional>
85#include <kiface_ids.h>
86
87
88
89// Minimum window size
90static const wxSize minSizeLookup( FRAME_T aFrameType, wxWindow* aWindow )
91{
92 switch( aFrameType )
93 {
95 return wxWindow::FromDIP( wxSize( 406, 354 ), aWindow );
96
97 default:
98 return wxWindow::FromDIP( wxSize( 500, 400 ), aWindow );
99 }
100}
101
102
103static const wxSize defaultSize( FRAME_T aFrameType, wxWindow* aWindow )
104{
105 switch( aFrameType )
106 {
108 return wxWindow::FromDIP( wxSize( 850, 540 ), aWindow );
109
110 default:
111 return wxWindow::FromDIP( wxSize( 1280, 720 ), aWindow );
112 }
113}
114
115
116BEGIN_EVENT_TABLE( EDA_BASE_FRAME, wxFrame )
117 // These event table entries are needed to handle events from the mac application menu
119 EVT_MENU( wxID_PREFERENCES, EDA_BASE_FRAME::OnPreferences )
120
121 EVT_CHAR_HOOK( EDA_BASE_FRAME::OnCharHook )
122 EVT_MENU_OPEN( EDA_BASE_FRAME::OnMenuEvent )
123 EVT_MENU_CLOSE( EDA_BASE_FRAME::OnMenuEvent )
124 EVT_MENU_HIGHLIGHT_ALL( EDA_BASE_FRAME::OnMenuEvent )
125 EVT_MOVE( EDA_BASE_FRAME::OnMove )
126 EVT_SIZE( EDA_BASE_FRAME::OnSize )
127 EVT_MAXIMIZE( EDA_BASE_FRAME::OnMaximize )
128
129 EVT_SYS_COLOUR_CHANGED( EDA_BASE_FRAME::onSystemColorChange )
130 EVT_ICONIZE( EDA_BASE_FRAME::onIconize )
131
133 END_EVENT_TABLE()
134
135
137{
138 m_ident = aFrameType;
139 m_maximizeByDefault = false;
140 m_infoBar = nullptr;
141 m_settingsManager = nullptr;
142 m_fileHistory = nullptr;
143 m_supportsAutoSave = false;
144 m_autoSavePending = false;
146 m_isClosing = false;
147 m_closeInProgress = false;
148 m_isNonUserClose = false;
149 m_autoSaveTimer = new wxTimer( this, ID_AUTO_SAVE_TIMER );
150 m_autoSaveRequired = false;
153 m_frameSize = defaultSize( aFrameType, this );
154 m_displayIndex = -1;
155
156 m_auimgr.SetArtProvider( new WX_AUI_DOCK_ART() );
157
159
160 // Set a reasonable minimal size for the frame
161 wxSize minSize = minSizeLookup( aFrameType, this );
162 SetSizeHints( minSize.x, minSize.y, -1, -1, -1, -1 );
163
164 // Store dimensions of the user area of the main window.
165 GetClientSize( &m_frameSize.x, &m_frameSize.y );
166
167 Connect( ID_AUTO_SAVE_TIMER, wxEVT_TIMER, wxTimerEventHandler( EDA_BASE_FRAME::onAutoSaveTimer ) );
168
169 // hook wxEVT_CLOSE_WINDOW so we can call SaveSettings(). This function seems
170 // to be called before any other hook for wxCloseEvent, which is necessary.
171 Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_BASE_FRAME::windowClosing ) );
172
173 KIPLATFORM::UI::SetWMClass( this, Pgm().GetDesktopAppId() );
174
175 initExitKey();
176}
177
178
179EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType, const wxString& aTitle,
180 const wxPoint& aPos, const wxSize& aSize, long aStyle,
181 const wxString& aFrameName, KIWAY* aKiway,
182 const EDA_IU_SCALE& aIuScale ) :
183 wxFrame( aParent, wxID_ANY, aTitle, aPos, aSize, aStyle, aFrameName ),
184 TOOLS_HOLDER(),
185 KIWAY_HOLDER( aKiway, KIWAY_HOLDER::FRAME ),
186 UNITS_PROVIDER( aIuScale, EDA_UNITS::MM )
187{
188 m_tbTopMain = nullptr;
189 m_tbTopAux = nullptr;
190 m_tbRight = nullptr;
191 m_tbLeft = nullptr;
193
194 commonInit( aFrameType );
195
196 Bind( wxEVT_DPI_CHANGED,
197 [&]( wxDPIChangedEvent& aEvent )
198 {
199#ifdef __WXMSW__
200 // Workaround to update toolbar sizes on MSW
201 if( m_auimgr.GetManagedWindow() )
202 {
203 wxAuiPaneInfoArray& panes = m_auimgr.GetAllPanes();
204
205 for( size_t ii = 0; ii < panes.GetCount(); ii++ )
206 {
207 wxAuiPaneInfo& pinfo = panes.Item( ii );
208 pinfo.best_size = pinfo.window->GetSize();
209
210 // But we still shouldn't make it too small.
211 pinfo.best_size.IncTo( pinfo.window->GetBestSize() );
212 pinfo.best_size.IncTo( pinfo.min_size );
213 }
214
215 m_auimgr.Update();
216 }
217#endif
218
219 aEvent.Skip();
220 } );
221}
222
223
224wxWindow* findQuasiModalDialog( wxWindow* aParent )
225{
226 for( wxWindow* child : aParent->GetChildren() )
227 {
228 if( DIALOG_SHIM* dlg = dynamic_cast<DIALOG_SHIM*>( child ) )
229 {
230 if( dlg->IsQuasiModal() )
231 return dlg;
232
233 if( wxWindow* nestedDlg = findQuasiModalDialog( child ) )
234 return nestedDlg;
235 }
236 }
237
238 return nullptr;
239}
240
241
243{
244 if( wxWindow* dlg = ::findQuasiModalDialog( this ) )
245 return dlg;
246
247 // FIXME: CvPcb is currently implemented on top of KIWAY_PLAYER rather than DIALOG_SHIM,
248 // so we have to look for it separately.
249 if( m_ident == FRAME_SCH )
250 {
251 wxWindow* cvpcb = wxWindow::FindWindowByName( wxS( "CvpcbFrame" ) );
252
253 if( cvpcb )
254 return cvpcb;
255 }
256
257 return nullptr;
258}
259
260
261void EDA_BASE_FRAME::windowClosing( wxCloseEvent& event )
262{
263 // Guard against re-entrant close events. GTK can deliver a second wxEVT_CLOSE_WINDOW
264 // while we are still processing the first one (e.g. during Destroy() calls), which leads
265 // to use-after-free crashes when child objects have already been torn down.
266 if( m_isClosing )
267 return;
268
269 // The unsaved-changes prompt in canCloseWindow() pumps messages, so a second close event
270 // (queued title-bar click, Alt+F4 repeat, session end) can arrive while the first close is
271 // still deciding. m_isClosing is not set until canCloseWindow() succeeds, so without this
272 // guard the second event would run the entire prompt and teardown re-entrantly and the
273 // first close would then resume against a demolished frame.
274 //
275 // A non-vetoable session end that lands during the prompt is dropped here rather than run
276 // re-entrantly. That trades a rare failure to persist settings on forced logoff for not
277 // crashing; the durable fix keeps the close off the OS default-window-proc stack entirely and
278 // needs Windows verification.
280 {
281 if( event.CanVeto() )
282 event.Veto();
283
284 return;
285 }
286
287 // Don't allow closing when a quasi-modal is open.
288 wxWindow* quasiModal = findQuasiModalDialog();
289
290 if( quasiModal )
291 {
292 // Raise and notify; don't give the user a warning regarding "quasi-modal dialogs"
293 // when they have no idea what those are.
294 quasiModal->Raise();
295 wxBell();
296
297 if( event.CanVeto() )
298 event.Veto();
299
300 return;
301 }
302
303
304 if( event.GetId() == wxEVT_QUERY_END_SESSION || event.GetId() == wxEVT_END_SESSION )
305 {
306 // End session means the OS is going to terminate us
307 m_isNonUserClose = true;
308 }
309
310 SCOPED_SET_RESET<bool> closeGuard( m_closeInProgress, true );
311
312 if( canCloseWindow( event ) )
313 {
314 m_isClosing = true;
315
316 if( m_infoBar )
317 m_infoBar->Dismiss();
318
319 APP_SETTINGS_BASE* cfg = config();
320
321 if( cfg )
322 SaveSettings( cfg ); // virtual, wxFrame specific
323
325
326 // Destroy (safe delete frame) this frame only in non modal mode.
327 // In modal mode, the caller will call Destroy().
328 if( !IsModal() )
329 Destroy();
330 }
331 else
332 {
333 if( event.CanVeto() )
334 event.Veto();
335 }
336}
337
338
340{
341 Disconnect( ID_AUTO_SAVE_TIMER, wxEVT_TIMER, wxTimerEventHandler( EDA_BASE_FRAME::onAutoSaveTimer ) );
342 Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_BASE_FRAME::windowClosing ) );
343
344 delete m_autoSaveTimer;
345 delete m_fileHistory;
346
348
350}
351
352
353bool EDA_BASE_FRAME::ProcessEvent( wxEvent& aEvent )
354{
355#ifdef __WXMAC__
356 // Apple in its infinite wisdom will raise a disabled window before even passing
357 // us the event, so we have no way to stop it. Instead, we have to catch an
358 // improperly ordered disabled window and quasi-modal dialog here and reorder
359 // them.
360 if( !IsEnabled() && IsActive() )
361 {
362 wxWindow* dlg = findQuasiModalDialog();
363
364 if( dlg )
365 dlg->Raise();
366 }
367#endif
368
369#ifdef __WXMSW__
370 // When changing DPI to a lower value, somehow, called from wxNonOwnedWindow::HandleDPIChange,
371 // our sizers compute a min size that is larger than the old frame size. wx then sets this wrong size.
372 // This shouldn't be needed since the OS have already sent a size event.
373 // Avoid this wx behaviour by pretending we've processed the event even if we use Skip in handlers.
374 if( aEvent.GetEventType() == wxEVT_DPI_CHANGED )
375 {
376 wxFrame::ProcessEvent( aEvent );
377 return true;
378 }
379#endif
380
381 if( !wxFrame::ProcessEvent( aEvent ) )
382 return false;
383
384 if( Pgm().m_Quitting )
385 return true;
386
387 if( !m_isClosing && m_supportsAutoSave && IsShownOnScreen() && IsActive()
389 && GetAutoSaveInterval() > 0 )
390 {
391 if( !m_autoSavePending )
392 {
393 wxLogTrace( traceAutoSave, wxT( "Starting auto save timer." ) );
394 m_autoSaveTimer->Start( GetAutoSaveInterval() * 1000, wxTIMER_ONE_SHOT );
395 m_autoSavePending = true;
396
397 // A fresh cycle starts here (a prior snapshot completed or an explicit save cleared
398 // the pending state), so drop any deferral streak left over from that cycle; otherwise
399 // its stale start time could force the next snapshot to run mid-interaction.
400 m_autoSaveDeferredSince = wxInvalidDateTime;
401 }
402 else if( m_autoSaveTimer->IsRunning() )
403 {
404 wxLogTrace( traceAutoSave, wxT( "Stopping auto save timer." ) );
405 m_autoSaveTimer->Stop();
406 m_autoSavePending = false;
407 }
408 }
409
410 return true;
411}
412
413
418
419
420void EDA_BASE_FRAME::onAutoSaveTimer( wxTimerEvent& aEvent )
421{
422 // Don't stomp on someone else's timer event.
423 if( aEvent.GetId() != ID_AUTO_SAVE_TIMER )
424 {
425 aEvent.Skip();
426 return;
427 }
428
429 // A one-shot tick can already be queued when the frame starts closing; running the saver batch
430 // then would serialize documents whose editors are mid-teardown, so bail once closing begins.
431 if( m_isClosing )
432 return;
433
434 // When the save is deferred (an interactive operation is in progress) keep the timer armed so
435 // a later tick retries. Maintaining m_autoSavePending here preserves the "pending == timer
436 // running" invariant that ProcessEvent() relies on to avoid re-arming the timer on every event.
438 {
439 m_autoSaveTimer->Start( GetAutoSaveInterval() * 1000, wxTIMER_ONE_SHOT );
440 m_autoSavePending = true;
441 }
442 else
443 {
444 m_autoSavePending = false;
445 }
446}
447
448
449static wxString buildRecoveredFileName( const wxFileName& aSrcFn, const wxDateTime& aStamp )
450{
451 wxString stamp = aStamp.IsValid() ? aStamp.Format( wxS( "%Y-%m-%d_%H%M%S" ) ) : wxString( wxS( "unknown-time" ) );
452
453 wxFileName recovered( aSrcFn );
454 recovered.SetName( aSrcFn.GetName() + wxS( ".recovered." ) + stamp );
455
456 int seq = 1;
457
458 while( recovered.FileExists() )
459 recovered.SetName( aSrcFn.GetName() + wxS( ".recovered." ) + stamp + wxString::Format( wxS( ".%d" ), seq++ ) );
460
461 return recovered.GetFullPath();
462}
463
464
465void EDA_BASE_FRAME::CheckForAutosaveFiles( const wxString& aProjectPath, const std::vector<wxString>& aExtensions )
466{
467 auto stale = Kiway().LocalHistory().FindStaleAutosaveFiles( aProjectPath, aExtensions );
468
469 if( stale.empty() )
470 return;
471
472 DIALOG_AUTOSAVE_RECOVERY dlg( this, stale );
473 dlg.ShowModal();
474
475 auto selected = dlg.GetSelectedStale();
476
477 switch( dlg.GetChoice() )
478 {
480 for( const auto& [autosavePath, srcPath] : selected )
481 {
482 if( !wxCopyFile( autosavePath, srcPath, true ) )
483 {
484 wxLogError( _( "Failed to recover auto-saved file '%s'." ), srcPath );
485 continue;
486 }
487
488 wxRemoveFile( autosavePath );
489 }
490 break;
491
493 for( const auto& [autosavePath, srcPath] : selected )
494 {
495 if( wxFileExists( autosavePath ) )
496 wxRemoveFile( autosavePath );
497 }
498 break;
499
501 for( const auto& [autosavePath, srcPath] : selected )
502 {
503 wxFileName autosaveFn( autosavePath );
504 wxFileName srcFn( srcPath );
505 wxDateTime stamp = autosaveFn.FileExists() ? autosaveFn.GetModificationTime() : wxDateTime::Now();
506
507 wxString target = buildRecoveredFileName( srcFn, stamp );
508
509 if( !wxCopyFile( autosavePath, target, true ) )
510 {
511 wxLogError( _( "Failed to write recovered file '%s'." ), target );
512 continue;
513 }
514
515 wxRemoveFile( autosavePath );
516 }
517 break;
518
520 // Leave all autosaves on disk so the dialog can offer them again next open.
521 break;
522 }
523}
524
525
527{
528 // Defer the snapshot if the user is mid-interaction. Serializing a large document on the
529 // UI thread freezes the editor for seconds; deferring keeps the dirty flags set so the
530 // rescheduled timer tick will pick the work up once the operation completes. To avoid
531 // starving the snapshot when the user parks in an interactive tool, the deferral is bounded
532 // and the save is forced once it has been outstanding for longer than the cap.
533 if( !canRunAutoSave() )
534 {
535 wxDateTime now = wxDateTime::Now();
536
537 if( !m_autoSaveDeferredSince.IsValid() )
539
540 wxTimeSpan maxDeferral = wxTimeSpan::Seconds( std::max( 60, GetAutoSaveInterval() * 12 ) );
541
542 if( now - m_autoSaveDeferredSince < maxDeferral )
543 {
544 wxLogTrace( traceAutoSave, wxT( "Deferring auto save; an interactive operation is in progress." ) );
545 return false;
546 }
547
548 wxLogTrace( traceAutoSave, wxT( "Auto save deferral exceeded; saving despite interactive operation." ) );
549 }
550
551 // The deferral is resolved (either the user went idle or the cap forced the snapshot), so the
552 // cycle is now consumed regardless of the saver outcome. The snapshot is best effort: a
553 // droppable cycle (a prior autosave still writing) is recaptured by the next edit's OnModify,
554 // so clear the flags here rather than re-arming on the saver result, which would poll forever
555 // in degenerate states such as no registered savers.
556 m_autoSaveDeferredSince = wxInvalidDateTime;
557 m_autoSaveRequired = false;
558
560
561 // Incremental and zip-autosave both write outside the project tree when the user
562 // selects USER_DIR, so a read-only project is fine in that mode. Only when the
563 // chosen location is the project directory does the project tree need to be writable.
564 if( cs->m_Backup.location == BACKUP_LOCATION::PROJECT_DIR && Prj().IsReadOnly() )
565 return true;
566
567 if( cs->AutosaveUsesLocalHistory() )
568 Kiway().LocalHistory().RunRegisteredSaversAndCommit( Prj().GetProjectPath(), wxS( "Autosave" ) );
569 else
571
572 return true;
573}
574
575
576void EDA_BASE_FRAME::OnCharHook( wxKeyEvent& aKeyEvent )
577{
578 wxLogTrace( kicadTraceKeyEvent, wxS( "EDA_BASE_FRAME::OnCharHook %s" ), dump( aKeyEvent ) );
579
580 // Key events can be filtered here.
581 // Currently no filtering is made.
582 aKeyEvent.Skip();
583}
584
585
586void EDA_BASE_FRAME::OnMenuEvent( wxMenuEvent& aEvent )
587{
588 if( !m_toolDispatcher )
589 aEvent.Skip();
590 else
591 m_toolDispatcher->DispatchWxEvent( aEvent );
592}
593
594
596{
597 // Bind a single wxID_ANY dispatcher on first use rather than one Bind() per action.
598 // wxEvtHandler::SearchDynamicEventTable does a linear scan through all dynamic bindings
599 // for every event dispatch (including mouse motion), so 150 individual bindings cost
600 // O(150) per event regardless of event type. One wxID_ANY binding costs O(1).
602 {
603 Bind( wxEVT_UPDATE_UI, &EDA_BASE_FRAME::onUpdateUI, this );
605 }
606
608 std::placeholders::_1,
609 this,
610 aConditions );
611}
612
613
615{
616 m_uiUpdateMap.erase( aID );
617}
618
619
620void EDA_BASE_FRAME::onUpdateUI( wxUpdateUIEvent& aEvent )
621{
622 const auto it = m_uiUpdateMap.find( aEvent.GetId() );
623
624 if( it != m_uiUpdateMap.end() )
625 it->second( aEvent );
626 else
627 aEvent.Skip();
628}
629
630
631void EDA_BASE_FRAME::HandleUpdateUIEvent( wxUpdateUIEvent& aEvent, EDA_BASE_FRAME* aFrame,
632 ACTION_CONDITIONS& aCond )
633{
634 bool checkRes = false;
635 bool enableRes = true;
636 bool showRes = true;
637 bool isCut = aEvent.GetId() == ACTIONS::cut.GetUIId();
638 bool isCopy = aEvent.GetId() == ACTIONS::copy.GetUIId();
639 bool isPaste = aEvent.GetId() == ACTIONS::paste.GetUIId();
640 SELECTION& selection = aFrame->GetCurrentSelection();
641
642 try
643 {
644 checkRes = aCond.checkCondition( selection );
645 enableRes = aCond.enableCondition( selection );
646 showRes = aCond.showCondition( selection );
647 }
648 catch( std::exception& )
649 {
650 // Something broke with the conditions, just skip the event.
651 aEvent.Skip();
652 return;
653 }
654
655 if( showRes && aEvent.GetId() == ACTIONS::undo.GetUIId() )
656 {
657 wxString msg = _( "Undo" );
658
659 if( enableRes )
660 msg += wxS( " " ) + aFrame->GetUndoActionDescription();
661
662 aEvent.SetText( msg );
663 }
664 else if( showRes && aEvent.GetId() == ACTIONS::redo.GetUIId() )
665 {
666 wxString msg = _( "Redo" );
667
668 if( enableRes )
669 msg += wxS( " " ) + aFrame->GetRedoActionDescription();
670
671 aEvent.SetText( msg );
672 }
673
674 if( isCut || isCopy || isPaste )
675 {
676 wxWindow* focus = wxWindow::FindFocus();
677 wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( focus );
678
679 if( textEntry && isCut && textEntry->CanCut() )
680 enableRes = true;
681 else if( textEntry && isCopy && textEntry->CanCopy() )
682 enableRes = true;
683 else if( textEntry && isPaste && textEntry->CanPaste() )
684 enableRes = true;
685 else if( dynamic_cast<WX_GRID*>( focus ) )
686 enableRes = false; // Must disable menu in order to get command as CharHook event
687 }
688
689 aEvent.Enable( enableRes );
690 aEvent.Show( showRes );
691
692 if( aEvent.IsCheckable() )
693 aEvent.Check( checkRes );
694}
695
696
698{
699 // Setup the conditions to check a language menu item
700 auto isCurrentLang =
701 [] ( const SELECTION& aSel, int aLangIdentifier )
702 {
703 return Pgm().GetSelectedLanguageIdentifier() == aLangIdentifier;
704 };
705
706 for( unsigned ii = 0; LanguagesList[ii].m_KI_Lang_Identifier != 0; ii++ )
707 {
709 cond.Check( std::bind( isCurrentLang, std::placeholders::_1,
710 LanguagesList[ii].m_WX_Lang_Identifier ) );
711 RegisterUIUpdateHandler( LanguagesList[ii].m_KI_Lang_Identifier, cond );
712 }
713}
714
715
717 const ACTION_TOOLBAR_CONTROL_FACTORY& aControlFactory )
718{
719 m_toolbarControlFactories.emplace( aControlDesc.GetName(), aControlFactory );
720}
721
722
724{
725 for( auto& control : m_toolbarControlFactories )
726 {
727 if( control.first == aName )
728 return &control.second;
729 }
730
731 return nullptr;
732}
733
734
738
739
741{
742 if( m_tbLeft )
743 m_tbLeft->SelectAction( aAction );
744
745 if( m_tbTopMain )
746 m_tbTopMain->SelectAction( aAction );
747
748 if( m_tbTopAux )
749 m_tbTopAux->SelectAction( aAction );
750
751 if( m_tbRight )
752 m_tbRight->SelectAction( aAction );
753}
754
755
757{
758 wxWindowUpdateLocker dummy( this );
759
760 wxASSERT( m_toolbarSettings );
761
762 if( m_tbRight )
763 m_tbRight->ClearToolbar();
764
765 if( m_tbLeft )
766 m_tbLeft->ClearToolbar();
767
768 if( m_tbTopMain )
769 m_tbTopMain->ClearToolbar();
770
771 if( m_tbTopAux )
772 m_tbTopAux->ClearToolbar();
773
774 std::optional<TOOLBAR_CONFIGURATION> tbConfig;
775
776 // Drawing tools (typically on right edge of window)
777 tbConfig = m_toolbarSettings->GetToolbarConfig( TOOLBAR_LOC::RIGHT, config()->m_CustomToolbars );
778
779 if( tbConfig.has_value() )
780 {
781 if( !m_tbRight )
782 {
783 m_tbRight = new ACTION_TOOLBAR( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
784 KICAD_AUI_TB_STYLE | wxAUI_TB_VERTICAL | wxAUI_TB_TEXT
785 | wxAUI_TB_OVERFLOW );
786 m_tbRight->SetAuiManager( &m_auimgr );
787 }
788
789 m_tbRight->ApplyConfiguration( tbConfig.value() );
790 }
791
792 // Options (typically on left edge of window)
793 tbConfig = m_toolbarSettings->GetToolbarConfig( TOOLBAR_LOC::LEFT, config()->m_CustomToolbars );
794
795 if( tbConfig.has_value() )
796 {
797 if( !m_tbLeft )
798 {
799 m_tbLeft = new ACTION_TOOLBAR( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
800 KICAD_AUI_TB_STYLE | wxAUI_TB_VERTICAL | wxAUI_TB_TEXT | wxAUI_TB_OVERFLOW );
801 m_tbLeft->SetAuiManager( &m_auimgr );
802 }
803
804 m_tbLeft->ApplyConfiguration( tbConfig.value() );
805 }
806
807 // Top main toolbar (the top one)
808 tbConfig = m_toolbarSettings->GetToolbarConfig( TOOLBAR_LOC::TOP_MAIN, config()->m_CustomToolbars );
809
810 if( tbConfig.has_value() )
811 {
812 if( !m_tbTopMain )
813 {
814 m_tbTopMain = new ACTION_TOOLBAR( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
815 KICAD_AUI_TB_STYLE | wxAUI_TB_HORZ_LAYOUT | wxAUI_TB_HORIZONTAL
816 | wxAUI_TB_TEXT | wxAUI_TB_OVERFLOW );
817 m_tbTopMain->SetAuiManager( &m_auimgr );
818 }
819
820 m_tbTopMain->ApplyConfiguration( tbConfig.value() );
821 }
822
823 // Top aux toolbar (the bottom one)
824 tbConfig = m_toolbarSettings->GetToolbarConfig( TOOLBAR_LOC::TOP_AUX, config()->m_CustomToolbars );
825
826 if( tbConfig.has_value() )
827 {
828 if( !m_tbTopAux )
829 {
830 m_tbTopAux = new ACTION_TOOLBAR( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
831 KICAD_AUI_TB_STYLE | wxAUI_TB_HORZ_LAYOUT | wxAUI_TB_HORIZONTAL
832 | wxAUI_TB_TEXT | wxAUI_TB_OVERFLOW );
833 m_tbTopAux->SetAuiManager( &m_auimgr );
834 }
835
836 m_tbTopAux->ApplyConfiguration( tbConfig.value() );
837 }
838}
839
840
842{
843 if( m_tbTopMain )
844 m_tbTopMain->UpdateControlWidths();
845
846 if( m_tbRight )
847 m_tbRight->UpdateControlWidths();
848
849 if( m_tbLeft )
850 m_tbLeft->UpdateControlWidths();
851
852 if( m_tbTopAux )
853 m_tbTopAux->UpdateControlWidths();
854
855}
856
857
859{
860 if( m_tbTopMain )
861 m_auimgr.GetPane( m_tbTopMain ).MaxSize( m_tbTopMain->GetSize() );
862
863 if( m_tbRight )
864 m_auimgr.GetPane( m_tbRight ).MaxSize( m_tbRight->GetSize() );
865
866 if( m_tbLeft )
867 m_auimgr.GetPane( m_tbLeft ).MaxSize( m_tbLeft->GetSize() );
868
869 if( m_tbTopAux )
870 m_auimgr.GetPane( m_tbTopAux ).MaxSize( m_tbTopAux->GetSize() );
871
872 m_auimgr.Update();
873}
874
875
877{
884
885 CallAfter( [this]()
886 {
887 if( !m_isClosing )
889 } );
890}
891
892
893void EDA_BASE_FRAME::AddStandardHelpMenu( wxMenuBar* aMenuBar )
894{
895 COMMON_CONTROL* commonControl = m_toolManager->GetTool<COMMON_CONTROL>();
896 ACTION_MENU* helpMenu = new ACTION_MENU( false, commonControl );
897
898 helpMenu->Add( ACTIONS::help );
899 helpMenu->Add( ACTIONS::gettingStarted );
900 helpMenu->Add( ACTIONS::listHotKeys );
901 helpMenu->Add( ACTIONS::getInvolved );
902 helpMenu->Add( ACTIONS::donate );
903 helpMenu->Add( ACTIONS::reportBug );
904
905 helpMenu->AppendSeparator();
906 helpMenu->Add( ACTIONS::about );
907
908 aMenuBar->Append( helpMenu, _( "&Help" ) );
909}
910
911
912
914{
915 wxString menuItemLabel = aAction.GetMenuLabel();
916 wxMenuBar* menuBar = GetMenuBar();
917
918 for( size_t ii = 0; ii < menuBar->GetMenuCount(); ++ii )
919 {
920 for( wxMenuItem* menuItem : menuBar->GetMenu( ii )->GetMenuItems() )
921 {
922 if( menuItem->GetItemLabelText() == menuItemLabel )
923 {
924 wxString menuTitleLabel = menuBar->GetMenuLabelText( ii );
925
926 menuTitleLabel.Replace( wxS( "&" ), wxS( "&&" ) );
927 menuItemLabel.Replace( wxS( "&" ), wxS( "&&" ) );
928
929 return wxString::Format( _( "Run: %s > %s" ),
930 menuTitleLabel,
931 menuItemLabel );
932 }
933 }
934 }
935
936 return wxString::Format( _( "Run: %s" ), aAction.GetFriendlyName() );
937};
938
939
941{
943
944 if( GetMenuBar() )
945 {
947 GetMenuBar()->Refresh();
948 }
949}
950
951
953{
955
956 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
957
958 bool running = Pgm().GetApiServer().Running();
959
960 if( running && !settings->m_Api.enable_server )
961 Pgm().GetApiServer().Stop();
962 else if( !running && settings->m_Api.enable_server )
963 Pgm().GetApiServer().Start();
964
965 if( m_fileHistory )
966 {
967 int historySize = settings->m_System.file_history_size;
968 m_fileHistory->SetMaxFiles( (unsigned) std::max( 0, historySize ) );
969 }
970
971 if( Pgm().GetCommonSettings()->AutosaveUsesLocalHistory() )
972 Kiway().LocalHistory().Init( Prj().GetProjectPath() );
973
975 ThemeChanged();
976
977 if( GetMenuBar() )
978 {
979 // For icons in menus, icon scaling & hotkeys
981 GetMenuBar()->Refresh();
982 }
983
984 // Update the toolbars
986}
987
988
990{
992
993 // Update all the toolbars to have new icons
994 wxAuiPaneInfoArray panes = m_auimgr.GetAllPanes();
995
996 for( size_t i = 0; i < panes.GetCount(); ++i )
997 {
998 if( ACTION_TOOLBAR* toolbar = dynamic_cast<ACTION_TOOLBAR*>( panes[i].window ) )
999 toolbar->RefreshBitmaps();
1000 }
1001}
1002
1003
1004void EDA_BASE_FRAME::OnSize( wxSizeEvent& aEvent )
1005{
1006#ifdef __WXMAC__
1007 int currentDisplay = wxDisplay::GetFromWindow( this );
1008
1009 if( m_displayIndex >= 0 && currentDisplay >= 0 && currentDisplay != m_displayIndex )
1010 {
1011 wxLogTrace( traceDisplayLocation, wxS( "OnSize: current display changed %d to %d" ),
1012 m_displayIndex, currentDisplay );
1013 m_displayIndex = currentDisplay;
1015 }
1016#endif
1017
1018 aEvent.Skip();
1019}
1020
1021
1022void EDA_BASE_FRAME::LoadWindowState( const wxString& aFileName )
1023{
1024 if( !Pgm().GetCommonSettings()->m_Session.remember_open_files )
1025 return;
1026
1027 if( const PROJECT_FILE_STATE* state = Prj().GetLocalSettings().GetFileState( aFileName ) )
1028 LoadWindowState( state->window );
1029}
1030
1031
1033{
1034 bool wasDefault = false;
1035
1036 m_framePos.x = aState.pos_x;
1037 m_framePos.y = aState.pos_y;
1038 m_frameSize.x = aState.size_x;
1039 m_frameSize.y = aState.size_y;
1040
1041 wxLogTrace( traceDisplayLocation, wxS( "Config position (%d, %d) with size (%d, %d)" ),
1043
1044 // Ensure minimum size is set if the stored config was zero-initialized
1045 wxSize minSize = minSizeLookup( m_ident, this );
1046
1047 if( m_frameSize.x < minSize.x || m_frameSize.y < minSize.y )
1048 {
1049 m_frameSize = defaultSize( m_ident, this );
1050 wasDefault = true;
1051
1052 wxLogTrace( traceDisplayLocation, wxS( "Using minimum size (%d, %d)" ), m_frameSize.x, m_frameSize.y );
1053 }
1054
1055 wxLogTrace( traceDisplayLocation, wxS( "Number of displays: %d" ), wxDisplay::GetCount() );
1056
1057 if( aState.display >= wxDisplay::GetCount() )
1058 {
1059 wxLogTrace( traceDisplayLocation, wxS( "Previous display not found" ) );
1060
1061 // If it isn't attached, use the first display
1062 // Warning wxDisplay has 2 ctor variants. the parameter needs a type:
1063 const unsigned int index = 0;
1064 wxDisplay display( index );
1065 wxRect clientSize = display.GetGeometry();
1066
1067 m_framePos = wxDefaultPosition;
1068
1069 // Ensure the window fits on the display, since the other one could have been larger
1070 if( m_frameSize.x > clientSize.width )
1071 m_frameSize.x = clientSize.width;
1072
1073 if( m_frameSize.y > clientSize.height )
1074 m_frameSize.y = clientSize.height;
1075 }
1076 else
1077 {
1078 wxPoint upperRight( m_framePos.x + m_frameSize.x, m_framePos.y );
1079 wxPoint upperLeft( m_framePos.x, m_framePos.y );
1080
1081 wxDisplay display( aState.display );
1082 wxRect clientSize = display.GetClientArea();
1083
1084 int yLimTop = clientSize.y;
1085 int yLimBottom = clientSize.y + clientSize.height;
1086 int xLimLeft = clientSize.x;
1087 int xLimRight = clientSize.x + clientSize.width;
1088
1089 if( upperLeft.x > xLimRight || // Upper left corner too close to right edge of screen
1090 upperRight.x < xLimLeft || // Upper right corner too close to left edge of screen
1091 upperLeft.y < yLimTop || // Upper corner too close to the bottom of the screen
1092 upperLeft.y > yLimBottom )
1093 {
1094 m_framePos = wxDefaultPosition;
1095 wxLogTrace( traceDisplayLocation, wxS( "Resetting to default position" ) );
1096 }
1097
1098 // Clamp the saved size to the current display, in case the window was sized for a
1099 // larger external monitor that is no longer attached.
1100 if( m_frameSize.x > clientSize.width )
1101 {
1102 wxLogTrace( traceDisplayLocation, wxS( "Clamping window width %d to display width %d" ),
1103 m_frameSize.x, clientSize.width );
1104 m_frameSize.x = clientSize.width;
1105 }
1106
1107 if( m_frameSize.y > clientSize.height )
1108 {
1109 wxLogTrace( traceDisplayLocation, wxS( "Clamping window height %d to display height %d" ),
1110 m_frameSize.y, clientSize.height );
1111 m_frameSize.y = clientSize.height;
1112 }
1113 }
1114
1115 wxLogTrace( traceDisplayLocation, wxS( "Final window position (%d, %d) with size (%d, %d)" ),
1117
1118 SetSize( m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
1119
1120 // Center the window if we reset to default
1121 if( m_framePos.x == -1 )
1122 {
1123 wxLogTrace( traceDisplayLocation, wxS( "Centering window" ) );
1124 Center();
1125 m_framePos = GetPosition();
1126 }
1127
1128 // Record the frame sizes in an un-maximized state
1131
1132 // Maximize if we were maximized before
1133 if( aState.maximized || ( wasDefault && m_maximizeByDefault ) )
1134 {
1135 wxLogTrace( traceDisplayLocation, wxS( "Maximizing window" ) );
1136 Maximize();
1137 }
1138
1139 m_displayIndex = wxDisplay::GetFromWindow( this );
1140}
1141
1142
1144{
1145 wxDisplay display( wxDisplay::GetFromWindow( this ) );
1146 wxRect clientSize = display.GetClientArea();
1147 wxPoint pos = GetPosition();
1148 wxSize size = GetWindowSize();
1149
1150 wxLogTrace( traceDisplayLocation, wxS( "ensureWindowIsOnScreen: clientArea (%d, %d) w %d h %d" ),
1151 clientSize.x, clientSize.y,
1152 clientSize.width, clientSize.height );
1153
1154 if( pos.y < clientSize.y )
1155 {
1156 wxLogTrace( traceDisplayLocation, wxS( "ensureWindowIsOnScreen: y pos %d below minimum, setting to %d" ),
1157 pos.y, clientSize.y );
1158 pos.y = clientSize.y;
1159 }
1160
1161 if( pos.x < clientSize.x )
1162 {
1163 wxLogTrace( traceDisplayLocation, wxS( "ensureWindowIsOnScreen: x pos %d below minimum, setting to %d" ),
1164 pos.x, clientSize.x );
1165 pos.x = clientSize.x;
1166 }
1167
1168 if( pos.x + size.x - clientSize.x > clientSize.width )
1169 {
1170 int newWidth = clientSize.width - ( pos.x - clientSize.x );
1171 wxLogTrace( traceDisplayLocation, wxS( "ensureWindowIsOnScreen: width %d above available %d, setting to %d" ),
1172 pos.x + size.x, clientSize.width, newWidth );
1173 size.x = newWidth;
1174 }
1175
1176 if( pos.y + size.y - clientSize.y > clientSize.height )
1177 {
1178 int newHeight = clientSize.height - ( pos.y - clientSize.y );
1179 wxLogTrace( traceDisplayLocation, wxS( "ensureWindowIsOnScreen: height %d above available %d, setting to %d" ),
1180 pos.y + size.y, clientSize.height, newHeight );
1181 size.y = newHeight;
1182 }
1183
1184 wxLogTrace( traceDisplayLocation, wxS( "Updating window position (%d, %d) with size (%d, %d)" ),
1185 pos.x, pos.y, size.x, size.y );
1186
1187 SetSize( pos.x, pos.y, size.x, size.y );
1188}
1189
1190
1192{
1193 LoadWindowState( aCfg->state );
1194
1195 m_perspective = aCfg->perspective;
1196 m_auiLayoutState = std::make_unique<nlohmann::json>( aCfg->aui_state );
1197 m_mruPath = aCfg->mru_path;
1198
1200}
1201
1202
1204{
1205 if( IsIconized() )
1206 return;
1207
1208 // If the window is maximized, we use the saved window size from before it was maximized
1209 if( IsMaximized() )
1210 {
1213 }
1214 else
1215 {
1217 m_framePos = GetPosition();
1218 }
1219
1220 aCfg->state.pos_x = m_framePos.x;
1221 aCfg->state.pos_y = m_framePos.y;
1222 aCfg->state.size_x = m_frameSize.x;
1223 aCfg->state.size_y = m_frameSize.y;
1224 aCfg->state.maximized = IsMaximized();
1225 aCfg->state.display = wxDisplay::GetFromWindow( this );
1226
1227 wxLogTrace( traceDisplayLocation, wxS( "Saving window maximized: %s" ),
1228 IsMaximized() ? wxS( "true" ) : wxS( "false" ) );
1229 wxLogTrace( traceDisplayLocation, wxS( "Saving config position (%d, %d) with size (%d, %d)" ),
1231
1232 // Once this is fully implemented, wxAuiManager will be used to maintain
1233 // the persistence of the main frame and all it's managed windows and
1234 // all of the legacy frame persistence position code can be removed.
1235#if wxCHECK_VERSION( 3, 3, 0 )
1236 {
1237 WX_AUI_JSON_SERIALIZER serializer( m_auimgr );
1238 nlohmann::json state = serializer.Serialize();
1239
1240 if( state.is_null() || state.empty() )
1241 aCfg->aui_state = nlohmann::json();
1242 else
1243 aCfg->aui_state = state;
1244
1245 aCfg->perspective.clear();
1246 }
1247#else
1248 aCfg->perspective = m_auimgr.SavePerspective().ToStdString();
1249 aCfg->aui_state = nlohmann::json();
1250#endif
1251
1252 aCfg->mru_path = m_mruPath;
1253}
1254
1255
1257{
1259
1260 // Get file history size from common settings
1261 int fileHistorySize = Pgm().GetCommonSettings()->m_System.file_history_size;
1262
1263 // Load the recently used files into the history menu
1264 m_fileHistory = new FILE_HISTORY( (unsigned) std::max( 1, fileHistorySize ), ID_FILE1, ID_FILE_LIST_CLEAR );
1265 m_fileHistory->Load( *aCfg );
1266}
1267
1268
1270{
1271 wxCHECK( config(), /* void */ );
1272
1274
1275 bool fileOpen = m_isClosing && m_isNonUserClose;
1276
1277 wxString currentlyOpenedFile = GetCurrentFileName();
1278
1279 if( Pgm().GetCommonSettings()->m_Session.remember_open_files && !currentlyOpenedFile.IsEmpty() )
1280 {
1281 wxFileName rfn( currentlyOpenedFile );
1282 rfn.MakeRelativeTo( Prj().GetProjectPath() );
1283 Prj().GetLocalSettings().SaveFileState( rfn.GetFullPath(), &aCfg->m_Window, fileOpen );
1284 }
1285
1286 // Save the recently used files list
1287 if( m_fileHistory )
1288 {
1289 // Save the currently opened file in the file history
1290 if( !currentlyOpenedFile.IsEmpty() )
1291 UpdateFileHistory( currentlyOpenedFile );
1292
1293 m_fileHistory->Save( *aCfg );
1294 }
1295}
1296
1297
1302
1303
1305{
1306 // KICAD_MANAGER_FRAME overrides this
1307 return Kiface().KifaceSettings();
1308}
1309
1310
1312{
1313 return Kiface().KifaceSearch();
1314}
1315
1316
1318{
1319 return Kiface().GetHelpFileName();
1320}
1321
1322
1323void EDA_BASE_FRAME::PrintMsg( const wxString& text )
1324{
1325 SetStatusText( text );
1326}
1327
1328
1330{
1331 wxWindow* canvas = GetToolCanvas();
1332
1333 wxCHECK( canvas, /* void */ );
1334
1335 m_infoBar = new WX_INFOBAR( canvas, wxID_ANY, true );
1336}
1337
1338
1340{
1341 m_auimgr.Update();
1342}
1343
1344
1346{
1347 if( !ADVANCED_CFG::GetCfg().m_EnableUseAuiPerspective )
1348 return;
1349
1350 bool restored = false;
1351
1352#if wxCHECK_VERSION( 3, 3, 0 )
1353 if( m_auiLayoutState && !m_auiLayoutState->is_null() && !m_auiLayoutState->empty() )
1354 {
1355 WX_AUI_JSON_SERIALIZER serializer( m_auimgr );
1356
1357 if( serializer.Deserialize( *m_auiLayoutState ) )
1358 restored = true;
1359 }
1360#endif
1361
1362 /*
1363 * Legacy loading of the string AUI perspective (if it exists). This is needed for
1364 * wx 3.2 or the first settings upgrade when wx 3.3 is used in KiCad (e.g., 9.0->10.0 for Windows and macOS).
1365 */
1366 if( !restored && !m_perspective.IsEmpty() )
1367 m_auimgr.LoadPerspective( m_perspective );
1368
1369 // Workaround for two bugs:
1370 // 1) wx 3.2: LoadPerspective() hides all panes first, then shows only
1371 // those in the saved string. If toolbar names changed or new toolbars were added,
1372 // they'd stay hidden. Ensure all toolbars are visible after restore.
1373 // 2) We still saw this even after this fix, so just make the toolbars shown unconditionally
1374 // since we don't actually allow hiding them. The root cause of this part is not known.
1375 wxAuiPaneInfoArray& panes = m_auimgr.GetAllPanes();
1376
1377 for( size_t i = 0; i < panes.GetCount(); ++i )
1378 {
1379 if( panes.Item( i ).IsToolbar() )
1380 panes.Item( i ).Show( true );
1381 }
1382}
1383
1384
1385void EDA_BASE_FRAME::ShowInfoBarError( const wxString& aErrorMsg, bool aShowCloseButton,
1386 INFOBAR_MESSAGE_TYPE aType )
1387{
1388 m_infoBar->RemoveAllButtons();
1389
1390 if( aShowCloseButton )
1391 m_infoBar->AddCloseButton();
1392
1393 GetInfoBar()->ShowMessageFor( aErrorMsg, 8000, wxICON_ERROR, aType );
1394}
1395
1396
1397void EDA_BASE_FRAME::ShowInfoBarError( const wxString& aErrorMsg, bool aShowCloseButton,
1398 std::function<void(void)> aCallback )
1399{
1400 m_infoBar->RemoveAllButtons();
1401
1402 if( aShowCloseButton )
1403 m_infoBar->AddCloseButton();
1404
1405 if( aCallback )
1406 m_infoBar->SetCallback( aCallback );
1407
1408 GetInfoBar()->ShowMessageFor( aErrorMsg, 6000, wxICON_ERROR );
1409}
1410
1411
1412void EDA_BASE_FRAME::ShowInfoBarWarning( const wxString& aWarningMsg, bool aShowCloseButton )
1413{
1414 m_infoBar->RemoveAllButtons();
1415
1416 if( aShowCloseButton )
1417 m_infoBar->AddCloseButton();
1418
1419 GetInfoBar()->ShowMessageFor( aWarningMsg, 6000, wxICON_WARNING );
1420}
1421
1422
1423void EDA_BASE_FRAME::ShowInfoBarMsg( const wxString& aMsg, bool aShowCloseButton )
1424{
1425 m_infoBar->RemoveAllButtons();
1426
1427 if( aShowCloseButton )
1428 m_infoBar->AddCloseButton();
1429
1430 GetInfoBar()->ShowMessageFor( aMsg, 8000, wxICON_INFORMATION );
1431}
1432
1433
1434void EDA_BASE_FRAME::UpdateFileHistory( const wxString& FullFileName, FILE_HISTORY* aFileHistory )
1435{
1436 if( !aFileHistory )
1437 aFileHistory = m_fileHistory;
1438
1439 wxASSERT( aFileHistory );
1440
1441 aFileHistory->AddFileToHistory( FullFileName );
1442
1443 // Update the menubar to update the file history menu
1444 if( !m_isClosing && GetMenuBar() )
1445 {
1447 GetMenuBar()->Refresh();
1448 }
1449}
1450
1451
1452wxString EDA_BASE_FRAME::GetFileFromHistory( int cmdId, const wxString& type, FILE_HISTORY* aFileHistory )
1453{
1454 if( !aFileHistory )
1455 aFileHistory = m_fileHistory;
1456
1457 wxASSERT( aFileHistory );
1458
1459 int baseId = aFileHistory->GetBaseId();
1460
1461 wxASSERT( cmdId >= baseId && cmdId < baseId + (int) aFileHistory->GetCount() );
1462 int i = cmdId - baseId;
1463
1464 wxString fn = aFileHistory->GetHistoryFile( i );
1465
1466 if( !wxFileName::FileExists( fn ) )
1467 {
1468 KICAD_MESSAGE_DIALOG dlg( this, wxString::Format( _( "File '%s' was not found.\n" ), fn ), _( "Error" ),
1469 wxYES_NO | wxYES_DEFAULT | wxICON_ERROR | wxCENTER );
1470
1471 dlg.SetExtendedMessage( _( "Do you want to remove it from list of recently opened files?" ) );
1472 dlg.SetYesNoLabels( KICAD_MESSAGE_DIALOG::ButtonLabel( _( "Remove" ) ),
1473 KICAD_MESSAGE_DIALOG::ButtonLabel( _( "Keep" ) ) );
1474
1475 if( dlg.ShowModal() == wxID_YES )
1476 aFileHistory->RemoveFileFromHistory( i );
1477
1478 fn.Clear();
1479 }
1480
1481 // Update the menubar to update the file history menu
1482 if( GetMenuBar() )
1483 {
1485 GetMenuBar()->Refresh();
1486 }
1487
1488 return fn;
1489}
1490
1491
1493{
1494 wxASSERT( m_fileHistory );
1495
1496 m_fileHistory->ClearFileHistory();
1497
1498 // Update the menubar to update the file history menu
1499 if( GetMenuBar() )
1500 {
1502 GetMenuBar()->Refresh();
1503 }
1504}
1505
1506
1507void EDA_BASE_FRAME::OnKicadAbout( wxCommandEvent& event )
1508{
1509 void ShowAboutDialog( EDA_BASE_FRAME * aParent ); // See AboutDialog_main.cpp
1510 ShowAboutDialog( this );
1511}
1512
1513
1514void EDA_BASE_FRAME::OnPreferences( wxCommandEvent& event )
1515{
1516 ShowPreferences( wxEmptyString, wxEmptyString );
1517}
1518
1519
1520void EDA_BASE_FRAME::ShowPreferences( const wxString& aStartPage, const wxString& aStartParentPage )
1521{
1522 PAGED_DIALOG dlg( this, _( "Preferences" ), true, true, wxEmptyString,
1523 wxWindow::FromDIP( wxSize( 980, 560 ), nullptr ) );
1524
1525 dlg.SetEvtHandlerEnabled( false );
1526
1527 {
1528 WX_BUSY_INDICATOR busy_cursor;
1529
1530 WX_TREEBOOK* book = dlg.GetTreebook();
1531 PANEL_HOTKEYS_EDITOR* hotkeysPanel = new PANEL_HOTKEYS_EDITOR( this, book, false );
1532 std::vector<int> expand;
1533
1534 wxWindow* kicadMgr_window = wxWindow::FindWindowByName( KICAD_MANAGER_FRAME_NAME );
1535
1536 if( KICAD_MANAGER_FRAME* kicadMgr = static_cast<KICAD_MANAGER_FRAME*>( kicadMgr_window ) )
1537 {
1538 ACTION_MANAGER* actionMgr = kicadMgr->GetToolManager()->GetActionManager();
1539
1540 for( const auto& [name, action] : actionMgr->GetActions() )
1541 hotkeysPanel->ActionsList().push_back( action );
1542 }
1543
1544 book->AddLazyPage(
1545 []( wxWindow* aParent ) -> wxWindow*
1546 {
1547 return new PANEL_COMMON_SETTINGS( aParent );
1548 },
1549 _( "Common" ) );
1550
1551 book->AddLazyPage(
1552 []( wxWindow* aParent ) -> wxWindow*
1553 {
1554 return new PANEL_MOUSE_SETTINGS( aParent );
1555 }, _( "Mouse and Touchpad" ) );
1556
1557#if defined(__linux__) || defined(__FreeBSD__)
1558 book->AddLazyPage(
1559 [] ( wxWindow* aParent ) -> wxWindow*
1560 {
1561 return new PANEL_SPACEMOUSE( aParent );
1562 }, _( "SpaceMouse" ) );
1563#endif
1564
1565 book->AddPage( hotkeysPanel, _( "Hotkeys" ) );
1566
1567 book->AddLazyPage(
1568 []( wxWindow* aParent ) -> wxWindow*
1569 {
1570 return new PANEL_GIT_REPOS( aParent );
1571 }, _( "Version Control" ) );
1572
1573#ifdef KICAD_USE_SENTRY
1574 book->AddLazyPage(
1575 []( wxWindow* aParent ) -> wxWindow*
1576 {
1577 return new PANEL_DATA_COLLECTION( aParent );
1578 }, _( "Data Collection" ) );
1579#endif
1580
1581#define LAZY_CTOR( key ) \
1582 [this, kiface]( wxWindow* aParent ) \
1583 { \
1584 return kiface->CreateKiWindow( aParent, key, &Kiway() ); \
1585 }
1586
1587 // If a dll is not loaded, the loader will show an error message.
1588
1589 try
1590 {
1591 if( KIFACE* kiface = Kiway().KiFACE( KIWAY::FACE_SCH ) )
1592 {
1593 kiface->GetActions( hotkeysPanel->ActionsList() );
1594
1596 expand.push_back( (int) book->GetPageCount() );
1597
1598 book->AddPage( new wxPanel( book ), _( "Symbol Editor" ) );
1599 book->AddLazySubPage( LAZY_CTOR( PANEL_SYM_DISP_OPTIONS ), _( "Display Options" ) );
1600 book->AddLazySubPage( LAZY_CTOR( PANEL_SYM_EDIT_GRIDS ), _( "Grids" ) );
1601 book->AddLazySubPage( LAZY_CTOR( PANEL_SYM_SNAPPING ), _( "Snapping" ) );
1602 book->AddLazySubPage( LAZY_CTOR( PANEL_SYM_EDIT_OPTIONS ), _( "Editing Options" ) );
1603 book->AddLazySubPage( LAZY_CTOR( PANEL_SYM_COLORS ), _( "Colors" ) );
1604 book->AddLazySubPage( LAZY_CTOR( PANEL_SYM_TOOLBARS ), _( "Toolbars" ) );
1605
1606 if( GetFrameType() == FRAME_SCH )
1607 expand.push_back( (int) book->GetPageCount() );
1608
1609 book->AddPage( new wxPanel( book ), _( "Schematic Editor" ) );
1610 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_DISP_OPTIONS ), _( "Display Options" ) );
1611 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_GRIDS ), _( "Grids" ) );
1612 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_SNAPPING ), _( "Snapping" ) );
1613 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_EDIT_OPTIONS ), _( "Editing Options" ) );
1614 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_COLORS ), _( "Colors" ) );
1615 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_TOOLBARS ), _( "Toolbars" ) );
1616 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_FIELD_NAME_TEMPLATES ), _( "Field Name Templates" ) );
1617 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_DATA_SOURCES ), _( "Data Sources" ) );
1618 book->AddLazySubPage( LAZY_CTOR( PANEL_SCH_SIMULATOR ), _( "Simulator" ) );
1619 }
1620 }
1621 catch( ... )
1622 {
1623 }
1624
1625 try
1626 {
1627 if( KIFACE* kiface = Kiway().KiFACE( KIWAY::FACE_PCB ) )
1628 {
1629 kiface->GetActions( hotkeysPanel->ActionsList() );
1630
1632 expand.push_back( (int) book->GetPageCount() );
1633
1634 book->AddPage( new wxPanel( book ), _( "Footprint Editor" ) );
1635 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_DISPLAY_OPTIONS ), _( "Display Options" ) );
1636 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_GRIDS ), _( "Grids" ) );
1637 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_SNAPPING ), _( "Snapping" ) );
1638 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_ORIGINS_AXES ), _( "Origins & Axes" ) );
1639 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_EDIT_OPTIONS ), _( "Editing Options" ) );
1640 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_COLORS ), _( "Colors" ) );
1641 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_TOOLBARS ), _( "Toolbars" ) );
1642 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_DEFAULT_FIELDS ), _( "Footprint Defaults" ) );
1643 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_DEFAULT_GRAPHICS_VALUES ), _( "Graphics Defaults" ) );
1644 book->AddLazySubPage( LAZY_CTOR( PANEL_FP_USER_LAYER_NAMES ), _( "User Layer Names" ) );
1645
1647 expand.push_back( (int) book->GetPageCount() );
1648
1649 book->AddPage( new wxPanel( book ), _( "PCB Editor" ) );
1650 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_DISPLAY_OPTS ), _( "Display Options" ) );
1651 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_GRIDS ), _( "Grids" ) );
1652 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_SNAPPING ), _( "Snapping" ) );
1653 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_ORIGINS_AXES ), _( "Origins & Axes" ) );
1654 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_EDIT_OPTIONS ), _( "Editing Options" ) );
1655 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_COLORS ), _( "Colors" ) );
1656 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_TOOLBARS ), _( "Toolbars" ) );
1657 book->AddLazySubPage( LAZY_CTOR( PANEL_PCB_ACTION_PLUGINS ), _( "Plugins" ) );
1658
1660 expand.push_back( (int) book->GetPageCount() );
1661
1662 book->AddPage( new wxPanel( book ), _( "3D Viewer" ) );
1663 book->AddLazySubPage( LAZY_CTOR( PANEL_3DV_DISPLAY_OPTIONS ), _( "General" ) );
1664 book->AddLazySubPage( LAZY_CTOR( PANEL_3DV_TOOLBARS ), _( "Toolbars" ) );
1665 book->AddLazySubPage( LAZY_CTOR( PANEL_3DV_OPENGL ), _( "Realtime Renderer" ) );
1666 book->AddLazySubPage( LAZY_CTOR( PANEL_3DV_RAYTRACING ), _( "Raytracing Renderer" ) );
1667 }
1668 }
1669 catch( ... )
1670 {
1671 }
1672
1673 try
1674 {
1675 if( KIFACE* kiface = Kiway().KiFACE( KIWAY::FACE_GERBVIEW ) )
1676 {
1677 kiface->GetActions( hotkeysPanel->ActionsList() );
1678
1679 if( GetFrameType() == FRAME_GERBER )
1680 expand.push_back( (int) book->GetPageCount() );
1681
1682 book->AddPage( new wxPanel( book ), _( "Gerber Viewer" ) );
1683 book->AddLazySubPage( LAZY_CTOR( PANEL_GBR_DISPLAY_OPTIONS ), _( "Display Options" ) );
1684 book->AddLazySubPage( LAZY_CTOR( PANEL_GBR_COLORS ), _( "Colors" ) );
1685 book->AddLazySubPage( LAZY_CTOR( PANEL_GBR_TOOLBARS ), _( "Toolbars" ) );
1686 book->AddLazySubPage( LAZY_CTOR( PANEL_GBR_GRIDS ), _( "Grids" ) );
1687 book->AddLazySubPage( LAZY_CTOR( PANEL_GBR_SNAPPING ), _( "Snapping" ) );
1688 book->AddLazySubPage( LAZY_CTOR( PANEL_GBR_EXCELLON_OPTIONS ), _( "Excellon Options" ) );
1689 }
1690 }
1691 catch( ... )
1692 {
1693 }
1694
1695 try
1696 {
1697 if( KIFACE* kiface = Kiway().KiFACE( KIWAY::FACE_PL_EDITOR ) )
1698 {
1699 kiface->GetActions( hotkeysPanel->ActionsList() );
1700
1701 if( GetFrameType() == FRAME_PL_EDITOR )
1702 expand.push_back( (int) book->GetPageCount() );
1703
1704 book->AddPage( new wxPanel( book ), _( "Drawing Sheet Editor" ) );
1705 book->AddLazySubPage( LAZY_CTOR( PANEL_DS_DISPLAY_OPTIONS ), _( "Display Options" ) );
1706 book->AddLazySubPage( LAZY_CTOR( PANEL_DS_GRIDS ), _( "Grids" ) );
1707 book->AddLazySubPage( LAZY_CTOR( PANEL_DS_SNAPPING ), _( "Snapping" ) );
1708 book->AddLazySubPage( LAZY_CTOR( PANEL_DS_COLORS ), _( "Colors" ) );
1709 book->AddLazySubPage( LAZY_CTOR( PANEL_DS_TOOLBARS ), _( "Toolbars" ) );
1710
1711 book->AddLazyPage(
1712 []( wxWindow* aParent ) -> wxWindow*
1713 {
1714 return new PANEL_PACKAGES_AND_UPDATES( aParent );
1715 }, _( "Packages and Updates" ) );
1716 }
1717 }
1718 catch( ... )
1719 {
1720 }
1721
1722 book->AddPage( new PANEL_PLUGIN_SETTINGS( book ), _( "Plugins" ) );
1723
1724 book->AddPage( new PANEL_MAINTENANCE( book, this ), _( "Maintenance" ) );
1725
1726 // Update all of the action hotkeys. The process of loading the actions through
1727 // the KiFACE will only get us the default hotkeys
1728 ReadHotKeyConfigIntoActions( wxEmptyString, hotkeysPanel->ActionsList() );
1729
1730 for( size_t i = 0; i < book->GetPageCount(); ++i )
1731 book->GetPage( i )->Layout();
1732
1733 for( int page : expand )
1734 book->ExpandNode( page );
1735
1736 if( !aStartPage.IsEmpty() )
1737 dlg.SetInitialPage( aStartPage, aStartParentPage );
1738
1739 dlg.SetEvtHandlerEnabled( true );
1740#undef LAZY_CTOR
1741 }
1742
1743 if( dlg.ShowModal() == wxID_OK )
1744 {
1745 // Update our grids that are cached in the tool
1746 m_toolManager->ResetTools( TOOL_BASE::REDRAW );
1749 }
1750
1751}
1752
1753
1754void EDA_BASE_FRAME::OnDropFiles( wxDropFilesEvent& aEvent )
1755{
1756 Raise();
1757
1758 wxString* files = aEvent.GetFiles();
1759
1760 for( int nb = 0; nb < aEvent.GetNumberOfFiles(); nb++ )
1761 {
1762 const wxFileName fn = wxFileName( files[nb] );
1763 wxString ext = fn.GetExt();
1764
1765 // Alias all gerber files as GerberFileExtension
1768
1769 if( m_acceptedExts.find( ext.ToStdString() ) != m_acceptedExts.end() )
1770 m_AcceptedFiles.emplace_back( fn );
1771 }
1772
1774 m_AcceptedFiles.clear();
1775}
1776
1777
1779{
1780 for( const wxFileName& file : m_AcceptedFiles )
1781 {
1782 wxString fn = file.GetFullPath();
1783 m_toolManager->RunAction<wxString*>( *m_acceptedExts.at( file.GetExt() ), &fn );
1784 }
1785}
1786
1787
1788bool EDA_BASE_FRAME::IsWritable( const wxFileName& aFileName, bool aVerbose )
1789{
1790 wxString msg;
1791 wxFileName fn = aFileName;
1792
1793 // Check for absence of a file path with a file name. Unfortunately KiCad
1794 // uses paths relative to the current project path without the ./ part which
1795 // confuses wxFileName. Making the file name path absolute may be less than
1796 // elegant but it solves the problem.
1797 if( fn.GetPath().IsEmpty() && fn.HasName() )
1798 fn.MakeAbsolute();
1799
1800 wxCHECK_MSG( fn.IsOk(), false, wxT( "File name object is invalid. Bad programmer!" ) );
1801 wxCHECK_MSG( !fn.GetPath().IsEmpty(), false,
1802 wxT( "File name object path <" ) + fn.GetFullPath() + wxT( "> is not set. Bad programmer!" ) );
1803
1804 if( fn.IsDir() && !fn.IsDirWritable() )
1805 {
1806 msg.Printf( _( "Insufficient permissions to folder '%s'." ), fn.GetPath() );
1807 }
1808 else if( !fn.FileExists() && !fn.IsDirWritable() )
1809 {
1810 msg.Printf( _( "Insufficient permissions to save file '%s'." ), fn.GetFullPath() );
1811 }
1812 else if( fn.FileExists() && !fn.IsFileWritable() )
1813 {
1814 msg.Printf( _( "Insufficient permissions to save file '%s'." ), fn.GetFullPath() );
1815 }
1816
1817 if( !msg.IsEmpty() )
1818 {
1819 if( aVerbose )
1820 DisplayErrorMessage( this, msg );
1821
1822 return false;
1823 }
1824
1825 return true;
1826}
1827
1828
1830{
1831 // This function should be overridden in child classes
1832 return false;
1833}
1834
1835
1837{
1838 wxAcceleratorEntry entries[1];
1839 entries[0].Set( wxACCEL_CTRL, int( 'Q' ), wxID_EXIT );
1840 wxAcceleratorTable accel( 1, entries );
1841 SetAcceleratorTable( accel );
1842}
1843
1844
1850
1851
1853{
1854 m_undoList.PushCommand( aNewitem );
1855
1856 // Delete the extra items, if count max reached
1857 if( m_undoRedoCountMax > 0 )
1858 {
1859 int extraitems = GetUndoCommandCount() - m_undoRedoCountMax;
1860
1861 if( extraitems > 0 )
1862 ClearUndoORRedoList( UNDO_LIST, extraitems );
1863 }
1864}
1865
1866
1868{
1869 m_redoList.PushCommand( aNewitem );
1870
1871 // Delete the extra items, if count max reached
1872 if( m_undoRedoCountMax > 0 )
1873 {
1874 int extraitems = GetRedoCommandCount() - m_undoRedoCountMax;
1875
1876 if( extraitems > 0 )
1877 ClearUndoORRedoList( REDO_LIST, extraitems );
1878 }
1879}
1880
1881
1886
1887
1892
1893
1895{
1896 if( GetUndoCommandCount() > 0 )
1897 return m_undoList.m_CommandsList.back()->GetDescription();
1898
1899 return wxEmptyString;
1900}
1901
1902
1904{
1905 if( GetRedoCommandCount() > 0 )
1906 return m_redoList.m_CommandsList.back()->GetDescription();
1907
1908 return wxEmptyString;
1909}
1910
1911
1913{
1914 m_autoSaveRequired = true;
1915}
1916
1917
1919{
1920 SetUserUnits( aUnits );
1922
1923 wxCommandEvent e( EDA_EVT_UNITS_CHANGED );
1924 e.SetInt( static_cast<int>( aUnits ) );
1925 e.SetClientData( this );
1926 ProcessEventLocally( e );
1927}
1928
1929
1930void EDA_BASE_FRAME::OnMaximize( wxMaximizeEvent& aEvent )
1931{
1932 // When we maximize the window, we want to save the old information
1933 // so that we can add it to the settings on next window load.
1934 // Contrary to the documentation, this event seems to be generated
1935 // when the window is also being unmaximized on OSX, so we only
1936 // capture the size information when we maximize the window when on OSX.
1937#ifdef __WXOSX__
1938 if( !IsMaximized() )
1939#endif
1940 {
1942 m_normalFramePos = GetPosition();
1943 wxLogTrace( traceDisplayLocation, "Maximizing window - Saving position (%d, %d) with size (%d, %d)",
1946 }
1947
1948 // Skip event to actually maximize the window
1949 aEvent.Skip();
1950}
1951
1952
1954{
1955#if defined( __WXGTK__ ) && !wxCHECK_VERSION( 3, 2, 9 )
1956 wxSize winSize = GetSize();
1957
1958 // GTK includes the window decorations in the normal GetSize call,
1959 // so we have to use a GTK-specific sizing call that returns the
1960 // non-decorated window size.
1962 {
1963 int width = 0;
1964 int height = 0;
1965 GTKDoGetSize( &width, &height );
1966
1967 winSize.Set( width, height );
1968 }
1969#else
1970 wxSize winSize = GetSize();
1971#endif
1972
1973 return winSize;
1974}
1975
1976
1978{
1979 // Update the icon theme when the system theme changes and update the toolbars
1981 ThemeChanged();
1982
1983 // This isn't handled by ThemeChanged()
1984 if( GetMenuBar() )
1985 {
1986 // For icons in menus, icon scaling & hotkeys
1988 GetMenuBar()->Refresh();
1989 }
1990}
1991
1992
1993void EDA_BASE_FRAME::onSystemColorChange( wxSysColourChangedEvent& aEvent )
1994{
1995 // Call the handler to update the colors used in the frame
1997
1998 // Skip the change event to ensure the rest of the window controls get it
1999 aEvent.Skip();
2000}
2001
2002
2003void EDA_BASE_FRAME::onIconize( wxIconizeEvent& aEvent )
2004{
2005 // Call the handler
2006 handleIconizeEvent( aEvent );
2007
2008 // Skip the event.
2009 aEvent.Skip();
2010}
2011
2012
2013#ifdef __WXMSW__
2014WXLRESULT EDA_BASE_FRAME::MSWWindowProc( WXUINT message, WXWPARAM wParam, WXLPARAM lParam )
2015{
2016 // This will help avoid the menu keeping focus when the alt key is released
2017 // You can still trigger accelerators as long as you hold down alt
2018 if( message == WM_SYSCOMMAND )
2019 {
2020 if( wParam == SC_KEYMENU && ( lParam >> 16 ) <= 0 )
2021 return 0;
2022 }
2023
2024 return wxFrame::MSWWindowProc( message, wParam, lParam );
2025}
2026#endif
2027
2028
2030{
2031 ACTION_MENU* langsMenu = new ACTION_MENU( false, aControlTool );
2032 langsMenu->SetTitle( _( "Set Language" ) );
2033 langsMenu->SetIcon( BITMAPS::language );
2034
2035 wxString tooltip;
2036
2037 for( unsigned ii = 0; LanguagesList[ii].m_KI_Lang_Identifier != 0; ii++ )
2038 {
2039 wxString label;
2040
2041 if( LanguagesList[ii].m_DoNotTranslate )
2042 label = LanguagesList[ii].m_Lang_Label;
2043 else
2044 label = wxGetTranslation( LanguagesList[ii].m_Lang_Label );
2045
2046 wxMenuItem* item = new wxMenuItem( langsMenu, LanguagesList[ii].m_KI_Lang_Identifier, // wxMenuItem wxID
2047 label, tooltip, wxITEM_CHECK );
2048
2049 langsMenu->Append( item );
2050 }
2051
2052 // This must be done after the items are added
2053 aMasterMenu->Add( langsMenu );
2054}
2055
2056
2057void EDA_BASE_FRAME::OnLanguageSelectionEvent( wxCommandEvent& event )
2058{
2059 int id = event.GetId();
2060
2061 // tell all the KIWAY_PLAYERs about the language change.
2062 Kiway().SetLanguage( id );
2063}
void ShowAboutDialog(EDA_BASE_FRAME *aParent)
int index
const char * name
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
void ClearScaledBitmapCache()
Wipes out the scaled bitmap cache so that the icon theme can be changed.
Definition bitmap.cpp:180
BITMAP_STORE * GetBitmapStore()
Definition bitmap.cpp:88
static TOOL_ACTION paste
Definition actions.h:76
static TOOL_ACTION about
Definition actions.h:285
static TOOL_ACTION reportBug
Definition actions.h:289
static TOOL_ACTION copy
Definition actions.h:74
static TOOL_ACTION donate
Definition actions.h:287
static TOOL_ACTION listHotKeys
Definition actions.h:286
static TOOL_ACTION getInvolved
Definition actions.h:288
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION redo
Definition actions.h:72
static TOOL_ACTION cut
Definition actions.h:73
static TOOL_ACTION gettingStarted
Definition actions.h:283
static TOOL_ACTION help
Definition actions.h:284
Manage TOOL_ACTION objects.
const std::map< std::string, TOOL_ACTION * > & GetActions() const
Get a list of currently-registered actions mapped by their name.
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:43
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
Class to hold basic information about controls that can be added to the toolbars.
const std::string & GetName() const
Define the structure of a toolbar with buttons that invoke ACTIONs.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
WINDOW_SETTINGS m_Window
void ThemeChanged()
Notifies the store that the icon theme has been changed by the user, so caches must be invalidated.
Handle actions that are shared between different applications.
bool AutosaveUsesLocalHistory() const
The backup format is the single switch that selects the autosave mechanism: the incremental format re...
AUTO_BACKUP m_Backup
std::vector< std::pair< wxString, wxString > > GetSelectedStale() const
AUTOSAVE_RECOVERY_CHOICE GetChoice() const
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:80
int ShowModal() override
The base frame for deriving all KiCad main window classes.
void SelectToolbarAction(const TOOL_ACTION &aAction) override
Select the given action in the toolbar group which contains it, if any.
virtual wxString help_name()
virtual bool doAutoSave()
This should be overridden by the derived class to handle the auto save feature.
void LoadWindowState(const wxString &aFileName)
FRAME_T GetFrameType() const
virtual void UnregisterUIUpdateHandler(int aID) override
Unregister a UI handler for a given ID that was registered using RegisterUIUpdateHandler.
virtual bool isAutoSaveRequired() const
Return the auto save status of the application.
virtual APP_SETTINGS_BASE * config() const
Return the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
virtual void handleIconizeEvent(wxIconizeEvent &aEvent)
Handle a window iconize event.
virtual void PushCommandToUndoList(PICKED_ITEMS_LIST *aItem)
Add a command to undo in the undo list.
void ShowPreferences(const wxString &aStartPage, const wxString &aStartParentPage)
Display the preferences and settings of all opened editors paged dialog, starting with a particular p...
void windowClosing(wxCloseEvent &event)
(with its unexpected name so it does not collide with the real OnWindowClose() function provided in d...
virtual void OnCharHook(wxKeyEvent &aKeyEvent)
Capture the key event before it is sent to the GUI.
virtual int GetRedoCommandCount() const
void CommonSettingsChanged(int aFlags) override
Notification event that some of the common (suite-wide) settings have changed.
UNDO_REDO_CONTAINER m_undoList
virtual void OnMove(wxMoveEvent &aEvent)
virtual WINDOW_SETTINGS * GetWindowSettings(APP_SETTINGS_BASE *aCfg)
Return a pointer to the window settings for this frame.
virtual void doCloseWindow()
void OnToolbarSizeChanged()
Update toolbars if desired toolbar icon changed.
void OnMenuEvent(wxMenuEvent &event)
The TOOL_DISPATCHER needs these to work around some issues in wxWidgets where the menu events aren't ...
virtual bool IsModal() const
Return true if the frame is shown in our modal mode and false if the frame is shown as an usual frame...
void ShowChangedLanguage() override
Redraw the menus and what not in current language.
virtual void HandleSystemColorChange()
Update the UI in response to a change in the system colors.
virtual void setupUIConditions()
Setup the UI conditions for the various actions and their controls in this frame.
bool m_isNonUserClose
Set by NonUserClose() to indicate that the user did not request the current close.
void CheckForAutosaveFiles(const wxString &aProjectPath, const std::vector< wxString > &aExtensions)
Check for autosave files newer than their source files for the given project.
void LoadWindowSettings(const WINDOW_SETTINGS *aCfg)
Load window settings from the given settings object.
std::vector< wxFileName > m_AcceptedFiles
bool m_uiUpdateHandlerBound
True once the single wxID_ANY UPDATE_UI handler has been bound.
bool m_autoSavePermissionError
void OnKicadAbout(wxCommandEvent &event)
virtual void UpdateToolbarControlSizes()
Update the sizes of any controls in the toolbars of the frame.
virtual void ClearUndoRedoList()
Clear the undo and redo list using ClearUndoORRedoList()
virtual void DoWithAcceptedFiles()
Execute action on accepted dropped file.
virtual void OnModify()
Must be called after a model change in order to set the "modify" flag and do other frame-specific pro...
wxWindow * findQuasiModalDialog()
wxString m_perspective
wxDateTime m_autoSaveDeferredSince
virtual void ClearUndoORRedoList(UNDO_REDO_LIST aList, int aItemCount=-1)
Remove the aItemCount of old commands from aList and delete commands, pickers and picked items if nee...
virtual void ThemeChanged()
Process light/dark theme change.
EDA_BASE_FRAME(wxWindow *aParent, FRAME_T aFrameType, const wxString &aTitle, const wxPoint &aPos, const wxSize &aSize, long aStyle, const wxString &aFrameName, KIWAY *aKiway, const EDA_IU_SCALE &aIuScale)
static constexpr int KICAD_AUI_TB_STYLE
Default style flags used for wxAUI toolbars.
bool m_closeInProgress
Set while windowClosing() is deciding whether the frame may close.
ACTION_TOOLBAR * m_tbRight
void initExitKey()
Set the common key-pair for exiting the application (Ctrl-Q) and ties it to the wxID_EXIT event id.
void OnPreferences(wxCommandEvent &event)
virtual const SEARCH_STACK & sys_search()
Return a SEARCH_STACK pertaining to entire program.
WX_INFOBAR * m_infoBar
void onAutoSaveTimer(wxTimerEvent &aEvent)
Handle the auto save timer event.
void SaveWindowSettings(WINDOW_SETTINGS *aCfg)
Save window settings to the given settings object.
virtual wxString GetRedoActionDescription() const
TOOLBAR_SETTINGS * m_toolbarSettings
virtual wxString GetCurrentFileName() const
Get the full filename + path of the currently opened file in the frame.
void ChangeUserUnits(EDA_UNITS aUnits)
void AddMenuLanguageList(ACTION_MENU *aMasterMenu, TOOL_INTERACTIVE *aControlTool)
Create a menu list for language choice, and add it as submenu to MasterMenu.
virtual bool canRunAutoSave() const
Return true when it is safe to run an autosave snapshot right now.
void RegisterCustomToolbarControlFactory(const ACTION_TOOLBAR_CONTROL &aControlDesc, const ACTION_TOOLBAR_CONTROL_FACTORY &aControlFactory)
Register a creation factory for toolbar controls that are present in this frame.
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
virtual void configureToolbars()
wxTimer * m_autoSaveTimer
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, INFOBAR_MESSAGE_TYPE aType=INFOBAR_MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
void UpdateFileHistory(const wxString &FullFileName, FILE_HISTORY *aFileHistory=nullptr)
Update the list of recently opened files.
wxAuiManager m_auimgr
void PrintMsg(const wxString &text)
void onUpdateUI(wxUpdateUIEvent &aEvent)
void commonInit(FRAME_T aFrameType)
Collect common initialization functions used in all CTORs.
virtual bool IsContentModified() const
Get if the contents of the frame have been modified since the last save.
void ShowInfoBarWarning(const wxString &aWarningMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and a warning icon on the left ...
virtual PICKED_ITEMS_LIST * PopCommandFromRedoList()
Return the last command to undo and remove it from list, nothing is deleted.
virtual void RecreateToolbars()
std::map< int, UIUpdateHandler > m_uiUpdateMap
Map containing the UI update handlers registered with wx for each action.
std::map< std::string, ACTION_TOOLBAR_CONTROL_FACTORY > m_toolbarControlFactories
ACTION_TOOLBAR_CONTROL_FACTORY * GetCustomToolbarControlFactory(const std::string &aName)
static void HandleUpdateUIEvent(wxUpdateUIEvent &aEvent, EDA_BASE_FRAME *aFrame, ACTION_CONDITIONS &aCond)
Handle events generated when the UI is trying to figure out the current state of the UI controls rela...
UNDO_REDO_CONTAINER m_redoList
virtual void LoadSettings(APP_SETTINGS_BASE *aCfg)
Load common frame parameters from a configuration file.
FILE_HISTORY * m_fileHistory
ACTION_TOOLBAR * m_tbLeft
SETTINGS_MANAGER * m_settingsManager
virtual void OnSize(wxSizeEvent &aEvent)
virtual wxString GetUndoActionDescription() const
virtual PICKED_ITEMS_LIST * PopCommandFromUndoList()
Return the last command to undo and remove it from list, nothing is deleted.
void OnLanguageSelectionEvent(wxCommandEvent &aEvent)
An event handler called on a language menu selection.
wxString GetRunMenuCommandDescription(const TOOL_ACTION &aAction)
virtual bool canCloseWindow(wxCloseEvent &aCloseEvent)
bool ProcessEvent(wxEvent &aEvent) override
Override the default process event handler to implement the auto save feature.
bool IsWritable(const wxFileName &aFileName, bool aVerbose=true)
Check if aFileName can be written.
wxPoint m_normalFramePos
void OnMaximize(wxMaximizeEvent &aEvent)
std::unique_ptr< nlohmann::json > m_auiLayoutState
virtual void ClearFileHistory()
Remove all files from the file history.
ACTION_TOOLBAR * m_tbTopAux
virtual void OnDropFiles(wxDropFilesEvent &aEvent)
Handle event fired when a file is dropped to the window.
std::map< const wxString, TOOL_ACTION * > m_acceptedExts
Associate file extensions with action to execute.
void onIconize(wxIconizeEvent &aEvent)
virtual void unitsChangeRefresh()
Called when when the units setting has changed to allow for any derived classes to handle refreshing ...
wxString GetFileFromHistory(int cmdId, const wxString &type, FILE_HISTORY *aFileHistory=nullptr)
Fetch the file name from the file history list.
wxSize GetWindowSize()
Get the undecorated window size that can be used for restoring the window size.
int GetAutoSaveInterval() const
virtual void SaveSettings(APP_SETTINGS_BASE *aCfg)
Save common frame parameters to a configuration data file.
void onSystemColorChange(wxSysColourChangedEvent &aEvent)
virtual int GetUndoCommandCount() const
virtual void RegisterUIUpdateHandler(int aID, const ACTION_CONDITIONS &aConditions) override
Register a UI update handler for the control with ID aID.
ACTION_TOOLBAR * m_tbTopMain
virtual void PushCommandToRedoList(PICKED_ITEMS_LIST *aItem)
Add a command to redo in the redo list.
bool m_isClosing
Set by the close window event handler after frames are asked if they can close.
void AddStandardHelpMenu(wxMenuBar *aMenuBar)
Add the standard KiCad help menu to the menubar.
void ReCreateMenuBar()
Recreate the menu bar.
virtual void doReCreateMenuBar()
WX_INFOBAR * GetInfoBar()
This class implements a file history object to store a list of files, that can then be added to a men...
void AddFileToHistory(const wxString &aFile) override
Adds a file to the history.
bool Running() const
The main KiCad project manager frame.
SEARCH_STACK & KifaceSearch()
Only for DSO specific 'non-library' files.
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:91
const wxString & GetHelpFileName() const
Return just the basename portion of the current help file.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY_HOLDER(KIWAY *aKiway, HOLDER_TYPE aType)
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:340
virtual void SetLanguage(int aLanguage)
Change the language and then calls ShowChangedLanguage() on all #KIWAY_PLAYERs.
Definition kiway.cpp:506
@ FACE_SCH
eeschema DSO
Definition kiway.h:347
@ FACE_PL_EDITOR
Definition kiway.h:351
@ FACE_PCB
pcbnew DSO
Definition kiway.h:348
@ FACE_GERBVIEW
Definition kiway.h:350
LOCAL_HISTORY & LocalHistory()
Return the LOCAL_HISTORY associated with this KIWAY.
Definition kiway.h:451
virtual void CommonSettingsChanged(int aFlags=0)
Call CommonSettingsChanged() on all KIWAY_PLAYERs.
Definition kiway.cpp:580
bool RunRegisteredSaversAndCommit(const wxString &aProjectPath, const wxString &aTitle, const wxString &aTagFileType=wxEmptyString)
Run all registered savers and, if any staged changes differ from HEAD, create a commit.
std::vector< std::pair< wxString, wxString > > FindStaleAutosaveFiles(const wxString &aProjectPath, const std::vector< wxString > &aExtensions) const
Enumerate autosave files newer than their corresponding source files for the project at aProjectPath,...
bool Init(const wxString &aProjectPath)
Initialize the local history repository for the given project path.
bool RunRegisteredSaversAsAutosaveFiles(const wxString &aProjectPath)
Run all registered savers and write their output to autosave files instead of committing to the local...
WX_TREEBOOK * GetTreebook()
void SetInitialPage(const wxString &aPage, const wxString &aParentPage=wxEmptyString)
std::vector< TOOL_ACTION * > & ActionsList()
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition paths.cpp:137
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
virtual int GetSelectedLanguageIdentifier() const
Definition pgm_base.h:229
KICAD_API_SERVER & GetApiServer()
Definition pgm_base.h:141
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:123
A holder to handle information on schematic or board items.
void SaveFileState(const wxString &aFileName, const WINDOW_SETTINGS *aWindowCfg, bool aOpen)
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:207
RAII class that sets an value at construction and resets it to the original value at destruction.
Look for files in a number of paths.
virtual wxWindow * GetToolCanvas() const =0
Canvas access.
virtual void CommonSettingsChanged(int aFlags=0)
Notification event that some of the common (suite-wide) settings have changed.
TOOL_MANAGER * m_toolManager
virtual void ShowChangedLanguage()
TOOL_DISPATCHER * m_toolDispatcher
virtual SELECTION & GetCurrentSelection()
Get the current selection from the canvas area.
Represent a single user action.
wxString GetMenuLabel() const
Return the translated label for the action.
wxString GetFriendlyName() const
Return the translated user-friendly name of the action.
@ REDRAW
Full drawing refresh.
Definition tool_base.h:79
UNITS_PROVIDER(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits)
void SetUserUnits(EDA_UNITS aUnits)
bool Deserialize(const nlohmann::json &aState) const
nlohmann::json Serialize() const
Simple wrapper around wxBusyCursor for used with the generic BUSY_INDICATOR interface.
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void ShowMessageFor(const wxString &aMessage, int aTime, int aFlags=wxICON_INFORMATION, MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the infobar with the provided message and icon for a specific period of time.
bool AddLazyPage(std::function< wxWindow *(wxWindow *aParent)> aLazyCtor, const wxString &text, bool bSelect=false, int imageId=NO_IMAGE)
bool AddLazySubPage(std::function< wxWindow *(wxWindow *aParent)> aLazyCtor, const wxString &text, bool bSelect=false, int imageId=NO_IMAGE)
@ PROJECT_DIR
Inside the project directory (default)
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
#define KICAD_MESSAGE_DIALOG
Definition confirm.h:48
const int minSize
Push and Shove router track width and via size dialog.
#define _(s)
static wxString buildRecoveredFileName(const wxFileName &aSrcFn, const wxDateTime &aStamp)
static const wxSize minSizeLookup(FRAME_T aFrameType, wxWindow *aWindow)
#define LAZY_CTOR(key)
static const wxSize defaultSize(FRAME_T aFrameType, wxWindow *aWindow)
wxWindow * findQuasiModalDialog(wxWindow *aParent)
Base window classes and related definitions.
#define KICAD_MANAGER_FRAME_NAME
std::function< void(ACTION_TOOLBAR *)> ACTION_TOOLBAR_CONTROL_FACTORY
#define DEFAULT_MAX_UNDO_ITEMS
EDA_UNITS
Definition eda_units.h:44
EVT_MENU_RANGE(ID_GERBVIEW_DRILL_FILE1, ID_GERBVIEW_DRILL_FILEMAX, GERBVIEW_FRAME::OnDrlFileHistory) EVT_MENU_RANGE(ID_GERBVIEW_ZIP_FILE1
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition frame_type.h:29
@ PANEL_PCB_GRIDS
Definition frame_type.h:102
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ PANEL_SYM_EDIT_GRIDS
Definition frame_type.h:74
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:31
@ PANEL_3DV_TOOLBARS
Definition frame_type.h:113
@ PANEL_SCH_FIELD_NAME_TEMPLATES
Definition frame_type.h:86
@ PANEL_DS_TOOLBARS
Definition frame_type.h:127
@ PANEL_SCH_TOOLBARS
Definition frame_type.h:85
@ PANEL_GBR_DISPLAY_OPTIONS
Definition frame_type.h:115
@ PANEL_3DV_OPENGL
Definition frame_type.h:111
@ PANEL_GBR_SNAPPING
Definition frame_type.h:119
@ PANEL_FP_DEFAULT_GRAPHICS_VALUES
Definition frame_type.h:97
@ PANEL_PCB_TOOLBARS
Definition frame_type.h:106
@ PANEL_SYM_SNAPPING
Definition frame_type.h:75
@ PANEL_PCB_ORIGINS_AXES
Definition frame_type.h:108
@ PANEL_PCB_EDIT_OPTIONS
Definition frame_type.h:104
@ PANEL_SCH_DISP_OPTIONS
Definition frame_type.h:80
@ PANEL_FP_DISPLAY_OPTIONS
Definition frame_type.h:90
@ PANEL_SCH_SIMULATOR
Definition frame_type.h:87
@ FRAME_SCH
Definition frame_type.h:30
@ PANEL_DS_COLORS
Definition frame_type.h:126
@ PANEL_PCB_COLORS
Definition frame_type.h:105
@ PANEL_SYM_TOOLBARS
Definition frame_type.h:78
@ PANEL_FP_SNAPPING
Definition frame_type.h:92
@ PANEL_3DV_RAYTRACING
Definition frame_type.h:112
@ PANEL_SYM_EDIT_OPTIONS
Definition frame_type.h:76
@ PANEL_FP_GRIDS
Definition frame_type.h:91
@ PANEL_SCH_EDIT_OPTIONS
Definition frame_type.h:83
@ PANEL_FP_ORIGINS_AXES
Definition frame_type.h:99
@ PANEL_SYM_DISP_OPTIONS
Definition frame_type.h:73
@ PANEL_PCB_DISPLAY_OPTS
Definition frame_type.h:101
@ PANEL_DS_SNAPPING
Definition frame_type.h:125
@ PANEL_FP_COLORS
Definition frame_type.h:94
@ PANEL_FP_DEFAULT_FIELDS
Definition frame_type.h:96
@ PANEL_SYM_COLORS
Definition frame_type.h:77
@ FRAME_PL_EDITOR
Definition frame_type.h:55
@ PANEL_SCH_SNAPPING
Definition frame_type.h:82
@ PANEL_GBR_GRIDS
Definition frame_type.h:118
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
@ FRAME_GERBER
Definition frame_type.h:53
@ PANEL_DS_GRIDS
Definition frame_type.h:124
@ FRAME_PCB_DISPLAY3D
Definition frame_type.h:43
@ PANEL_GBR_TOOLBARS
Definition frame_type.h:121
@ PANEL_FP_EDIT_OPTIONS
Definition frame_type.h:93
@ PANEL_SCH_GRIDS
Definition frame_type.h:81
@ PANEL_FP_TOOLBARS
Definition frame_type.h:95
@ PANEL_PCB_ACTION_PLUGINS
Definition frame_type.h:107
@ PANEL_3DV_DISPLAY_OPTIONS
Definition frame_type.h:110
@ PANEL_DS_DISPLAY_OPTIONS
Definition frame_type.h:123
@ PANEL_SCH_COLORS
Definition frame_type.h:84
@ PANEL_GBR_COLORS
Definition frame_type.h:120
@ PANEL_GBR_EXCELLON_OPTIONS
Definition frame_type.h:117
@ PANEL_PCB_SNAPPING
Definition frame_type.h:103
@ KICAD_MAIN_FRAME_T
Definition frame_type.h:69
static const std::string GerberFileExtension
static bool IsGerberFileExtension(const wxString &ext)
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
const wxChar *const kicadTraceKeyEvent
Flag to enable wxKeyEvent debug tracing.
const wxChar *const traceDisplayLocation
Flag to enable debug output of display positioning logic.
void ReadHotKeyConfigIntoActions(const wxString &aFileName, std::vector< TOOL_ACTION * > &aActions)
Read a hotkey config file into a list of actions.
@ ID_FILE_LIST_CLEAR
Definition id.h:58
@ ID_LANGUAGE_CHOICE
Definition id.h:62
@ ID_LANGUAGE_CHOICE_END
Definition id.h:110
@ ID_FILE1
Definition id.h:55
@ ID_AUTO_SAVE_TIMER
Definition id.h:50
EVT_MENU(ID_COMPARE_PROJECT_BRANCHES, KICAD_MANAGER_FRAME::OnCompareProjectBranches) KICAD_MANAGER_FRAME
void RemoveShutdownBlockReason(wxWindow *aWindow)
Removes any shutdown block reason set.
Definition unix/app.cpp:97
void SetWMClass(wxWindow *aWindow, const wxString &aClass)
Tag a top-level window with the freedesktop application id of its installed launcher,...
Definition wxgtk/ui.cpp:226
PGM_BASE & Pgm()
The global program "get" accessor.
LANGUAGE_DESCR LanguagesList[]
An array containing all the languages that KiCad supports.
Definition pgm_base.cpp:86
see class PGM_BASE
std::vector< FAB_LAYER_COLOR > dummy
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
SELECTION_CONDITION enableCondition
Returns true if the UI control should be enabled.
SELECTION_CONDITION checkCondition
Returns true if the UI control should be checked.
SELECTION_CONDITION showCondition
Returns true if the UI control should be shown.
ACTION_CONDITIONS & Check(const SELECTION_CONDITION &aCondition)
BACKUP_LOCATION location
Where backups, history, and autosave files live.
Implement a participant in the KIWAY alchemy.
Definition kiway.h:153
Store the common settings that are saved and loaded for each window / frame.
WINDOW_STATE state
wxString mru_path
nlohmann::json aui_state
wxString perspective
Store the window positioning/state.
unsigned int display
IFACE KIFACE_BASE kiface("pcb_test_frame", KIWAY::FACE_PCB)
static const long long MM
@ RIGHT
Toolbar on the right side of the canvas.
@ LEFT
Toolbar on the left side of the canvas.
@ TOP_AUX
Toolbar on the top of the canvas.
@ TOP_MAIN
Toolbar on the top of the canvas.
#define HOTKEYS_CHANGED
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.
INFOBAR_MESSAGE_TYPE
Sets the type of message for special handling if needed.