KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kicad_manager_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 CERN (www.cern.ch)
6 * Copyright The 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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include "kicad_id.h"
23#include "pcm.h"
24#include "pgm_kicad.h"
25#include "project_tree.h"
26#include "project_tree_pane.h"
28#include "local_history_pane.h"
30
31#include <advanced_config.h>
33#include <bitmaps.h>
34#include <build_version.h>
35#include <confirm.h>
39#include <local_history.h>
41#include <wx/msgdlg.h>
42#include <eda_base_frame.h>
43#include <executable_names.h>
44#include <file_history.h>
45#include <local_history.h>
46#include <policy_keys.h>
47#include <gestfich.h>
48#include <kiplatform/app.h>
50#include <kiplatform/ui.h>
51#include <kiplatform/policy.h>
52#include <build_version.h>
53#include <kiway.h>
54#include <kiway_mail.h>
55#include <launch_ext.h>
56#include <lockfile.h>
58#include <reporter.h>
60#include <sch_file_versions.h>
63#include <tool/action_manager.h>
64#include <tool/action_toolbar.h>
65#include <tool/common_control.h>
67#include <tool/tool_manager.h>
73#include <widgets/kistatusbar.h>
74#include <widgets/ui_common.h>
75#include <wx/ffile.h>
76#include <wx/filedlg.h>
77#include <wx/dnd.h>
78#include <wx/process.h>
79#include <wx/snglinst.h>
80#include <algorithm>
81#include <atomic>
82#include <update_manager.h>
83#include <jobs/jobset.h>
85
86#include <../pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h> // for SEXPR_BOARD_FILE_VERSION def
87
88
89#ifdef __WXMAC__
90#include <MacTypes.h>
91#include <ApplicationServices/ApplicationServices.h>
92#endif
93
94#include "kicad_manager_frame.h"
96
98
99
100#define EDITORS_CAPTION _( "Editors" )
101#define PROJECT_FILES_CAPTION _( "Project Files" )
102
103#define ID_INIT_WATCHED_PATHS 52913
104
105#define SEP() wxFileName::GetPathSeparator()
106
107
108// Menubar and toolbar event table
109BEGIN_EVENT_TABLE( KICAD_MANAGER_FRAME, EDA_BASE_FRAME )
110 // Window events
113
114 // Menu events
129 KICAD_MANAGER_FRAME::OnCompareProjectBranches )
130
131 // Range menu events
133 EVT_MENU( ID_FILE_LIST_CLEAR, KICAD_MANAGER_FRAME::OnClearFileHistory )
134
135 // Special functions
136 EVT_MENU( ID_INIT_WATCHED_PATHS, KICAD_MANAGER_FRAME::OnChangeWatchedPaths )
137
138 // Drop files event
139 EVT_DROP_FILES( KICAD_MANAGER_FRAME::OnDropFiles )
140
141END_EVENT_TABLE()
142
143// See below the purpose of this include
144#include <wx/xml/xml.h>
145
146KICAD_MANAGER_FRAME::KICAD_MANAGER_FRAME( wxWindow* parent, const wxString& title,
147 const wxPoint& pos, const wxSize& size ) :
150 m_openSavedWindows( false ),
151 m_restoredFromHistory( false ),
152 m_active_project( false ),
153 m_showHistoryPanel( false ),
154 m_projectTreePane( nullptr ),
155 m_historyPane( nullptr ),
156 m_launcher( nullptr ),
157 m_lastToolbarIconSize( 0 ),
158 m_pcmButton( nullptr ),
159 m_pcmUpdateCount( 0 )
160{
161 const int defaultLeftWinWidth = FromDIP( 250 );
162
163 m_leftWinWidth = defaultLeftWinWidth; // Default value
164 m_aboutTitle = "KiCad";
165
166 // JPC: A very ugly hack to fix an issue on Linux: if the wxbase315u_xml_gcc_custom.so is
167 // used **only** in PCM, it is not found in some cases at run time.
168 // So just use it in the main module to avoid a not found issue
169 // wxbase315u_xml_gcc_custom shared object when launching Kicad
170 wxXmlDocument dummy;
171
172 // Create the status line (bottom of the frame). Left half is for project name; right half
173 // is for Reporter (currently used by archiver/unarchiver and PCM).
174 // Note: this is a KISTATUSBAR status bar. Therefore the specified number of fields
175 // is the extra number of fields, not the full field count.
176 // We need here 2 fields: the extra fiels to display the project name, and another field
177 // to display a info (specific to Windows) using the FIELD_OFFSET_BGJOB_TEXT id offset (=1)
178 // So the extra field count is 1
179 CreateStatusBar( 2 );
182 Pgm().RegisterLibraryLoadStatusBar( (KISTATUSBAR*) GetStatusBar() );
183 GetStatusBar()->SetFont( KIUI::GetStatusFont( this ) );
184
185 // Give an icon
186 wxIcon icon;
187 wxIconBundle icon_bundle;
188
189 if( IsNightlyVersion())
190 {
191 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly, 48 ) );
192 icon_bundle.AddIcon( icon );
193 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly, 128 ) );
194 icon_bundle.AddIcon( icon );
195 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly, 256 ) );
196 icon_bundle.AddIcon( icon );
197 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly_32 ) );
198 icon_bundle.AddIcon( icon );
199 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly_16 ) );
200 icon_bundle.AddIcon( icon );
201 }
202 else
203 {
204 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad, 48 ) );
205 icon_bundle.AddIcon( icon );
206 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad, 128 ) );
207 icon_bundle.AddIcon( icon );
208 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad, 256 ) );
209 icon_bundle.AddIcon( icon );
210 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_32 ) );
211 icon_bundle.AddIcon( icon );
212 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_16 ) );
213 icon_bundle.AddIcon( icon );
214 }
215
216 SetIcons( icon_bundle );
217
218 // Load the settings
219 LoadSettings( config() );
220
221 // Left window: is the box which display tree project
222 m_projectTreePane = new PROJECT_TREE_PANE( this );
223
224 setupTools();
225 setupUIConditions();
226
227 m_toolbarSettings = GetToolbarSettings<KICAD_MANAGER_TOOLBAR_SETTINGS>( "kicad-toolbars" );
228 configureToolbars();
229 RecreateToolbars();
230 ReCreateMenuBar();
231
232 m_auimgr.SetManagedWindow( this );
233 m_auimgr.SetFlags( wxAUI_MGR_LIVE_RESIZE );
234
235 m_auimgr.AddPane( m_tbLeft, EDA_PANE().VToolbar().Name( "TopMainToolbar" ).Left().Layer( 2 ) );
236
237 // There is no wxAUIPaneInfo::SetSize(), but a trick is to use MinSize() to set the required pane width,
238 // and after give a reasonable MinSize value.
239 m_auimgr.AddPane( m_projectTreePane,
240 EDA_PANE().Palette().Name( "ProjectTree" ).Left().Layer( 1 )
241 .Caption( PROJECT_FILES_CAPTION ).PaneBorder( false )
242 .MinSize( m_leftWinWidth, -1 ).Floatable( false ).Movable( false ) );
243
244 m_historyPane = new LOCAL_HISTORY_PANE( this );
245 m_auimgr.AddPane( m_historyPane,
246 EDA_PANE().Palette().Name( "LocalHistory" ).Left().Layer( 1 ).Position( 1 )
247 .Caption( _( "Local History" ) ).PaneBorder( false )
248 .Floatable( false ).Movable( false ).CloseButton( true ).Hide() );
249
250 if( m_showHistoryPanel )
251 m_auimgr.GetPane( m_historyPane ).Show();
252
253 wxSize client_size = GetClientSize();
254 m_notebook = new wxAuiNotebook( this, wxID_ANY, wxPoint( client_size.x, client_size.y ),
255 FromDIP( wxSize( 700, 590 ) ),
256 wxAUI_NB_TOP | wxAUI_NB_CLOSE_ON_ALL_TABS | wxAUI_NB_TAB_MOVE
257 | wxAUI_NB_SCROLL_BUTTONS | wxNO_BORDER );
258
259 m_notebook->SetArtProvider( new WX_AUI_TAB_ART() );
260
261 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &KICAD_MANAGER_FRAME::onNotebookPageCloseRequest, this );
262 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSED, &KICAD_MANAGER_FRAME::onNotebookPageCountChanged, this );
263 m_launcher = new PANEL_KICAD_LAUNCHER( m_notebook );
264
265 m_notebook->Freeze();
266 m_launcher->SetClosable( false );
267 m_notebook->AddPage( m_launcher, EDITORS_CAPTION, false );
268 m_notebook->SetTabCtrlHeight( 0 );
269 m_notebook->Thaw();
270
271 m_auimgr.AddPane( m_notebook, EDA_PANE().Canvas().Name( "Editors" ).Center().Caption( EDITORS_CAPTION )
272 .PaneBorder( false ).MinSize( m_notebook->GetBestSize() ) );
273
274 m_auimgr.Update();
275
276 // Now the actual m_projectTreePane size is set, give it a reasonable min width
277 m_auimgr.GetPane( m_projectTreePane ).MinSize( defaultLeftWinWidth, FromDIP( 80 ) );
278
279
280 wxSizer* mainSizer = GetSizer();
281
282 // Only fit the initial window size the first time KiCad is run.
283 if( mainSizer && config()->m_Window.state.size_x == 0 && config()->m_Window.state.size_y == 0 )
284 {
285 Layout();
286 mainSizer->Fit( this );
287 Center();
288 }
289
290 if( ADVANCED_CFG::GetCfg().m_HideVersionFromTitle )
291 SetTitle( wxT( "KiCad" ) );
292 else
293 SetTitle( wxString( "KiCad " ) + GetMajorMinorVersion() );
294
295 // Do not let the messages window have initial focus
296 m_projectTreePane->SetFocus();
297
298 // Init for dropping files
301
302 // Gerber files
303 // Note that all gerber files are aliased as GerberFileExtension
307
308 DragAcceptFiles( true );
309}
310
311
313{
315 Unbind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
316
317 m_notebook->Unbind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &KICAD_MANAGER_FRAME::onNotebookPageCloseRequest, this );
318 m_notebook->Unbind( wxEVT_AUINOTEBOOK_PAGE_CLOSED, &KICAD_MANAGER_FRAME::onNotebookPageCountChanged, this );
319
322 Pgm().UnregisterLibraryLoadStatusBar( (KISTATUSBAR*) GetStatusBar() );
323
324 // Shutdown all running tools
325 if( m_toolManager )
326 m_toolManager->ShutdownAllTools();
327
328 if( m_pcm )
329 m_pcm->StopBackgroundUpdate();
330
331 // Stop update manager before tearing down the AUI framework. The update
332 // task runs on the thread pool and may call CallAfter on this frame, so it
333 // must complete before we uninitialize AUI or destroy child windows.
334 m_updateManager.reset();
335
336 delete m_actions;
337 delete m_toolManager;
338 delete m_toolDispatcher;
339
340 m_auimgr.UnInit();
341}
342
344{
345 if( m_notebook->GetPageCount() == 1 )
346 m_notebook->SetTabCtrlHeight( 0 );
347 else
348 m_notebook->SetTabCtrlHeight( -1 );
349}
350
351
353{
355}
356
357
359{
360 wxAuiNotebook* notebook = (wxAuiNotebook*) evt.GetEventObject();
361 wxWindow* page = notebook->GetPage( evt.GetSelection() );
362
363 if( PANEL_NOTEBOOK_BASE* panel = dynamic_cast<PANEL_NOTEBOOK_BASE*>( page ) )
364 {
365 if( panel->GetClosable() )
366 {
367 if( !panel->GetCanClose() )
368 evt.Veto();
369
370 CallAfter(
371 [this]()
372 {
374 } );
375 }
376 else
377 {
378 evt.Veto();
379 }
380 }
381}
382
383
384wxStatusBar* KICAD_MANAGER_FRAME::OnCreateStatusBar( int number, long style, wxWindowID id,
385 const wxString& name )
386{
387 KISTATUSBAR* sb = new KISTATUSBAR( number, this, id,
391
392 size_t sbFieldCnt = static_cast<size_t>( sb->GetFieldsCount() );
393 std::vector<int> sbFieldSizes( sbFieldCnt );
394
395 for( size_t i = 0; i < sbFieldCnt; i++ )
396 sbFieldSizes[i] = sb->GetStatusWidth( static_cast<int>( i ) );
397
398 // Field 0 (the project path) is the only stretchable field so it uses all the leftover width
399 // before ellipsizing. Field 1 gets a fixed width sized to its watcher text; longer content
400 // (archive progress) is ellipsized rather than allowed to steal the path's width.
401 if( sbFieldCnt > 0 )
402 sbFieldSizes[0] = -1;
403
404 if( sbFieldCnt > 1 )
405 {
406 int margin = KIUI::GetTextSize( wxT( "XX" ), sb ).x;
407 int watcherWidth = std::max( KIUI::GetTextSize( _( "Local path: monitoring folder changes" ), sb ).x,
408 KIUI::GetTextSize( _( "Network path: not monitoring folder changes" ), sb ).x );
409
410 sbFieldSizes[1] = watcherWidth + margin;
411 }
412
413 sb->SetStatusWidths( sbFieldCnt, sbFieldSizes.data() );
414
415 return sb;
416}
417
418
420{
421 // creates the PLUGIN_CONTENT_MANAGER, if not exists
422 if( m_pcm )
423 return;
424
425 m_pcm = std::make_shared<PLUGIN_CONTENT_MANAGER>(
426 [this]( int aUpdateCount )
427 {
428 if( Pgm().m_Quitting )
429 return;
430
431 m_pcmUpdateCount = aUpdateCount;
432
433 if( aUpdateCount > 0 )
434 {
436 wxS( "pcm" ),
437 _( "PCM Updates Available" ),
438 wxString::Format( _( "%d package update(s) available" ), aUpdateCount ),
439 wxT( "" ) );
440 }
441 else
442 {
443 Pgm().GetNotificationsManager().Remove( wxS( "pcm" ) );
444 }
445
446 CallAfter(
447 [this]()
448 {
450 } );
451 });
452
453 m_pcm->SetRepositoryList( kicadSettings()->m_PcmRepositories );
454}
455
456
458{
459 // Create the manager
461 m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
463
465
466 // Attach the events to the tool dispatcher
468 Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
469
470 // Register tools
471 m_toolManager->RegisterTool( new COMMON_CONTROL );
472 m_toolManager->RegisterTool( new KICAD_MANAGER_CONTROL );
473 m_toolManager->InitTools();
474}
475
476
478{
480
481 ACTION_MANAGER* manager = m_toolManager->GetActionManager();
482
483 wxASSERT( manager );
484
485 auto activeProject =
486 [this] ( const SELECTION& )
487 {
488 return m_active_project;
489 };
490
491#define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
492
493 ACTION_CONDITIONS activeProjectCond;
494 activeProjectCond.Enable( activeProject );
495
496 manager->SetConditions( ACTIONS::saveAs, activeProjectCond );
497 manager->SetConditions( KICAD_MANAGER_ACTIONS::closeProject, activeProjectCond );
498 manager->SetConditions( KICAD_MANAGER_ACTIONS::archiveProject, activeProjectCond );
499 manager->SetConditions( KICAD_MANAGER_ACTIONS::newJobsetFile, activeProjectCond );
500 manager->SetConditions( KICAD_MANAGER_ACTIONS::openJobsetFile, activeProjectCond );
501
502 auto historyCond =
503 [this]( const SELECTION& )
504 {
505 return HistoryPanelShown();
506 };
507
509
510 // These are just here for text boxes, search boxes, etc. in places such as the standard
511 // file dialogs.
515
516#undef ENABLE
517}
518
519
521{
522 return m_projectTreePane;
523}
524
525
527{
529 wxASSERT( ret );
530 return ret;
531}
532
533
535{
536 KICAD_SETTINGS* ret = dynamic_cast<KICAD_SETTINGS*>( config() );
537 wxASSERT( ret );
538 return ret;
539}
540
541
543{
544 CallAfter(
545 [&]()
546 {
547 KIFACE *schface = Kiway().KiFACE( KIWAY::FACE_SCH );
548 schface->PreloadLibraries( &Kiway() );
549
550 KIFACE *pcbface = Kiway().KiFACE( KIWAY::FACE_PCB );
551 pcbface->PreloadLibraries( &Kiway() );
552
554 } );
555}
556
557
559{
560 return GetProjectFileName();
561}
562
563
565{
567 : wxString( wxEmptyString );
568}
569
570
572{
573 wxFileName fn( GetProjectFileName() );
574
576 return fn.GetFullPath();
577}
578
579
581{
582 wxFileName fn( GetProjectFileName() );
583
585 return fn.GetFullPath();
586}
587
588
590{
591 wxFileName fn( GetProjectFileName() );
592
593 fn.SetExt( FILEEXT::PcbFileExtension );
594 return fn.GetFullPath();
595}
596
597
599{
600 wxFileName fn( GetProjectFileName() );
601
603 return fn.GetFullPath();
604}
605
606
608{
609 m_projectTreePane->ReCreateTreePrj();
610}
611
612
617
618
620{
621 return PgmTop().GetHelpFileName();
622}
623
624
625void KICAD_MANAGER_FRAME::OnSize( wxSizeEvent& event )
626{
627 if( m_auimgr.GetManagedWindow() )
628 m_auimgr.Update();
629
630 PrintPrjInfo();
631
632#if defined( _WIN32 )
633 KISTATUSBAR* statusBar = static_cast<KISTATUSBAR*>( GetStatusBar() );
635#endif
636
637 event.Skip();
638}
639
640
642{
643 // All fileNames are now in m_AcceptedFiles vector.
644 // Check if contains a project file name and load project.
645 // If not, open files in dedicated app.
646 for( const wxFileName& fileName : m_AcceptedFiles )
647 {
648 wxString ext = fileName.GetExt();
649
651 {
652 wxString fn = fileName.GetFullPath();
653 m_toolManager->RunAction<wxString*>( *m_acceptedExts.at( fileName.GetExt() ), &fn );
654
655 return;
656 }
657 }
658
659 // Then stock gerber files in gerberFiles and run action for other files.
660 wxString gerberFiles;
661
662 // Gerbview editor should be able to open Gerber and drill files
663 for( const wxFileName& fileName : m_AcceptedFiles )
664 {
665 wxString ext = fileName.GetExt();
666
670 {
671 gerberFiles += wxT( '\"' );
672 gerberFiles += fileName.GetFullPath() + wxT( '\"' );
673 gerberFiles = gerberFiles.Pad( 1 );
674 }
675 else
676 {
677 wxString fn = fileName.GetFullPath();
678 m_toolManager->RunAction<wxString*>( *m_acceptedExts.at( fileName.GetExt() ), &fn );
679 }
680 }
681
682 // Execute Gerbviewer
683 if( !gerberFiles.IsEmpty() )
684 {
685 wxString fullEditorName = FindKicadFile( GERBVIEW_EXE );
686
687 if( wxFileExists( fullEditorName ) )
688 {
689 wxString command = fullEditorName + " " + gerberFiles;
690 m_toolManager->RunAction<wxString*>( *m_acceptedExts.at( FILEEXT::GerberFileExtension ), &command );
691 }
692 }
693}
694
695
696bool KICAD_MANAGER_FRAME::canCloseWindow( wxCloseEvent& aEvent )
697{
698 KICAD_SETTINGS* settings = kicadSettings();
700
701 for( size_t i = 0; i < m_notebook->GetPageCount(); i++ )
702 {
703 wxWindow* page = m_notebook->GetPage( i );
704
705 if( PANEL_NOTEBOOK_BASE* panel = dynamic_cast<PANEL_NOTEBOOK_BASE*>( page ) )
706 {
707 if( !panel->GetCanClose() )
708 return false;
709 }
710 }
711
712 // CloseProject will recursively ask all the open editors if they need to save changes.
713 // If any of them cancel then we need to cancel closing the KICAD_MANAGER_FRAME.
714 if( CloseProject( true ) )
715 {
716 // Don't propagate event to frames which have already been closed
717 aEvent.StopPropagation();
718
719 return true;
720 }
721 else
722 {
723 if( aEvent.CanVeto() )
724 aEvent.Veto();
725
726 return false;
727 }
728}
729
730
732{
733#ifdef _WINDOWS_
734 // For some obscure reason, on Windows, when killing Kicad from the Windows task manager
735 // if a editor frame (schematic, library, board editor or fp editor) is open and has
736 // some edition to save, OnCloseWindow is run twice *at the same time*, creating race
737 // conditions between OnCloseWindow() code.
738 // Therefore I added (JPC) a ugly hack to discard the second call (unwanted) during
739 // execution of the first call (only one call is right).
740 // Note also if there is no change made in editors, this behavior does not happen.
741 static std::atomic<unsigned int> lock_close_event( 0 );
742
743 if( ++lock_close_event > 1 ) // Skip extra calls
744 {
745 return;
746 }
747#endif
748
749 m_projectTreePane->Show( false );
750 Pgm().m_Quitting = true;
751
752 Destroy();
753
754#ifdef _WINDOWS_
755 lock_close_event = 0; // Reenable event management
756#endif
757}
758
759
761{
763
764 if( !aIsExplicitUserSave && !cfg.ShouldAutoSave() )
765 return;
766
767 cfg.m_OpenJobSets.clear();
768
769 for( size_t i = 0; i < m_notebook->GetPageCount(); i++ )
770 {
771 if( PANEL_JOBSET* jobset = dynamic_cast<PANEL_JOBSET*>( m_notebook->GetPage( i ) ) )
772 {
773 wxFileName jobsetFn( jobset->GetFilePath() );
774 jobsetFn.MakeRelativeTo( Prj().GetProjectPath() );
775 cfg.m_OpenJobSets.emplace_back( jobsetFn.GetFullPath() );
776 }
777 }
778
779 cfg.SaveToFile( Prj().GetProjectPath() );
780}
781
782
783void KICAD_MANAGER_FRAME::OnExit( wxCommandEvent& event )
784{
785 Close( true );
786}
787
788
790{
791 if( !Kiway().PlayersClose( false ) )
792 return false;
793
794 // Abort any in-progress background load, since the threads depend on the project not changing
795 KIFACE *schface = Kiway().KiFACE( KIWAY::FACE_SCH );
796 schface->CancelPreload();
797
798 KIFACE *pcbface = Kiway().KiFACE( KIWAY::FACE_PCB );
799 pcbface->CancelPreload();
800
801 // Save the project file for the currently loaded project.
802 if( m_active_project )
803 {
805
806 if( Prj().GetLocalSettings().ShouldAutoSave() && Prj().GetProjectFile().ShouldAutoSave() )
807 {
809
810 if( aSave )
811 mgr.SaveProject();
812 }
813
814 // Ensure the Last_Save tag is at HEAD before closing. This handles the case where
815 // autosave commits were made after the last explicit save - without this, the next
816 // project load would offer to restore the autosave state, which is incorrect after
817 // a clean close.
818 wxString projPath = Prj().GetProjectPath();
819
820 // Wait for any in-flight autosave so the HEAD check below isn't racing it.
822
823 if( Pgm().GetCommonSettings()->AutosaveUsesLocalHistory()
824 && !projPath.IsEmpty() && Kiway().LocalHistory().HistoryExists( projPath ) )
825 {
826 if( Kiway().LocalHistory().HeadNewerThanLastSave( projPath ) )
827 {
828 // Tag unconditionally: even on no-op snapshots Last_Save must anchor at HEAD.
829 Kiway().LocalHistory().CommitFullProjectSnapshot( projPath, wxS( "Close" ) );
830 Kiway().LocalHistory().TagSave( projPath, wxS( "project" ) );
831 }
832 }
833
834 // The editors clean up autosaves for sheets actually dirtied in their session.
835 // Anything still on disk here was deferred by the user in the recovery dialog
836 // and must survive so the dialog can offer it again on the next open.
837
838 m_active_project = false;
839 // Enforce local history size limit (if enabled) once all pending saves/backups are done.
840 if( Pgm().GetCommonSettings()
841 && Pgm().GetCommonSettings()->AutosaveUsesLocalHistory() )
842 {
843 unsigned long long int limit = Pgm().GetCommonSettings()->m_Backup.limit_total_size;
844
845 if( limit > 0 )
846 {
847 WX_PROGRESS_REPORTER reporter( this, _( "Local History" ), 3, PR_NO_ABORT );
848 Kiway().LocalHistory().EnforceSizeLimit( Prj().GetProjectPath(), (size_t) limit, &reporter );
849 }
850 }
851
852 // Unregister the project saver before unloading the project to prevent
853 // dangling references
855
856 mgr.UnloadProject( &Prj() );
857 }
858
859 SetStatusText( "" );
860
861 // Traverse pages in reverse order so deleting them doesn't mess up our iterator.
862 for( int i = (int) m_notebook->GetPageCount() - 1; i >= 0; i-- )
863 {
864 wxWindow* page = m_notebook->GetPage( i );
865
866 if( PANEL_NOTEBOOK_BASE* panel = dynamic_cast<PANEL_NOTEBOOK_BASE*>( page ) )
867 {
868 if( panel->GetProjectTied() )
869 m_notebook->DeletePage( i );
870 }
871 }
872
873 m_projectTreePane->EmptyTreePrj();
875
876 return true;
877}
878
879
880void KICAD_MANAGER_FRAME::OpenJobsFile( const wxFileName& aFileName, bool aCreate, bool aResaveProjectPreferences )
881{
882 for( size_t i = 0; i < m_notebook->GetPageCount(); i++ )
883 {
884 if( PANEL_JOBSET* panel = dynamic_cast<PANEL_JOBSET*>( m_notebook->GetPage( i ) ) )
885 {
886 if( aFileName.GetFullPath() == panel->GetFilePath() )
887 {
888 m_notebook->SetSelection( i );
889 return;
890 }
891 }
892 }
893
894 try
895 {
896 std::unique_ptr<JOBSET> jobsFile = std::make_unique<JOBSET>( aFileName.GetFullPath().ToStdString() );
897
898 jobsFile->LoadFromFile();
899
900 if( aCreate && !aFileName.FileExists() )
901 {
902 JOBSET_DESTINATION* dest = jobsFile->AddNewDestination( JOBSET_DESTINATION_T::FOLDER );
903 dest->m_outputHandler->SetOutputPath( aFileName.GetName() );
904 jobsFile->SaveToFile( wxEmptyString, true );
905 }
906
907 PANEL_JOBSET* jobPanel = new PANEL_JOBSET( m_notebook, this, std::move( jobsFile ) );
908 jobPanel->SetProjectTied( true );
909 jobPanel->SetClosable( true );
910 m_notebook->AddPage( jobPanel, aFileName.GetFullName(), true );
912
913 if( aResaveProjectPreferences )
915 }
916 catch( ... )
917 {
918 DisplayErrorMessage( this, _( "Error opening jobs file" ) );
919 }
920}
921
922
923bool KICAD_MANAGER_FRAME::LoadProject( const wxFileName& aProjectFileName )
924{
925 // The project file should be valid by the time we get here or something has gone wrong.
926 if( !aProjectFileName.Exists() )
927 return false;
928
929 wxString fullPath = aProjectFileName.GetFullPath();
930
931 // Check if a lock file already exists BEFORE we try to acquire it. We only want to warn
932 // the user if the lock file pre-existed, not if we're about to create it ourselves.
933 // The actual lock acquisition happens in SETTINGS_MANAGER::LoadProject().
934 wxFileName lockFn( fullPath );
935 lockFn.SetName( FILEEXT::LockFilePrefix + lockFn.GetName() );
936 lockFn.SetExt( lockFn.GetExt() + wxS( "." ) + FILEEXT::LockFileExtension );
937 bool lockFilePreExisted = lockFn.FileExists();
938
939 bool lockOverrideGranted = false;
940
941 if( lockFilePreExisted )
942 {
943 // A lock file exists. Create a LOCKFILE to read who owns it and decide what to do.
944 LOCKFILE lockFile( fullPath );
945
946 if( !lockFile.Valid() && lockFile.IsLockedByMe() )
947 {
948 // If we cannot acquire the lock but we appear to be the one who locked it, check to
949 // see if there is another KiCad instance running. If not, then we can override the
950 // lock. This could happen if KiCad crashed or was interrupted.
951 if( !Pgm().SingleInstance()->IsAnotherRunning() )
952 lockFile.OverrideLock();
953 }
954
955 if( !lockFile.Valid() )
956 {
957 wxString msg;
958 msg.Printf( _( "Project '%s' is already open by '%s' at '%s'." ),
959 fullPath,
960 lockFile.GetUsername(),
961 lockFile.GetHostname() );
962
963 if( !AskOverrideLock( this, msg ) )
964 return false; // User clicked Cancel - abort project loading entirely
965
966 lockFile.OverrideLock();
967 lockOverrideGranted = true;
968 }
969
970 // The LOCKFILE goes out of scope here and releases/removes the lock file.
971 // SETTINGS_MANAGER::LoadProject() will create the actual persistent lock.
972 }
973
974 // Any open KIFACE's must be closed if they are not part of the new project.
975 // (We never want a KIWAY_PLAYER open on a KIWAY that isn't in the same project.)
976 // User is prompted here to close those KIWAY_PLAYERs:
977 if( !CloseProject( true ) )
978 return false;
979
980 m_active_project = true;
981
982 // NB: when loading a legacy project SETTINGS_MANAGER::LoadProject() will convert it to
983 // current extension. Be very careful with aProjectFileName vs. Prj().GetProjectPath()
984 // from here on out.
985
986 Pgm().GetSettingsManager().LoadProject( fullPath );
987
988 // Propagate lock override decision to the loaded project
989 if( lockOverrideGranted )
990 Prj().SetLockOverrideGranted( true );
991
992 LoadWindowState( aProjectFileName.GetFullName() );
993
994 if( aProjectFileName.IsDirWritable() )
995 SetMruPath( Prj().GetProjectPath() );
996
997 if( Pgm().GetCommonSettings()->AutosaveUsesLocalHistory()
998 && Kiway().LocalHistory().HeadNewerThanLastSave( Prj().GetProjectPath() ) )
999 {
1000 wxString head = Kiway().LocalHistory().GetHeadHash( Prj().GetProjectPath() );
1001
1002 KICAD_MESSAGE_DIALOG dlg( this, _( "KiCad found unsaved changes from your last session that are newer than "
1003 "the saved project files." ),
1004 _( "Recover Unsaved Changes" ), wxYES_NO | wxICON_QUESTION );
1005
1006 dlg.SetExtendedMessage( _( "This can happen if your previous session ended unexpectedly.\n\n"
1007 "Choose 'Restore' to recover those changes, or 'Discard' to keep the "
1008 "currently saved files." ) );
1009
1010 dlg.SetYesNoLabels( _( "Restore" ), _( "Discard" ) );
1011
1012 if( dlg.ShowModal() == wxID_YES )
1013 {
1014 // The dialog above is the confirmation, skip RestoreCommit's own prompt.
1015 Kiway().LocalHistory().RestoreCommit( Prj().GetProjectPath(), head, this, false );
1016 }
1017 else
1018 {
1019 // User declined; commit on-disk state and tag unconditionally so Last_Save anchors
1020 // at HEAD even if no new commit was needed.
1021 Kiway().LocalHistory().CommitFullProjectSnapshot( Prj().GetProjectPath(), wxS( "Declined restore" ) );
1022 Kiway().LocalHistory().TagSave( Prj().GetProjectPath(), wxS( "project" ) );
1023 }
1024 }
1025
1026 // Save history & window state to disk now. Don't wait around for a crash.
1027 KICAD_SETTINGS* settings = kicadSettings();
1028 SaveSettings( settings );
1029 settings->SaveToFile( Pgm().GetSettingsManager().GetPathForSettingsFile( settings ) );
1030
1031 m_projectTreePane->ReCreateTreePrj();
1032 m_historyPane->RefreshHistory( Prj().GetProjectPath() );
1033
1034 for( const wxString& jobset : Prj().GetLocalSettings().m_OpenJobSets )
1035 {
1036 wxFileName jobsetFn( jobset );
1037 jobsetFn.MakeAbsolute( Prj().GetProjectPath() );
1038
1039 if( jobsetFn.Exists() )
1040 OpenJobsFile( jobsetFn.GetFullPath(), false, false );
1041 }
1042
1043 // Always start with the apps page
1044 m_notebook->SetSelection( 0 );
1045
1046 // Rebuild the list of watched paths.
1047 // however this is possible only when the main loop event handler is running,
1048 // so we use it to run the rebuild function.
1049 wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_INIT_WATCHED_PATHS );
1050
1051 wxPostEvent( this, cmd );
1052
1053 PrintPrjInfo();
1054
1055 KIPLATFORM::APP::RegisterApplicationRestart( aProjectFileName.GetFullPath() );
1056 m_openSavedWindows = true;
1057
1058 KIPLATFORM::ENV::AddToRecentDocs( aProjectFileName.GetFullPath() );
1059
1060 // Now that we have a new project, trigger a library preload, which will load in any
1061 // project-specific symbol and footprint libraries into the manager
1063 return true;
1064}
1065
1066
1067void KICAD_MANAGER_FRAME::CreateNewProject( const wxFileName& aProjectFileName, bool aCreateStubFiles )
1068{
1069 wxCHECK_RET( aProjectFileName.DirExists() && aProjectFileName.IsDirWritable(),
1070 "Project folder must exist and be writable to create a new project." );
1071
1072 // If the project is legacy, convert it
1073 if( !aProjectFileName.FileExists() )
1074 {
1075 wxFileName legacyPro( aProjectFileName );
1076 legacyPro.SetExt( FILEEXT::LegacyProjectFileExtension );
1077
1078 if( legacyPro.FileExists() )
1079 {
1080 GetSettingsManager()->LoadProject( legacyPro.GetFullPath() );
1082
1083 wxRemoveFile( legacyPro.GetFullPath() );
1084 }
1085 else
1086 {
1087 // Copy template project file from template folder.
1088 wxString srcFileName = sys_search().FindValidPath( "kicad.kicad_pro" );
1089
1090 wxFileName destFileName( aProjectFileName );
1091 destFileName.SetExt( FILEEXT::ProjectFileExtension );
1092
1093 // Create a minimal project file if the template project file could not be copied
1094 if( !wxFileName::FileExists( srcFileName )
1095 || !wxCopyFile( srcFileName, destFileName.GetFullPath() ) )
1096 {
1097 wxFFile file( destFileName.GetFullPath(), "wb" );
1098
1099 if( file.IsOpened() )
1100 file.Write( wxT( "{\n}\n") );
1101
1102 // wxFFile dtor will close the file
1103 }
1104 }
1105 }
1106
1107 // Create a "stub" for a schematic root sheet and a board if requested.
1108 // It will avoid messages from the schematic editor or the board editor to create a new file
1109 // And forces the user to create main files under the right name for the project manager
1110 if( aCreateStubFiles )
1111 {
1112 wxFileName fn( aProjectFileName.GetFullPath() );
1114
1115 // If a <project>.kicad_sch file does not exist, create a "stub" file ( minimal schematic
1116 // file ).
1117 if( !fn.FileExists() )
1118 {
1119 wxFFile file( fn.GetFullPath(), "wb" );
1120
1121 if( file.IsOpened() )
1122 {
1123 file.Write( wxString::Format( "(kicad_sch\n"
1124 "\t(version %d)\n"
1125 "\t(generator \"eeschema\")\n"
1126 "\t(generator_version \"%s\")\n"
1127 "\t(uuid %s)\n"
1128 "\t(paper \"A4\")\n"
1129 "\t(lib_symbols)\n"
1130 "\t(sheet_instances\n"
1131 "\t\t(path \"/\"\n"
1132 "\t\t\t(page \"1\")\n"
1133 "\t\t)\n"
1134 "\t)\n"
1135 "\t(embedded_fonts no)\n"
1136 ")",
1139 KIID().AsString() ) );
1140 }
1141
1142 // wxFFile dtor will close the file
1143 }
1144
1145 // If a <project>.kicad_pcb or <project>.brd file does not exist,
1146 // create a .kicad_pcb "stub" file
1147 fn.SetExt( FILEEXT::KiCadPcbFileExtension );
1148 wxFileName leg_fn( fn );
1149 leg_fn.SetExt( FILEEXT::LegacyPcbFileExtension );
1150
1151 if( !fn.FileExists() && !leg_fn.FileExists() )
1152 {
1153 wxFFile file( fn.GetFullPath(), "wb" );
1154
1155 if( file.IsOpened() )
1156 {
1157 // Create a small dummy file as a stub for pcbnew:
1158 file.Write( wxString::Format( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version \"%s\")\n)",
1160 }
1161
1162 // wxFFile dtor will close the file
1163 }
1164 }
1165
1166 // Save history & window state to disk now. Don't wait around for a crash.
1167 KICAD_SETTINGS* settings = kicadSettings();
1168 SaveSettings( settings );
1169 settings->SaveToFile( Pgm().GetSettingsManager().GetPathForSettingsFile( settings ) );
1170
1171 m_openSavedWindows = true;
1172}
1173
1174
1176{
1177 git_repository* repo = ( m_projectTreePane && m_projectTreePane->m_TreeProject )
1178 ? m_projectTreePane->m_TreeProject->GetGitRepo()
1179 : nullptr;
1180
1181 if( !repo )
1182 {
1183 wxMessageBox( _( "Compare Project Branches requires a project that is "
1184 "tracked by git." ),
1185 _( "Compare Branches" ),
1186 wxOK | wxICON_INFORMATION, this );
1187 return;
1188 }
1189
1190 // Seed the picker with the conventional "MR" pair: an integration branch
1191 // (main/master) as the base, the current branch as the head. Resolve the
1192 // current branch from HEAD so a checked-out feature branch lands at
1193 // index 1 (where DIALOG_GIT_MR_REVIEW selects its head ref) instead of
1194 // the literal "HEAD" string.
1195 wxString currentBranch;
1196 git_reference* head = nullptr;
1197
1198 if( git_repository_head( &head, repo ) == 0 )
1199 {
1200 const char* shorthand = git_reference_shorthand( head );
1201
1202 if( shorthand && *shorthand )
1203 currentBranch = wxString::FromUTF8( shorthand );
1204
1205 git_reference_free( head );
1206 }
1207
1208 std::vector<wxString> refs;
1209
1210 auto pushUnique = [&]( const wxString& aRef )
1211 {
1212 if( aRef.IsEmpty() )
1213 return;
1214
1215 for( const wxString& existing : refs )
1216 {
1217 if( existing == aRef )
1218 return;
1219 }
1220
1221 refs.push_back( aRef );
1222 };
1223
1224 pushUnique( wxS( "main" ) );
1225 pushUnique( wxS( "master" ) );
1226 pushUnique( currentBranch );
1227 pushUnique( wxS( "HEAD" ) );
1228
1229 DIALOG_GIT_MR_REVIEW dlg( this, repo, refs );
1230 dlg.ShowModal();
1231}
1232
1233
1235{
1236 // show all files in file dialog (in Kicad all files are editable texts):
1237 wxString wildcard = FILEEXT::AllFilesWildcard();
1238
1239 wxString default_dir = Prj().GetProjectPath();
1240
1241 wxFileDialog dlg( this, _( "Edit File in Text Editor" ), default_dir, wxEmptyString, wildcard,
1242 wxFD_OPEN );
1243
1245
1246 if( dlg.ShowModal() == wxID_CANCEL )
1247 return;
1248
1249 wxString filename = dlg.GetPath();
1250
1251 if( !dlg.GetPath().IsEmpty() && !Pgm().GetTextEditor().IsEmpty() )
1252 m_toolManager->RunAction<wxString*>( KICAD_MANAGER_ACTIONS::openTextEditor, &filename );
1253}
1254
1255
1256void KICAD_MANAGER_FRAME::OnEditAdvancedCfg( wxCommandEvent& WXUNUSED( event ) )
1257{
1258 DIALOG_EDIT_CFG dlg( this );
1259 dlg.ShowModal();
1260}
1261
1262
1264{
1265 m_projectTreePane->ReCreateTreePrj();
1266}
1267
1268
1270{
1271 // call my base class
1273
1274 // tooltips in toolbars
1276 m_launcher->CreateLaunchers();
1277
1278 // update captions
1279 int pageId = m_notebook->FindPage( m_launcher );
1280
1281 if( pageId != wxNOT_FOUND )
1282 m_notebook->SetPageText( pageId, EDITORS_CAPTION );
1283
1284 m_auimgr.GetPane( m_projectTreePane ).Caption( PROJECT_FILES_CAPTION );
1285 m_auimgr.Update();
1286
1287 m_projectTreePane->FileWatcherReset();
1288
1289 PrintPrjInfo();
1290}
1291
1292
1294{
1296
1297 if( m_pcm && ( aFlags & ENVVARS_CHANGED ) )
1298 m_pcm->ReadEnvVar();
1299
1300 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
1301
1302 if( m_lastToolbarIconSize == 0
1304 {
1307 }
1308
1309 m_projectTreePane->ReCreateTreePrj();
1310}
1311
1312
1314{
1315 wxString file = GetProjectFileName();
1316
1317 // empty file string means no project loaded
1318 if( !Prj().IsNullProject() &&
1319 Prj().GetProjectLock() == nullptr )
1320 {
1321 LOCKFILE lockFile( file );
1322
1323 if( !lockFile.Valid() && lockFile.IsLockedByMe() )
1324 {
1325 // If we cannot acquire the lock but we appear to be the one who
1326 // locked it, check to see if there is another KiCad instance running.
1327 // If there is not, then we can override the lock. This could happen if
1328 // KiCad crashed or was interrupted
1329 if( !Pgm().SingleInstance()->IsAnotherRunning() )
1330 {
1331 lockFile.OverrideLock();
1332 }
1333 }
1334
1335 if( !lockFile.Valid() )
1336 {
1337 wxString msg;
1338 msg.Printf( _( "Project '%s' is already open by '%s' at '%s'." ),
1339 file,
1340 lockFile.GetUsername(),
1341 lockFile.GetHostname() );
1342
1343 if( AskOverrideLock( this, msg ) )
1344 {
1345 lockFile.OverrideLock();
1346 }
1347 }
1348
1349 Prj().SetReadOnly( !lockFile.Valid() || Prj().GetProjectFile().IsReadOnly() );
1350 Prj().SetProjectLock( new LOCKFILE( std::move( lockFile ) ) );
1351 }
1352
1353 wxString title;
1354
1355 if( !file.IsEmpty() )
1356 {
1357 wxFileName fn( file );
1358
1359 title = fn.GetName();
1360
1361 if( Prj().IsReadOnly() )
1362 title += wxS( " " ) + _( "[Read Only]" );
1363 }
1364 else
1365 {
1366 title = _( "[no project loaded]" );
1367 }
1368
1369 if( ADVANCED_CFG::GetCfg().m_HideVersionFromTitle )
1370 title += wxT( " \u2014 " ) + wxString( wxS( "KiCad" ) );
1371 else
1372 title += wxT( " \u2014 " ) + wxString( wxS( "KiCad " ) ) + GetMajorMinorVersion();
1373
1374 SetTitle( title );
1375
1376 // Register project file saver. Ensures project file participates in
1377 // autosave history commits without affecting dirty state.
1379 [this]( const wxString& aProjectPath, std::vector<HISTORY_FILE_DATA>& aFileData )
1380 {
1381 Prj().SaveToHistory( aProjectPath, aFileData );
1382 },
1384}
1385
1386
1388{
1390
1391 auto settings = dynamic_cast<KICAD_SETTINGS*>( aCfg );
1392
1393 wxCHECK( settings, /*void*/ );
1394
1395 m_leftWinWidth = settings->m_LeftWinWidth;
1396 m_showHistoryPanel = settings->m_ShowHistoryPanel;
1397}
1398
1399
1401{
1403
1404 KICAD_SETTINGS* settings = dynamic_cast<KICAD_SETTINGS*>( aCfg );
1405
1406 wxCHECK( settings, /*void*/ );
1407
1408 settings->m_LeftWinWidth = m_projectTreePane->GetSize().x;
1409 settings->m_ShowHistoryPanel = m_historyPane && m_auimgr.GetPane( m_historyPane ).IsShown();
1410
1411 if( !m_isClosing )
1413}
1414
1415
1417{
1418 // wxStatusBar's wxELLIPSIZE_MIDDLE flag doesn't work (at least on Mac).
1419
1420 wxString status = wxString::Format( _( "Project: %s" ), Prj().GetProjectFullName() );
1421 KISTATUSBAR* statusBar = static_cast<KISTATUSBAR*>( GetStatusBar() );
1422 statusBar->SetEllipsedTextField( status, 0 );
1423}
1424
1425
1430
1431
1432void KICAD_MANAGER_FRAME::OnIdle( wxIdleEvent& aEvent )
1433{
1439 if( !m_openSavedWindows )
1440 return;
1441
1442 m_openSavedWindows = false;
1443
1444 if( Pgm().GetCommonSettings()->m_Session.remember_open_files )
1445 {
1446 int previousOpenCount = std::count_if( Prj().GetLocalSettings().m_files.begin(),
1447 Prj().GetLocalSettings().m_files.end(),
1448 [&]( const PROJECT_FILE_STATE& f )
1449 {
1450 return !f.fileName.EndsWith( FILEEXT::ProjectFileExtension ) && f.open;
1451 } );
1452
1453 if( previousOpenCount > 0 )
1454 {
1455 APP_PROGRESS_DIALOG progressReporter( _( "Restoring session" ), wxEmptyString, previousOpenCount, this );
1456
1457 // We don't currently support opening more than one view per file
1458 std::set<wxString> openedFiles;
1459
1460 int i = 0;
1461
1462 for( const PROJECT_FILE_STATE& file : Prj().GetLocalSettings().m_files )
1463 {
1464 if( file.open && !openedFiles.count( file.fileName ) )
1465 {
1466 progressReporter.Update( i++, wxString::Format( _( "Restoring '%s'" ), file.fileName ) );
1467
1468 openedFiles.insert( file.fileName );
1469 wxFileName fn( file.fileName );
1470
1471 if( fn.GetExt() == FILEEXT::LegacySchematicFileExtension
1472 || fn.GetExt() == FILEEXT::KiCadSchematicFileExtension )
1473 {
1475 }
1476 else if( fn.GetExt() == FILEEXT::LegacyPcbFileExtension
1477 || fn.GetExt() == FILEEXT::KiCadPcbFileExtension )
1478 {
1480 }
1481 }
1482
1483 wxYield();
1484 }
1485 }
1486 }
1487
1488 // clear file states regardless if we opened windows or not due to setting
1490
1491 // After restore from history, mark open editors as dirty so user is prompted to save
1493 {
1494 m_restoredFromHistory = false;
1495
1496 // Mark schematic editor as dirty if open
1497 if( KIWAY_PLAYER* schFrame = Kiway().Player( FRAME_SCH, false ) )
1498 schFrame->OnModify();
1499
1500 // Mark PCB editor as dirty if open
1501 if( KIWAY_PLAYER* pcbFrame = Kiway().Player( FRAME_PCB_EDITOR, false ) )
1502 pcbFrame->OnModify();
1503 }
1504
1505 KICAD_SETTINGS* settings = kicadSettings();
1506
1508 && settings->m_PcmUpdateCheck )
1509 {
1510 if( !m_pcm )
1511 CreatePCM();
1512
1513 m_pcm->RunBackgroundUpdate();
1514 }
1515
1516#ifdef KICAD_UPDATE_CHECK
1517 if( !m_updateManager && settings->m_KiCadUpdateCheck )
1518 {
1519 m_updateManager = std::make_unique<UPDATE_MANAGER>();
1520 m_updateManager->CheckForUpdate( this );
1521 }
1522#endif
1523
1524 // This little diddy is needed to get the window put into the Mac dock icon's context menu.
1525 Raise();
1526}
1527
1528
1530{
1531 m_pcmButton = aButton;
1532
1534}
1535
1536
1538{
1539 if( m_pcmButton )
1540 {
1541 if( m_pcmUpdateCount > 0 )
1542 {
1543 m_pcmButton->SetShowBadge( true );
1544 m_pcmButton->SetBadgeText( wxString::Format( "%d", m_pcmUpdateCount ) );
1545 }
1546 else
1547 {
1548 m_pcmButton->SetShowBadge( false );
1549 }
1550
1551 m_pcmButton->Refresh();
1552 }
1553}
1554
1555
1557{
1558 // No idea why, but the same mechanism used in EDA_DRAW_FRAME doesn't work here
1559 // the only thing that seems to work is to blow it all up and start from scratch.
1560 m_auimgr.DetachPane( m_tbLeft );
1561 delete m_tbLeft;
1562 m_tbLeft = nullptr;
1564 m_auimgr.AddPane( m_tbLeft, EDA_PANE().HToolbar().Name( "TopMainToolbar" ).Left().Layer( 2 ) );
1565
1566 m_auimgr.Update();
1567}
1568
1569
1571{
1572 wxAuiPaneInfo& pane = m_auimgr.GetPane( m_historyPane );
1573 bool show = !pane.IsShown();
1574 pane.Show( show );
1575
1576 if( show )
1577 m_historyPane->RefreshHistory( Prj().GetProjectPath() );
1578
1579 m_auimgr.Update();
1580}
1581
1582
1584{
1585 if( !Kiway().PlayersClose( true ) )
1586 return;
1587
1588 if( Kiway().LocalHistory().RestoreCommit( Prj().GetProjectPath(), aHash, this ) )
1589 {
1590 m_restoredFromHistory = true; // Mark editors dirty when they reopen
1591 }
1592
1593 m_projectTreePane->ReCreateTreePrj();
1594 m_openSavedWindows = true;
1595 m_historyPane->RefreshHistory( Prj().GetProjectPath() );
1596}
1597
1598
1600{
1601 return m_historyPane && m_auimgr.GetPane( m_historyPane ).IsShown();
1602}
const char * name
constexpr EDA_IU_SCALE unityScale
Definition base_units.h:124
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:100
@ icon_kicad_nightly
@ icon_kicad_nightly_32
@ icon_kicad_nightly_16
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
bool IsNightlyVersion()
Check if the build is meant to be nightly.
static TOOL_ACTION paste
Definition actions.h:76
static TOOL_ACTION saveAs
Definition actions.h:55
static TOOL_ACTION copy
Definition actions.h:74
static TOOL_ACTION cut
Definition actions.h:73
Manage TOOL_ACTION objects.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
wxProgressDialog with the option to also update the application progress on the taskbar
virtual bool Update(int aValue, const wxString &aNewMsg=wxEmptyString, bool *aSkip=nullptr) override
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
void UnregisterStatusBar(KISTATUSBAR *aStatusBar)
Removes status bar from handling.
void RegisterStatusBar(KISTATUSBAR *aStatusBar)
Add a status bar for handling.
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
Handle actions that are shared between different applications.
APPEARANCE m_Appearance
AUTO_BACKUP m_Backup
Git PR-style branch comparison dialog (Phase 10).
int ShowModal() override
The base frame for deriving all KiCad main window classes.
void LoadWindowState(const wxString &aFileName)
void CommonSettingsChanged(int aFlags) override
Notification event that some of the common (suite-wide) settings have changed.
void ShowChangedLanguage() override
Redraw the menus and what not in current language.
virtual void setupUIConditions()
Setup the UI conditions for the various actions and their controls in this frame.
std::vector< wxFileName > m_AcceptedFiles
SETTINGS_MANAGER * GetSettingsManager() const
wxAuiManager m_auimgr
virtual void RecreateToolbars()
virtual void LoadSettings(APP_SETTINGS_BASE *aCfg)
Load common frame parameters from a configuration file.
ACTION_TOOLBAR * m_tbLeft
std::map< const wxString, TOOL_ACTION * > m_acceptedExts
Associate file extensions with action to execute.
virtual void SaveSettings(APP_SETTINGS_BASE *aCfg)
Save common frame parameters to a configuration data file.
void SetMruPath(const wxString &aPath)
bool m_isClosing
Set by the close window event handler after frames are asked if they can close.
Specialization of the wxAuiPaneInfo class for KiCad panels.
void SetOutputPath(const wxString &aPath)
Definition jobs_output.h:56
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
Calls Store() and then writes the contents of the JSON document to a file.
static TOOL_ACTION viewDroppedGerbers
static TOOL_ACTION loadProject
static TOOL_ACTION showLocalHistory
static TOOL_ACTION editSchematic
static TOOL_ACTION openTextEditor
static TOOL_ACTION archiveProject
static TOOL_ACTION closeProject
static TOOL_ACTION openJobsetFile
static TOOL_ACTION newJobsetFile
Handle actions in the kicad manager frame.
The main KiCad project manager frame.
std::shared_ptr< PLUGIN_CONTENT_MANAGER > m_pcm
void SetPcmButton(BITMAP_BUTTON *aButton)
void CreateNewProject(const wxFileName &aProjectFileName, bool aCreateStubFiles=true)
Creates a new project by setting up and initial project, schematic, and board files.
const SEARCH_STACK & sys_search() override
Return a SEARCH_STACK pertaining to entire program.
void OnImportEasyEdaProFiles(wxCommandEvent &event)
Open dialog to import EasyEDA Pro schematic and board files.
void ProjectChanged() override
Notification event that the project has changed.
void ShowChangedLanguage() override
Redraw the menus and what not in current language.
const wxString SchLegacyFileName()
wxWindow * GetToolCanvas() const override
Canvas access.
void OnImportEasyEdaFiles(wxCommandEvent &event)
Open dialog to import EasyEDA Std schematic and board files.
void OnImportPadsProjectFiles(wxCommandEvent &event)
Open dialog to import PADS Logic schematic and PCB files.
void OnImportAltiumProjectFiles(wxCommandEvent &event)
Open dialog to import Altium project files.
const wxString GetProjectFileName() const
KICAD_SETTINGS * kicadSettings() const
void OnImportEagleFiles(wxCommandEvent &event)
Open dialog to import Eagle schematic and board files.
wxStatusBar * OnCreateStatusBar(int number, long style, wxWindowID id, const wxString &name) override
Create the status line (like a wxStatusBar).
void OnExit(wxCommandEvent &event)
void OpenJobsFile(const wxFileName &aFileName, bool aCreate=false, bool aResaveProjectPreferences=true)
bool canCloseWindow(wxCloseEvent &aCloseEvent) override
void OnSize(wxSizeEvent &event) override
virtual void setupUIConditions() override
Setup the UI conditions for the various actions and their controls in this frame.
PROJECT_TREE_PANE * m_projectTreePane
void OnImportGedaFiles(wxCommandEvent &event)
Open dialog to import gEDA/gaf schematic and PCB files.
void RestoreCommitFromHistory(const wxString &aHash)
void DoWithAcceptedFiles() override
Execute action on accepted dropped file.
void OnOpenFileInTextEditor(wxCommandEvent &event)
APP_SETTINGS_BASE * config() const override
Return the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
void OnImportOrcadFiles(wxCommandEvent &event)
Open dialog to import an OrCAD schematic file.
const wxString PcbLegacyFileName()
void OnEditAdvancedCfg(wxCommandEvent &event)
void OnImportPcadProjectFiles(wxCommandEvent &event)
KICAD_MANAGER_FRAME(wxWindow *parent, const wxString &title, const wxPoint &pos, const wxSize &size)
bool CloseProject(bool aSave)
Closes the project, and saves it if aSave is true;.
wxString GetCurrentFileName() const override
Get the full filename + path of the currently opened file in the frame.
void onNotebookPageCloseRequest(wxAuiNotebookEvent &evt)
void CommonSettingsChanged(int aFlags) override
Notification event that some of the common (suite-wide) settings have changed.
void LoadSettings(APP_SETTINGS_BASE *aCfg) override
Load common frame parameters from a configuration file.
void OnIdle(wxIdleEvent &event)
void PrintPrjInfo()
Prints the current working directory name and the project name on the text panel.
void OnImportDipTraceFiles(wxCommandEvent &event)
Open dialog to import DipTrace schematic and board files.
void SaveOpenJobSetsToLocalSettings(bool aIsExplicitUserSave=false)
bool LoadProject(const wxFileName &aProjectFileName)
Loads a new project.
bool m_restoredFromHistory
Set after restore to mark editors dirty.
void onNotebookPageCountChanged(wxAuiNotebookEvent &evt)
LOCAL_HISTORY_PANE * m_historyPane
void OnImportCadstarArchiveFiles(wxCommandEvent &event)
Open dialog to import CADSTAR Schematic and PCB Archive files.
void OnCompareProjectBranches(wxCommandEvent &event)
Open DIALOG_GIT_MR_REVIEW so the user can pick two refs and review the changed-file list between them...
void SaveSettings(APP_SETTINGS_BASE *aCfg) override
Save common frame parameters to a configuration data file.
BITMAP_BUTTON * m_pcmButton
PANEL_KICAD_LAUNCHER * m_launcher
void HideTabsIfNeeded()
Hides the tabs for Editor notebook if there is only 1 page.
wxString help_name() override
std::unique_ptr< UPDATE_MANAGER > m_updateManager
std::vector< wxString > m_OpenProjects
Definition kiid.h:46
void SetEllipsedTextField(const wxString &aText, int aFieldId)
Set the text in a field using wxELLIPSIZE_MIDDLE option to adjust the text size to the field size.
virtual void SetStatusWidths(int aSize, const int *aWidths) override
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
virtual KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Return the KIFACE* given a FACE_T.
Definition kiway.cpp:207
@ FACE_SCH
eeschema DSO
Definition kiway.h:318
@ FACE_PCB
pcbnew DSO
Definition kiway.h:319
LOCAL_HISTORY & LocalHistory()
Return the LOCAL_HISTORY associated with this KIWAY.
Definition kiway.h:422
bool EnforceSizeLimit(const wxString &aProjectPath, size_t aMaxBytes, PROGRESS_REPORTER *aReporter=nullptr)
Enforce total size limit by rebuilding trimmed history keeping newest commits whose cumulative unique...
bool TagSave(const wxString &aProjectPath, const wxString &aFileType)
Tag a manual save in the local history repository.
wxString GetHeadHash(const wxString &aProjectPath)
Return the current head commit hash.
void WaitForPendingSave()
Block until any pending background save completes.
bool RestoreCommit(const wxString &aProjectPath, const wxString &aHash, wxWindow *aParent=nullptr, bool aConfirm=true)
Restore the project files to the state recorded by the given commit hash.
bool CommitFullProjectSnapshot(const wxString &aProjectPath, const wxString &aTitle)
Commit a snapshot of the entire project directory (excluding the .history directory and ignored trans...
void RegisterSaver(const void *aSaverObject, const std::function< void(const wxString &, std::vector< HISTORY_FILE_DATA > &)> &aSaver, const std::weak_ptr< void > &aLifetime={})
Register a saver callback invoked during autosave history commits.
void UnregisterSaver(const void *aSaverObject)
Unregister a previously registered saver callback.
bool OverrideLock(bool aRemoveOnRelease=true)
Force the lock, overwriting the data that existed already.
Definition lockfile.h:179
bool Valid() const
Definition lockfile.h:264
wxString GetUsername()
Definition lockfile.h:246
wxString GetHostname()
Definition lockfile.h:252
bool IsLockedByMe()
Definition lockfile.h:232
void RegisterStatusBar(KISTATUSBAR *aStatusBar)
Add a status bar for handling.
void CreateOrUpdate(const wxString &aKey, const wxString &aTitle, const wxString &aDescription, const wxString &aHref=wxEmptyString)
Create a notification with the given parameters or updates an existing one with the same key.
void Remove(const wxString &aKey)
Remove a notification by key.
void UnregisterStatusBar(KISTATUSBAR *aStatusBar)
Remove status bar from handling.
static REPORTER & GetInstance()
Definition reporter.cpp:179
void SetClosable(bool aYes)
void SetProjectTied(bool aYes)
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:562
void PreloadDesignBlockLibraries(KIWAY *aKiway)
Starts a background job to preload the global and project design block libraries.
Definition pgm_base.cpp:908
virtual BACKGROUND_JOBS_MONITOR & GetBackgroundJobMonitor() const
Definition pgm_base.h:130
void UnregisterLibraryLoadStatusBar(KISTATUSBAR *aStatusBar)
Unregister a status bar from receiving library load warning messages.
bool m_Quitting
Definition pgm_base.h:397
void RegisterLibraryLoadStatusBar(KISTATUSBAR *aStatusBar)
Register a status bar to receive library load warning messages.
Definition pgm_base.cpp:989
virtual NOTIFICATIONS_MANAGER & GetNotificationsManager() const
Definition pgm_base.h:135
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:124
wxString GetHelpFileName()
Definition pgm_kicad.h:56
SEARCH_STACK & SysSearch()
Definition pgm_kicad.h:54
APP_SETTINGS_BASE * PgmSettings()
Definition pgm_kicad.h:52
The project local settings are things that are attached to a particular project, but also might be pa...
bool SaveToFile(const wxString &aDirectory="", bool aForce=false) override
Calls Store() and then writes the contents of the JSON document to a file.
std::vector< wxString > m_OpenJobSets
PROJECT_TREE_PANE Window to display the tree files.
void SetProjectLock(LOCKFILE *aLockFile)
Definition project.cpp:461
virtual void SetReadOnly(bool aReadOnly=true)
Definition project.h:161
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:177
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:183
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:207
std::weak_ptr< void > GetHistoryLifetimeToken() const
Liveness token handed to LOCAL_HISTORY::RegisterSaver so a shared autosave timer skips this project's...
Definition project.h:315
virtual void SetLockOverrideGranted(bool aGranted=true)
Definition project.h:165
void SaveToHistory(const wxString &aProjectPath, std::vector< HISTORY_FILE_DATA > &aFileData)
Produce HISTORY_FILE_DATA entries for project files (.kicad_pro and .kicad_prl).
Definition project.cpp:467
Look for files in a number of paths.
static bool ShowNever(const SELECTION &aSelection)
Always returns false.
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Save a loaded project.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool IsProjectOpen() const
Helper for checking if we have a project open.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
std::vector< wxString > GetOpenProjects() const
bool TriggerBackupIfNeeded(REPORTER &aReporter) const
Call BackupProject() if a new backup is needed according to the current backup policy.
TOOL_MANAGER * m_toolManager
TOOL_DISPATCHER * m_toolDispatcher
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
ACTIONS * m_actions
virtual void DispatchWxEvent(wxEvent &aEvent)
Process wxEvents (mostly UI events), translate them to TOOL_EVENTs, and make tools handle those.
Master controller class:
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Multi-thread safe progress reporter dialog, intended for use of tasks that parallel reporting back of...
bool AskOverrideLock(wxWindow *aParent, const wxString &aMessage)
Display a dialog indicating the file is already open, with an option to reset the lock.
Definition confirm.cpp:38
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
#define ENABLE(x)
#define _(s)
Base window classes and related definitions.
#define KICAD_DEFAULT_DRAWFRAME_STYLE
#define KICAD_MANAGER_FRAME_NAME
EVT_MENU_RANGE(ID_GERBVIEW_DRILL_FILE1, ID_GERBVIEW_DRILL_FILEMAX, GERBVIEW_FRAME::OnDrlFileHistory) EVT_MENU_RANGE(ID_GERBVIEW_ZIP_FILE1
KiCad executable names.
const wxString GERBVIEW_EXE
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_SCH
Definition frame_type.h:30
@ KICAD_MAIN_FRAME_T
Definition frame_type.h:69
wxString FindKicadFile(const wxString &shortname)
Search the executable file shortname in KiCad binary path and return full file name if found or short...
Definition gestfich.cpp:62
static const std::string LegacySchematicFileExtension
static const std::string GerberJobFileExtension
static const std::string GerberFileExtension
static const std::string LockFileExtension
static const std::string ProjectFileExtension
static const std::string LegacyPcbFileExtension
static const std::string LegacyProjectFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string LockFilePrefix
static const std::string DrillFileExtension
static const std::string KiCadPcbFileExtension
static wxString AllFilesWildcard()
static bool IsGerberFileExtension(const wxString &ext)
@ ID_FILE_LIST_CLEAR
Definition id.h:58
@ ID_FILEMAX
Definition id.h:56
@ ID_FILE1
Definition id.h:55
PGM_KICAD & PgmTop()
Definition kicad.cpp:93
@ ID_EDIT_ADVANCED_CFG
Definition kicad_id.h:34
@ ID_IMPORT_DIPTRACE_PROJECT
Definition kicad_id.h:43
@ ID_IMPORT_EAGLE_PROJECT
Definition kicad_id.h:36
@ ID_IMPORT_EASYEDAPRO_PROJECT
Definition kicad_id.h:38
@ ID_IMPORT_PADS_PROJECT
Definition kicad_id.h:40
@ ID_IMPORT_GEDA_PROJECT
Definition kicad_id.h:42
@ ID_IMPORT_PCAD_PROJECT
Definition kicad_id.h:41
@ ID_IMPORT_CADSTAR_ARCHIVE_PROJECT
Definition kicad_id.h:35
@ ID_EDIT_LOCAL_FILE_IN_TEXT_EDITOR
Definition kicad_id.h:33
@ ID_IMPORT_ORCAD_PROJECT
Definition kicad_id.h:44
@ ID_IMPORT_EASYEDA_PROJECT
Definition kicad_id.h:37
@ ID_IMPORT_ALTIUM_PROJECT
Definition kicad_id.h:39
@ ID_COMPARE_PROJECT_BRANCHES
Definition kicad_id.h:45
EVT_MENU(ID_COMPARE_PROJECT_BRANCHES, KICAD_MANAGER_FRAME::OnCompareProjectBranches) KICAD_MANAGER_FRAME
#define EDITORS_CAPTION
#define ID_INIT_WATCHED_PATHS
#define PROJECT_FILES_CAPTION
File locking utilities.
bool RegisterApplicationRestart(const wxString &aCommandLine)
Registers the application for restart with the OS with the given command line string to pass as args.
Definition unix/app.cpp:77
void AddToRecentDocs(const wxString &aPath)
PBOOL GetPolicyBool(const wxString &aKey)
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:521
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition ui_common.cpp:78
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
PGM_BASE & Pgm()
The global program "get" accessor.
#define POLICY_KEY_PCM
Definition policy_keys.h:27
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
T * GetToolbarSettings(const wxString &aFilename)
KIWAY Kiway(KFCTL_STANDALONE)
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 ...
ACTION_CONDITIONS & Enable(const SELECTION_CONDITION &aCondition)
unsigned long long limit_total_size
Maximum total size of backups (bytes), 0 for unlimited.
std::shared_ptr< JOBS_OUTPUT_HANDLER > m_outputHandler
Definition jobset.h:136
Implement a participant in the KIWAY alchemy.
Definition kiway.h:152
virtual void PreloadLibraries(KIWAY *aKiway)
Definition kiway.h:275
virtual void CancelPreload(bool aBlock=true)
Definition kiway.h:277
IbisParser parser & reporter
#define ENVVARS_CHANGED
Functions to provide common constants and other functions to assist in making a consistent UI.
Definition of file extensions used in Kicad.
#define PR_NO_ABORT