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
127 KICAD_MANAGER_FRAME::OnCompareProjectBranches )
128
129 // Range menu events
131 EVT_MENU( ID_FILE_LIST_CLEAR, KICAD_MANAGER_FRAME::OnClearFileHistory )
132
133 // Special functions
134 EVT_MENU( ID_INIT_WATCHED_PATHS, KICAD_MANAGER_FRAME::OnChangeWatchedPaths )
135
136 // Drop files event
137 EVT_DROP_FILES( KICAD_MANAGER_FRAME::OnDropFiles )
138
139END_EVENT_TABLE()
140
141// See below the purpose of this include
142#include <wx/xml/xml.h>
143
144KICAD_MANAGER_FRAME::KICAD_MANAGER_FRAME( wxWindow* parent, const wxString& title,
145 const wxPoint& pos, const wxSize& size ) :
148 m_openSavedWindows( false ),
149 m_restoredFromHistory( false ),
150 m_active_project( false ),
151 m_showHistoryPanel( false ),
152 m_projectTreePane( nullptr ),
153 m_historyPane( nullptr ),
154 m_launcher( nullptr ),
155 m_lastToolbarIconSize( 0 ),
156 m_pcmButton( nullptr ),
157 m_pcmUpdateCount( 0 )
158{
159 const int defaultLeftWinWidth = FromDIP( 250 );
160
161 m_leftWinWidth = defaultLeftWinWidth; // Default value
162 m_aboutTitle = "KiCad";
163
164 // JPC: A very ugly hack to fix an issue on Linux: if the wxbase315u_xml_gcc_custom.so is
165 // used **only** in PCM, it is not found in some cases at run time.
166 // So just use it in the main module to avoid a not found issue
167 // wxbase315u_xml_gcc_custom shared object when launching Kicad
168 wxXmlDocument dummy;
169
170 // Create the status line (bottom of the frame). Left half is for project name; right half
171 // is for Reporter (currently used by archiver/unarchiver and PCM).
172 // Note: this is a KISTATUSBAR status bar. Therefore the specified number of fields
173 // is the extra number of fields, not the full field count.
174 // We need here 2 fields: the extra fiels to display the project name, and another field
175 // to display a info (specific to Windows) using the FIELD_OFFSET_BGJOB_TEXT id offset (=1)
176 // So the extra field count is 1
177 CreateStatusBar( 2 );
180 Pgm().RegisterLibraryLoadStatusBar( (KISTATUSBAR*) GetStatusBar() );
181 GetStatusBar()->SetFont( KIUI::GetStatusFont( this ) );
182
183 // Give an icon
184 wxIcon icon;
185 wxIconBundle icon_bundle;
186
187 if( IsNightlyVersion())
188 {
189 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly, 48 ) );
190 icon_bundle.AddIcon( icon );
191 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly, 128 ) );
192 icon_bundle.AddIcon( icon );
193 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly, 256 ) );
194 icon_bundle.AddIcon( icon );
195 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly_32 ) );
196 icon_bundle.AddIcon( icon );
197 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_nightly_16 ) );
198 icon_bundle.AddIcon( icon );
199 }
200 else
201 {
202 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad, 48 ) );
203 icon_bundle.AddIcon( icon );
204 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad, 128 ) );
205 icon_bundle.AddIcon( icon );
206 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad, 256 ) );
207 icon_bundle.AddIcon( icon );
208 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_32 ) );
209 icon_bundle.AddIcon( icon );
210 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_kicad_16 ) );
211 icon_bundle.AddIcon( icon );
212 }
213
214 SetIcons( icon_bundle );
215
216 // Load the settings
217 LoadSettings( config() );
218
219 // Left window: is the box which display tree project
220 m_projectTreePane = new PROJECT_TREE_PANE( this );
221
222 setupTools();
223 setupUIConditions();
224
225 m_toolbarSettings = GetToolbarSettings<KICAD_MANAGER_TOOLBAR_SETTINGS>( "kicad-toolbars" );
226 configureToolbars();
227 RecreateToolbars();
228 ReCreateMenuBar();
229
230 m_auimgr.SetManagedWindow( this );
231 m_auimgr.SetFlags( wxAUI_MGR_LIVE_RESIZE );
232
233 m_auimgr.AddPane( m_tbLeft, EDA_PANE().VToolbar().Name( "TopMainToolbar" ).Left().Layer( 2 ) );
234
235 // There is no wxAUIPaneInfo::SetSize(), but a trick is to use MinSize() to set the required pane width,
236 // and after give a reasonable MinSize value.
237 m_auimgr.AddPane( m_projectTreePane,
238 EDA_PANE().Palette().Name( "ProjectTree" ).Left().Layer( 1 )
239 .Caption( PROJECT_FILES_CAPTION ).PaneBorder( false )
240 .MinSize( m_leftWinWidth, -1 ).Floatable( false ).Movable( false ) );
241
242 m_historyPane = new LOCAL_HISTORY_PANE( this );
243 m_auimgr.AddPane( m_historyPane,
244 EDA_PANE().Palette().Name( "LocalHistory" ).Left().Layer( 1 ).Position( 1 )
245 .Caption( _( "Local History" ) ).PaneBorder( false )
246 .Floatable( false ).Movable( false ).CloseButton( true ).Hide() );
247
248 if( m_showHistoryPanel )
249 m_auimgr.GetPane( m_historyPane ).Show();
250
251 wxSize client_size = GetClientSize();
252 m_notebook = new wxAuiNotebook( this, wxID_ANY, wxPoint( client_size.x, client_size.y ),
253 FromDIP( wxSize( 700, 590 ) ),
254 wxAUI_NB_TOP | wxAUI_NB_CLOSE_ON_ALL_TABS | wxAUI_NB_TAB_MOVE
255 | wxAUI_NB_SCROLL_BUTTONS | wxNO_BORDER );
256
257 m_notebook->SetArtProvider( new WX_AUI_TAB_ART() );
258
259 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &KICAD_MANAGER_FRAME::onNotebookPageCloseRequest, this );
260 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSED, &KICAD_MANAGER_FRAME::onNotebookPageCountChanged, this );
261 m_launcher = new PANEL_KICAD_LAUNCHER( m_notebook );
262
263 m_notebook->Freeze();
264 m_launcher->SetClosable( false );
265 m_notebook->AddPage( m_launcher, EDITORS_CAPTION, false );
266 m_notebook->SetTabCtrlHeight( 0 );
267 m_notebook->Thaw();
268
269 m_auimgr.AddPane( m_notebook, EDA_PANE().Canvas().Name( "Editors" ).Center().Caption( EDITORS_CAPTION )
270 .PaneBorder( false ).MinSize( m_notebook->GetBestSize() ) );
271
272 m_auimgr.Update();
273
274 // Now the actual m_projectTreePane size is set, give it a reasonable min width
275 m_auimgr.GetPane( m_projectTreePane ).MinSize( defaultLeftWinWidth, FromDIP( 80 ) );
276
277
278 wxSizer* mainSizer = GetSizer();
279
280 // Only fit the initial window size the first time KiCad is run.
281 if( mainSizer && config()->m_Window.state.size_x == 0 && config()->m_Window.state.size_y == 0 )
282 {
283 Layout();
284 mainSizer->Fit( this );
285 Center();
286 }
287
288 if( ADVANCED_CFG::GetCfg().m_HideVersionFromTitle )
289 SetTitle( wxT( "KiCad" ) );
290 else
291 SetTitle( wxString( "KiCad " ) + GetMajorMinorVersion() );
292
293 // Do not let the messages window have initial focus
294 m_projectTreePane->SetFocus();
295
296 // Init for dropping files
299
300 // Gerber files
301 // Note that all gerber files are aliased as GerberFileExtension
305
306 DragAcceptFiles( true );
307}
308
309
311{
313 Unbind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
314
315 m_notebook->Unbind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &KICAD_MANAGER_FRAME::onNotebookPageCloseRequest, this );
316 m_notebook->Unbind( wxEVT_AUINOTEBOOK_PAGE_CLOSED, &KICAD_MANAGER_FRAME::onNotebookPageCountChanged, this );
317
320 Pgm().UnregisterLibraryLoadStatusBar( (KISTATUSBAR*) GetStatusBar() );
321
322 // Shutdown all running tools
323 if( m_toolManager )
324 m_toolManager->ShutdownAllTools();
325
326 if( m_pcm )
327 m_pcm->StopBackgroundUpdate();
328
329 // Stop update manager before tearing down the AUI framework. The update
330 // task runs on the thread pool and may call CallAfter on this frame, so it
331 // must complete before we uninitialize AUI or destroy child windows.
332 m_updateManager.reset();
333
334 delete m_actions;
335 delete m_toolManager;
336 delete m_toolDispatcher;
337
338 m_auimgr.UnInit();
339}
340
342{
343 if( m_notebook->GetPageCount() == 1 )
344 m_notebook->SetTabCtrlHeight( 0 );
345 else
346 m_notebook->SetTabCtrlHeight( -1 );
347}
348
349
351{
353}
354
355
357{
358 wxAuiNotebook* notebook = (wxAuiNotebook*) evt.GetEventObject();
359 wxWindow* page = notebook->GetPage( evt.GetSelection() );
360
361 if( PANEL_NOTEBOOK_BASE* panel = dynamic_cast<PANEL_NOTEBOOK_BASE*>( page ) )
362 {
363 if( panel->GetClosable() )
364 {
365 if( !panel->GetCanClose() )
366 evt.Veto();
367
368 CallAfter(
369 [this]()
370 {
372 } );
373 }
374 else
375 {
376 evt.Veto();
377 }
378 }
379}
380
381
382wxStatusBar* KICAD_MANAGER_FRAME::OnCreateStatusBar( int number, long style, wxWindowID id,
383 const wxString& name )
384{
385 KISTATUSBAR* sb = new KISTATUSBAR( number, this, id,
389
390 size_t sbFieldCnt = static_cast<size_t>( sb->GetFieldsCount() );
391 std::vector<int> sbFieldSizes( sbFieldCnt );
392
393 for( size_t i = 0; i < sbFieldCnt; i++ )
394 sbFieldSizes[i] = sb->GetStatusWidth( static_cast<int>( i ) );
395
396 // Field 0 (the project path) is the only stretchable field so it uses all the leftover width
397 // before ellipsizing. Field 1 gets a fixed width sized to its watcher text; longer content
398 // (archive progress) is ellipsized rather than allowed to steal the path's width.
399 if( sbFieldCnt > 0 )
400 sbFieldSizes[0] = -1;
401
402 if( sbFieldCnt > 1 )
403 {
404 int margin = KIUI::GetTextSize( wxT( "XX" ), sb ).x;
405 int watcherWidth = std::max( KIUI::GetTextSize( _( "Local path: monitoring folder changes" ), sb ).x,
406 KIUI::GetTextSize( _( "Network path: not monitoring folder changes" ), sb ).x );
407
408 sbFieldSizes[1] = watcherWidth + margin;
409 }
410
411 sb->SetStatusWidths( sbFieldCnt, sbFieldSizes.data() );
412
413 return sb;
414}
415
416
418{
419 // creates the PLUGIN_CONTENT_MANAGER, if not exists
420 if( m_pcm )
421 return;
422
423 m_pcm = std::make_shared<PLUGIN_CONTENT_MANAGER>(
424 [this]( int aUpdateCount )
425 {
426 if( Pgm().m_Quitting )
427 return;
428
429 m_pcmUpdateCount = aUpdateCount;
430
431 if( aUpdateCount > 0 )
432 {
434 wxS( "pcm" ),
435 _( "PCM Updates Available" ),
436 wxString::Format( _( "%d package update(s) available" ), aUpdateCount ),
437 wxT( "" ) );
438 }
439 else
440 {
441 Pgm().GetNotificationsManager().Remove( wxS( "pcm" ) );
442 }
443
444 CallAfter(
445 [this]()
446 {
448 } );
449 });
450
451 m_pcm->SetRepositoryList( kicadSettings()->m_PcmRepositories );
452}
453
454
456{
457 // Create the manager
459 m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
461
463
464 // Attach the events to the tool dispatcher
466 Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
467
468 // Register tools
469 m_toolManager->RegisterTool( new COMMON_CONTROL );
470 m_toolManager->RegisterTool( new KICAD_MANAGER_CONTROL );
471 m_toolManager->InitTools();
472}
473
474
476{
478
479 ACTION_MANAGER* manager = m_toolManager->GetActionManager();
480
481 wxASSERT( manager );
482
483 auto activeProject =
484 [this] ( const SELECTION& )
485 {
486 return m_active_project;
487 };
488
489#define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
490
491 ACTION_CONDITIONS activeProjectCond;
492 activeProjectCond.Enable( activeProject );
493
494 manager->SetConditions( ACTIONS::saveAs, activeProjectCond );
495 manager->SetConditions( KICAD_MANAGER_ACTIONS::closeProject, activeProjectCond );
496 manager->SetConditions( KICAD_MANAGER_ACTIONS::archiveProject, activeProjectCond );
497 manager->SetConditions( KICAD_MANAGER_ACTIONS::newJobsetFile, activeProjectCond );
498 manager->SetConditions( KICAD_MANAGER_ACTIONS::openJobsetFile, activeProjectCond );
499
500 auto historyCond =
501 [this]( const SELECTION& )
502 {
503 return HistoryPanelShown();
504 };
505
507
508 // These are just here for text boxes, search boxes, etc. in places such as the standard
509 // file dialogs.
513
514#undef ENABLE
515}
516
517
519{
520 return m_projectTreePane;
521}
522
523
525{
527 wxASSERT( ret );
528 return ret;
529}
530
531
533{
534 KICAD_SETTINGS* ret = dynamic_cast<KICAD_SETTINGS*>( config() );
535 wxASSERT( ret );
536 return ret;
537}
538
539
541{
542 CallAfter(
543 [&]()
544 {
545 KIFACE *schface = Kiway().KiFACE( KIWAY::FACE_SCH );
546 schface->PreloadLibraries( &Kiway() );
547
548 KIFACE *pcbface = Kiway().KiFACE( KIWAY::FACE_PCB );
549 pcbface->PreloadLibraries( &Kiway() );
550
552 } );
553}
554
555
557{
558 return GetProjectFileName();
559}
560
561
563{
565 : wxString( wxEmptyString );
566}
567
568
570{
571 wxFileName fn( GetProjectFileName() );
572
574 return fn.GetFullPath();
575}
576
577
579{
580 wxFileName fn( GetProjectFileName() );
581
583 return fn.GetFullPath();
584}
585
586
588{
589 wxFileName fn( GetProjectFileName() );
590
591 fn.SetExt( FILEEXT::PcbFileExtension );
592 return fn.GetFullPath();
593}
594
595
597{
598 wxFileName fn( GetProjectFileName() );
599
601 return fn.GetFullPath();
602}
603
604
606{
607 m_projectTreePane->ReCreateTreePrj();
608}
609
610
615
616
618{
619 return PgmTop().GetHelpFileName();
620}
621
622
623void KICAD_MANAGER_FRAME::OnSize( wxSizeEvent& event )
624{
625 if( m_auimgr.GetManagedWindow() )
626 m_auimgr.Update();
627
628 PrintPrjInfo();
629
630#if defined( _WIN32 )
631 KISTATUSBAR* statusBar = static_cast<KISTATUSBAR*>( GetStatusBar() );
633#endif
634
635 event.Skip();
636}
637
638
640{
641 // All fileNames are now in m_AcceptedFiles vector.
642 // Check if contains a project file name and load project.
643 // If not, open files in dedicated app.
644 for( const wxFileName& fileName : m_AcceptedFiles )
645 {
646 wxString ext = fileName.GetExt();
647
649 {
650 wxString fn = fileName.GetFullPath();
651 m_toolManager->RunAction<wxString*>( *m_acceptedExts.at( fileName.GetExt() ), &fn );
652
653 return;
654 }
655 }
656
657 // Then stock gerber files in gerberFiles and run action for other files.
658 wxString gerberFiles;
659
660 // Gerbview editor should be able to open Gerber and drill files
661 for( const wxFileName& fileName : m_AcceptedFiles )
662 {
663 wxString ext = fileName.GetExt();
664
668 {
669 gerberFiles += wxT( '\"' );
670 gerberFiles += fileName.GetFullPath() + wxT( '\"' );
671 gerberFiles = gerberFiles.Pad( 1 );
672 }
673 else
674 {
675 wxString fn = fileName.GetFullPath();
676 m_toolManager->RunAction<wxString*>( *m_acceptedExts.at( fileName.GetExt() ), &fn );
677 }
678 }
679
680 // Execute Gerbviewer
681 if( !gerberFiles.IsEmpty() )
682 {
683 wxString fullEditorName = FindKicadFile( GERBVIEW_EXE );
684
685 if( wxFileExists( fullEditorName ) )
686 {
687 wxString command = fullEditorName + " " + gerberFiles;
688 m_toolManager->RunAction<wxString*>( *m_acceptedExts.at( FILEEXT::GerberFileExtension ), &command );
689 }
690 }
691}
692
693
694bool KICAD_MANAGER_FRAME::canCloseWindow( wxCloseEvent& aEvent )
695{
696 KICAD_SETTINGS* settings = kicadSettings();
698
699 for( size_t i = 0; i < m_notebook->GetPageCount(); i++ )
700 {
701 wxWindow* page = m_notebook->GetPage( i );
702
703 if( PANEL_NOTEBOOK_BASE* panel = dynamic_cast<PANEL_NOTEBOOK_BASE*>( page ) )
704 {
705 if( !panel->GetCanClose() )
706 return false;
707 }
708 }
709
710 // CloseProject will recursively ask all the open editors if they need to save changes.
711 // If any of them cancel then we need to cancel closing the KICAD_MANAGER_FRAME.
712 if( CloseProject( true ) )
713 {
714 // Don't propagate event to frames which have already been closed
715 aEvent.StopPropagation();
716
717 return true;
718 }
719 else
720 {
721 if( aEvent.CanVeto() )
722 aEvent.Veto();
723
724 return false;
725 }
726}
727
728
730{
731#ifdef _WINDOWS_
732 // For some obscure reason, on Windows, when killing Kicad from the Windows task manager
733 // if a editor frame (schematic, library, board editor or fp editor) is open and has
734 // some edition to save, OnCloseWindow is run twice *at the same time*, creating race
735 // conditions between OnCloseWindow() code.
736 // Therefore I added (JPC) a ugly hack to discard the second call (unwanted) during
737 // execution of the first call (only one call is right).
738 // Note also if there is no change made in editors, this behavior does not happen.
739 static std::atomic<unsigned int> lock_close_event( 0 );
740
741 if( ++lock_close_event > 1 ) // Skip extra calls
742 {
743 return;
744 }
745#endif
746
747 m_projectTreePane->Show( false );
748 Pgm().m_Quitting = true;
749
750 Destroy();
751
752#ifdef _WINDOWS_
753 lock_close_event = 0; // Reenable event management
754#endif
755}
756
757
759{
761
762 if( !aIsExplicitUserSave && !cfg.ShouldAutoSave() )
763 return;
764
765 cfg.m_OpenJobSets.clear();
766
767 for( size_t i = 0; i < m_notebook->GetPageCount(); i++ )
768 {
769 if( PANEL_JOBSET* jobset = dynamic_cast<PANEL_JOBSET*>( m_notebook->GetPage( i ) ) )
770 {
771 wxFileName jobsetFn( jobset->GetFilePath() );
772 jobsetFn.MakeRelativeTo( Prj().GetProjectPath() );
773 cfg.m_OpenJobSets.emplace_back( jobsetFn.GetFullPath() );
774 }
775 }
776
777 cfg.SaveToFile( Prj().GetProjectPath() );
778}
779
780
781void KICAD_MANAGER_FRAME::OnExit( wxCommandEvent& event )
782{
783 Close( true );
784}
785
786
788{
789 if( !Kiway().PlayersClose( false ) )
790 return false;
791
792 // Abort any in-progress background load, since the threads depend on the project not changing
793 KIFACE *schface = Kiway().KiFACE( KIWAY::FACE_SCH );
794 schface->CancelPreload();
795
796 KIFACE *pcbface = Kiway().KiFACE( KIWAY::FACE_PCB );
797 pcbface->CancelPreload();
798
799 // Save the project file for the currently loaded project.
800 if( m_active_project )
801 {
803
804 if( Prj().GetLocalSettings().ShouldAutoSave() && Prj().GetProjectFile().ShouldAutoSave() )
805 {
807
808 if( aSave )
809 mgr.SaveProject();
810 }
811
812 // Ensure the Last_Save tag is at HEAD before closing. This handles the case where
813 // autosave commits were made after the last explicit save - without this, the next
814 // project load would offer to restore the autosave state, which is incorrect after
815 // a clean close.
816 wxString projPath = Prj().GetProjectPath();
817
818 // Wait for any in-flight autosave so the HEAD check below isn't racing it.
820
821 if( !projPath.IsEmpty() && Kiway().LocalHistory().HistoryExists( projPath ) )
822 {
823 if( Kiway().LocalHistory().HeadNewerThanLastSave( projPath ) )
824 {
825 // Tag unconditionally: even on no-op snapshots Last_Save must anchor at HEAD.
826 Kiway().LocalHistory().CommitFullProjectSnapshot( projPath, wxS( "Close" ) );
827 Kiway().LocalHistory().TagSave( projPath, wxS( "project" ) );
828 }
829 }
830
831 // The editors clean up autosaves for sheets actually dirtied in their session.
832 // Anything still on disk here was deferred by the user in the recovery dialog
833 // and must survive so the dialog can offer it again on the next open.
834
835 m_active_project = false;
836 // Enforce local history size limit (if enabled) once all pending saves/backups are done.
837 if( Pgm().GetCommonSettings() && Pgm().GetCommonSettings()->m_Backup.enabled )
838 {
839 unsigned long long int limit = Pgm().GetCommonSettings()->m_Backup.limit_total_size;
840
841 if( limit > 0 )
842 {
843 WX_PROGRESS_REPORTER reporter( this, _( "Local History" ), 3, PR_NO_ABORT );
844 Kiway().LocalHistory().EnforceSizeLimit( Prj().GetProjectPath(), (size_t) limit, &reporter );
845 }
846 }
847
848 // Unregister the project saver before unloading the project to prevent
849 // dangling references
851
852 mgr.UnloadProject( &Prj() );
853 }
854
855 SetStatusText( "" );
856
857 // Traverse pages in reverse order so deleting them doesn't mess up our iterator.
858 for( int i = (int) m_notebook->GetPageCount() - 1; i >= 0; i-- )
859 {
860 wxWindow* page = m_notebook->GetPage( i );
861
862 if( PANEL_NOTEBOOK_BASE* panel = dynamic_cast<PANEL_NOTEBOOK_BASE*>( page ) )
863 {
864 if( panel->GetProjectTied() )
865 m_notebook->DeletePage( i );
866 }
867 }
868
869 m_projectTreePane->EmptyTreePrj();
871
872 return true;
873}
874
875
876void KICAD_MANAGER_FRAME::OpenJobsFile( const wxFileName& aFileName, bool aCreate, bool aResaveProjectPreferences )
877{
878 for( size_t i = 0; i < m_notebook->GetPageCount(); i++ )
879 {
880 if( PANEL_JOBSET* panel = dynamic_cast<PANEL_JOBSET*>( m_notebook->GetPage( i ) ) )
881 {
882 if( aFileName.GetFullPath() == panel->GetFilePath() )
883 {
884 m_notebook->SetSelection( i );
885 return;
886 }
887 }
888 }
889
890 try
891 {
892 std::unique_ptr<JOBSET> jobsFile = std::make_unique<JOBSET>( aFileName.GetFullPath().ToStdString() );
893
894 jobsFile->LoadFromFile();
895
896 if( aCreate && !aFileName.FileExists() )
897 {
898 JOBSET_DESTINATION* dest = jobsFile->AddNewDestination( JOBSET_DESTINATION_T::FOLDER );
899 dest->m_outputHandler->SetOutputPath( aFileName.GetName() );
900 jobsFile->SaveToFile( wxEmptyString, true );
901 }
902
903 PANEL_JOBSET* jobPanel = new PANEL_JOBSET( m_notebook, this, std::move( jobsFile ) );
904 jobPanel->SetProjectTied( true );
905 jobPanel->SetClosable( true );
906 m_notebook->AddPage( jobPanel, aFileName.GetFullName(), true );
908
909 if( aResaveProjectPreferences )
911 }
912 catch( ... )
913 {
914 DisplayErrorMessage( this, _( "Error opening jobs file" ) );
915 }
916}
917
918
919bool KICAD_MANAGER_FRAME::LoadProject( const wxFileName& aProjectFileName )
920{
921 // The project file should be valid by the time we get here or something has gone wrong.
922 if( !aProjectFileName.Exists() )
923 return false;
924
925 wxString fullPath = aProjectFileName.GetFullPath();
926
927 // Check if a lock file already exists BEFORE we try to acquire it. We only want to warn
928 // the user if the lock file pre-existed, not if we're about to create it ourselves.
929 // The actual lock acquisition happens in SETTINGS_MANAGER::LoadProject().
930 wxFileName lockFn( fullPath );
931 lockFn.SetName( FILEEXT::LockFilePrefix + lockFn.GetName() );
932 lockFn.SetExt( lockFn.GetExt() + wxS( "." ) + FILEEXT::LockFileExtension );
933 bool lockFilePreExisted = lockFn.FileExists();
934
935 bool lockOverrideGranted = false;
936
937 if( lockFilePreExisted )
938 {
939 // A lock file exists. Create a LOCKFILE to read who owns it and decide what to do.
940 LOCKFILE lockFile( fullPath );
941
942 if( !lockFile.Valid() && lockFile.IsLockedByMe() )
943 {
944 // If we cannot acquire the lock but we appear to be the one who locked it, check to
945 // see if there is another KiCad instance running. If not, then we can override the
946 // lock. This could happen if KiCad crashed or was interrupted.
947 if( !Pgm().SingleInstance()->IsAnotherRunning() )
948 lockFile.OverrideLock();
949 }
950
951 if( !lockFile.Valid() )
952 {
953 wxString msg;
954 msg.Printf( _( "Project '%s' is already open by '%s' at '%s'." ),
955 fullPath,
956 lockFile.GetUsername(),
957 lockFile.GetHostname() );
958
959 if( !AskOverrideLock( this, msg ) )
960 return false; // User clicked Cancel - abort project loading entirely
961
962 lockFile.OverrideLock();
963 lockOverrideGranted = true;
964 }
965
966 // The LOCKFILE goes out of scope here and releases/removes the lock file.
967 // SETTINGS_MANAGER::LoadProject() will create the actual persistent lock.
968 }
969
970 // Any open KIFACE's must be closed if they are not part of the new project.
971 // (We never want a KIWAY_PLAYER open on a KIWAY that isn't in the same project.)
972 // User is prompted here to close those KIWAY_PLAYERs:
973 if( !CloseProject( true ) )
974 return false;
975
976 m_active_project = true;
977
978 // NB: when loading a legacy project SETTINGS_MANAGER::LoadProject() will convert it to
979 // current extension. Be very careful with aProjectFileName vs. Prj().GetProjectPath()
980 // from here on out.
981
982 Pgm().GetSettingsManager().LoadProject( fullPath );
983
984 // Propagate lock override decision to the loaded project
985 if( lockOverrideGranted )
986 Prj().SetLockOverrideGranted( true );
987
988 LoadWindowState( aProjectFileName.GetFullName() );
989
990 if( aProjectFileName.IsDirWritable() )
991 SetMruPath( Prj().GetProjectPath() );
992
993 if( Kiway().LocalHistory().HeadNewerThanLastSave( Prj().GetProjectPath() ) )
994 {
995 wxString head = Kiway().LocalHistory().GetHeadHash( Prj().GetProjectPath() );
996
997 KICAD_MESSAGE_DIALOG dlg( this, _( "KiCad found unsaved changes from your last session that are newer than "
998 "the saved project files." ),
999 _( "Recover Unsaved Changes" ), wxYES_NO | wxICON_QUESTION );
1000
1001 dlg.SetExtendedMessage( _( "This can happen if your previous session ended unexpectedly.\n\n"
1002 "Choose 'Restore' to recover those changes, or 'Discard' to keep the "
1003 "currently saved files." ) );
1004
1005 dlg.SetYesNoLabels( _( "Restore" ), _( "Discard" ) );
1006
1007 if( dlg.ShowModal() == wxID_YES )
1008 {
1009 Kiway().LocalHistory().RestoreCommit( Prj().GetProjectPath(), head, this );
1010 }
1011 else
1012 {
1013 // User declined; commit on-disk state and tag unconditionally so Last_Save anchors
1014 // at HEAD even if no new commit was needed.
1015 Kiway().LocalHistory().CommitFullProjectSnapshot( Prj().GetProjectPath(), wxS( "Declined restore" ) );
1016 Kiway().LocalHistory().TagSave( Prj().GetProjectPath(), wxS( "project" ) );
1017 }
1018 }
1019
1020 // Save history & window state to disk now. Don't wait around for a crash.
1021 KICAD_SETTINGS* settings = kicadSettings();
1022 SaveSettings( settings );
1023 settings->SaveToFile( Pgm().GetSettingsManager().GetPathForSettingsFile( settings ) );
1024
1025 m_projectTreePane->ReCreateTreePrj();
1026 m_historyPane->RefreshHistory( Prj().GetProjectPath() );
1027
1028 for( const wxString& jobset : Prj().GetLocalSettings().m_OpenJobSets )
1029 {
1030 wxFileName jobsetFn( jobset );
1031 jobsetFn.MakeAbsolute( Prj().GetProjectPath() );
1032
1033 if( jobsetFn.Exists() )
1034 OpenJobsFile( jobsetFn.GetFullPath(), false, false );
1035 }
1036
1037 // Always start with the apps page
1038 m_notebook->SetSelection( 0 );
1039
1040 // Rebuild the list of watched paths.
1041 // however this is possible only when the main loop event handler is running,
1042 // so we use it to run the rebuild function.
1043 wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_INIT_WATCHED_PATHS );
1044
1045 wxPostEvent( this, cmd );
1046
1047 PrintPrjInfo();
1048
1049 KIPLATFORM::APP::RegisterApplicationRestart( aProjectFileName.GetFullPath() );
1050 m_openSavedWindows = true;
1051
1052 KIPLATFORM::ENV::AddToRecentDocs( aProjectFileName.GetFullPath() );
1053
1054 // Now that we have a new project, trigger a library preload, which will load in any
1055 // project-specific symbol and footprint libraries into the manager
1057 return true;
1058}
1059
1060
1061void KICAD_MANAGER_FRAME::CreateNewProject( const wxFileName& aProjectFileName, bool aCreateStubFiles )
1062{
1063 wxCHECK_RET( aProjectFileName.DirExists() && aProjectFileName.IsDirWritable(),
1064 "Project folder must exist and be writable to create a new project." );
1065
1066 // If the project is legacy, convert it
1067 if( !aProjectFileName.FileExists() )
1068 {
1069 wxFileName legacyPro( aProjectFileName );
1070 legacyPro.SetExt( FILEEXT::LegacyProjectFileExtension );
1071
1072 if( legacyPro.FileExists() )
1073 {
1074 GetSettingsManager()->LoadProject( legacyPro.GetFullPath() );
1076
1077 wxRemoveFile( legacyPro.GetFullPath() );
1078 }
1079 else
1080 {
1081 // Copy template project file from template folder.
1082 wxString srcFileName = sys_search().FindValidPath( "kicad.kicad_pro" );
1083
1084 wxFileName destFileName( aProjectFileName );
1085 destFileName.SetExt( FILEEXT::ProjectFileExtension );
1086
1087 // Create a minimal project file if the template project file could not be copied
1088 if( !wxFileName::FileExists( srcFileName )
1089 || !wxCopyFile( srcFileName, destFileName.GetFullPath() ) )
1090 {
1091 wxFFile file( destFileName.GetFullPath(), "wb" );
1092
1093 if( file.IsOpened() )
1094 file.Write( wxT( "{\n}\n") );
1095
1096 // wxFFile dtor will close the file
1097 }
1098 }
1099 }
1100
1101 // Create a "stub" for a schematic root sheet and a board if requested.
1102 // It will avoid messages from the schematic editor or the board editor to create a new file
1103 // And forces the user to create main files under the right name for the project manager
1104 if( aCreateStubFiles )
1105 {
1106 wxFileName fn( aProjectFileName.GetFullPath() );
1108
1109 // If a <project>.kicad_sch file does not exist, create a "stub" file ( minimal schematic
1110 // file ).
1111 if( !fn.FileExists() )
1112 {
1113 wxFFile file( fn.GetFullPath(), "wb" );
1114
1115 if( file.IsOpened() )
1116 {
1117 file.Write( wxString::Format( "(kicad_sch\n"
1118 "\t(version %d)\n"
1119 "\t(generator \"eeschema\")\n"
1120 "\t(generator_version \"%s\")\n"
1121 "\t(uuid %s)\n"
1122 "\t(paper \"A4\")\n"
1123 "\t(lib_symbols)\n"
1124 "\t(sheet_instances\n"
1125 "\t\t(path \"/\"\n"
1126 "\t\t\t(page \"1\")\n"
1127 "\t\t)\n"
1128 "\t)\n"
1129 "\t(embedded_fonts no)\n"
1130 ")",
1133 KIID().AsString() ) );
1134 }
1135
1136 // wxFFile dtor will close the file
1137 }
1138
1139 // If a <project>.kicad_pcb or <project>.brd file does not exist,
1140 // create a .kicad_pcb "stub" file
1141 fn.SetExt( FILEEXT::KiCadPcbFileExtension );
1142 wxFileName leg_fn( fn );
1143 leg_fn.SetExt( FILEEXT::LegacyPcbFileExtension );
1144
1145 if( !fn.FileExists() && !leg_fn.FileExists() )
1146 {
1147 wxFFile file( fn.GetFullPath(), "wb" );
1148
1149 if( file.IsOpened() )
1150 {
1151 // Create a small dummy file as a stub for pcbnew:
1152 file.Write( wxString::Format( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version \"%s\")\n)",
1154 }
1155
1156 // wxFFile dtor will close the file
1157 }
1158 }
1159
1160 // Save history & window state to disk now. Don't wait around for a crash.
1161 KICAD_SETTINGS* settings = kicadSettings();
1162 SaveSettings( settings );
1163 settings->SaveToFile( Pgm().GetSettingsManager().GetPathForSettingsFile( settings ) );
1164
1165 m_openSavedWindows = true;
1166}
1167
1168
1170{
1171 git_repository* repo = ( m_projectTreePane && m_projectTreePane->m_TreeProject )
1172 ? m_projectTreePane->m_TreeProject->GetGitRepo()
1173 : nullptr;
1174
1175 if( !repo )
1176 {
1177 wxMessageBox( _( "Compare Project Branches requires a project that is "
1178 "tracked by git." ),
1179 _( "Compare Branches" ),
1180 wxOK | wxICON_INFORMATION, this );
1181 return;
1182 }
1183
1184 // Seed the picker with the conventional "MR" pair: an integration branch
1185 // (main/master) as the base, the current branch as the head. Resolve the
1186 // current branch from HEAD so a checked-out feature branch lands at
1187 // index 1 (where DIALOG_GIT_MR_REVIEW selects its head ref) instead of
1188 // the literal "HEAD" string.
1189 wxString currentBranch;
1190 git_reference* head = nullptr;
1191
1192 if( git_repository_head( &head, repo ) == 0 )
1193 {
1194 const char* shorthand = git_reference_shorthand( head );
1195
1196 if( shorthand && *shorthand )
1197 currentBranch = wxString::FromUTF8( shorthand );
1198
1199 git_reference_free( head );
1200 }
1201
1202 std::vector<wxString> refs;
1203
1204 auto pushUnique = [&]( const wxString& aRef )
1205 {
1206 if( aRef.IsEmpty() )
1207 return;
1208
1209 for( const wxString& existing : refs )
1210 {
1211 if( existing == aRef )
1212 return;
1213 }
1214
1215 refs.push_back( aRef );
1216 };
1217
1218 pushUnique( wxS( "main" ) );
1219 pushUnique( wxS( "master" ) );
1220 pushUnique( currentBranch );
1221 pushUnique( wxS( "HEAD" ) );
1222
1223 DIALOG_GIT_MR_REVIEW dlg( this, repo, refs );
1224 dlg.ShowModal();
1225}
1226
1227
1229{
1230 // show all files in file dialog (in Kicad all files are editable texts):
1231 wxString wildcard = FILEEXT::AllFilesWildcard();
1232
1233 wxString default_dir = Prj().GetProjectPath();
1234
1235 wxFileDialog dlg( this, _( "Edit File in Text Editor" ), default_dir, wxEmptyString, wildcard,
1236 wxFD_OPEN );
1237
1239
1240 if( dlg.ShowModal() == wxID_CANCEL )
1241 return;
1242
1243 wxString filename = dlg.GetPath();
1244
1245 if( !dlg.GetPath().IsEmpty() && !Pgm().GetTextEditor().IsEmpty() )
1246 m_toolManager->RunAction<wxString*>( KICAD_MANAGER_ACTIONS::openTextEditor, &filename );
1247}
1248
1249
1250void KICAD_MANAGER_FRAME::OnEditAdvancedCfg( wxCommandEvent& WXUNUSED( event ) )
1251{
1252 DIALOG_EDIT_CFG dlg( this );
1253 dlg.ShowModal();
1254}
1255
1256
1258{
1259 m_projectTreePane->ReCreateTreePrj();
1260}
1261
1262
1264{
1265 // call my base class
1267
1268 // tooltips in toolbars
1270 m_launcher->CreateLaunchers();
1271
1272 // update captions
1273 int pageId = m_notebook->FindPage( m_launcher );
1274
1275 if( pageId != wxNOT_FOUND )
1276 m_notebook->SetPageText( pageId, EDITORS_CAPTION );
1277
1278 m_auimgr.GetPane( m_projectTreePane ).Caption( PROJECT_FILES_CAPTION );
1279 m_auimgr.Update();
1280
1281 m_projectTreePane->FileWatcherReset();
1282
1283 PrintPrjInfo();
1284}
1285
1286
1288{
1290
1291 if( m_pcm && ( aFlags & ENVVARS_CHANGED ) )
1292 m_pcm->ReadEnvVar();
1293
1294 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
1295
1296 if( m_lastToolbarIconSize == 0
1298 {
1301 }
1302
1303 m_projectTreePane->ReCreateTreePrj();
1304}
1305
1306
1308{
1309 wxString file = GetProjectFileName();
1310
1311 // empty file string means no project loaded
1312 if( !Prj().IsNullProject() &&
1313 Prj().GetProjectLock() == nullptr )
1314 {
1315 LOCKFILE lockFile( file );
1316
1317 if( !lockFile.Valid() && lockFile.IsLockedByMe() )
1318 {
1319 // If we cannot acquire the lock but we appear to be the one who
1320 // locked it, check to see if there is another KiCad instance running.
1321 // If there is not, then we can override the lock. This could happen if
1322 // KiCad crashed or was interrupted
1323 if( !Pgm().SingleInstance()->IsAnotherRunning() )
1324 {
1325 lockFile.OverrideLock();
1326 }
1327 }
1328
1329 if( !lockFile.Valid() )
1330 {
1331 wxString msg;
1332 msg.Printf( _( "Project '%s' is already open by '%s' at '%s'." ),
1333 file,
1334 lockFile.GetUsername(),
1335 lockFile.GetHostname() );
1336
1337 if( AskOverrideLock( this, msg ) )
1338 {
1339 lockFile.OverrideLock();
1340 }
1341 }
1342
1343 Prj().SetReadOnly( !lockFile.Valid() || Prj().GetProjectFile().IsReadOnly() );
1344 Prj().SetProjectLock( new LOCKFILE( std::move( lockFile ) ) );
1345 }
1346
1347 wxString title;
1348
1349 if( !file.IsEmpty() )
1350 {
1351 wxFileName fn( file );
1352
1353 title = fn.GetName();
1354
1355 if( Prj().IsReadOnly() )
1356 title += wxS( " " ) + _( "[Read Only]" );
1357 }
1358 else
1359 {
1360 title = _( "[no project loaded]" );
1361 }
1362
1363 if( ADVANCED_CFG::GetCfg().m_HideVersionFromTitle )
1364 title += wxT( " \u2014 " ) + wxString( wxS( "KiCad" ) );
1365 else
1366 title += wxT( " \u2014 " ) + wxString( wxS( "KiCad " ) ) + GetMajorMinorVersion();
1367
1368 SetTitle( title );
1369
1370 // Register project file saver. Ensures project file participates in
1371 // autosave history commits without affecting dirty state.
1373 [this]( const wxString& aProjectPath, std::vector<HISTORY_FILE_DATA>& aFileData )
1374 {
1375 Prj().SaveToHistory( aProjectPath, aFileData );
1376 } );
1377}
1378
1379
1381{
1383
1384 auto settings = dynamic_cast<KICAD_SETTINGS*>( aCfg );
1385
1386 wxCHECK( settings, /*void*/ );
1387
1388 m_leftWinWidth = settings->m_LeftWinWidth;
1389 m_showHistoryPanel = settings->m_ShowHistoryPanel;
1390}
1391
1392
1394{
1396
1397 KICAD_SETTINGS* settings = dynamic_cast<KICAD_SETTINGS*>( aCfg );
1398
1399 wxCHECK( settings, /*void*/ );
1400
1401 settings->m_LeftWinWidth = m_projectTreePane->GetSize().x;
1402 settings->m_ShowHistoryPanel = m_historyPane && m_auimgr.GetPane( m_historyPane ).IsShown();
1403
1404 if( !m_isClosing )
1406}
1407
1408
1410{
1411 // wxStatusBar's wxELLIPSIZE_MIDDLE flag doesn't work (at least on Mac).
1412
1413 wxString status = wxString::Format( _( "Project: %s" ), Prj().GetProjectFullName() );
1414 KISTATUSBAR* statusBar = static_cast<KISTATUSBAR*>( GetStatusBar() );
1415 statusBar->SetEllipsedTextField( status, 0 );
1416}
1417
1418
1423
1424
1425void KICAD_MANAGER_FRAME::OnIdle( wxIdleEvent& aEvent )
1426{
1432 if( !m_openSavedWindows )
1433 return;
1434
1435 m_openSavedWindows = false;
1436
1437 if( Pgm().GetCommonSettings()->m_Session.remember_open_files )
1438 {
1439 int previousOpenCount = std::count_if( Prj().GetLocalSettings().m_files.begin(),
1440 Prj().GetLocalSettings().m_files.end(),
1441 [&]( const PROJECT_FILE_STATE& f )
1442 {
1443 return !f.fileName.EndsWith( FILEEXT::ProjectFileExtension ) && f.open;
1444 } );
1445
1446 if( previousOpenCount > 0 )
1447 {
1448 APP_PROGRESS_DIALOG progressReporter( _( "Restoring session" ), wxEmptyString, previousOpenCount, this );
1449
1450 // We don't currently support opening more than one view per file
1451 std::set<wxString> openedFiles;
1452
1453 int i = 0;
1454
1455 for( const PROJECT_FILE_STATE& file : Prj().GetLocalSettings().m_files )
1456 {
1457 if( file.open && !openedFiles.count( file.fileName ) )
1458 {
1459 progressReporter.Update( i++, wxString::Format( _( "Restoring '%s'" ), file.fileName ) );
1460
1461 openedFiles.insert( file.fileName );
1462 wxFileName fn( file.fileName );
1463
1464 if( fn.GetExt() == FILEEXT::LegacySchematicFileExtension
1465 || fn.GetExt() == FILEEXT::KiCadSchematicFileExtension )
1466 {
1468 }
1469 else if( fn.GetExt() == FILEEXT::LegacyPcbFileExtension
1470 || fn.GetExt() == FILEEXT::KiCadPcbFileExtension )
1471 {
1473 }
1474 }
1475
1476 wxYield();
1477 }
1478 }
1479 }
1480
1481 // clear file states regardless if we opened windows or not due to setting
1483
1484 // After restore from history, mark open editors as dirty so user is prompted to save
1486 {
1487 m_restoredFromHistory = false;
1488
1489 // Mark schematic editor as dirty if open
1490 if( KIWAY_PLAYER* schFrame = Kiway().Player( FRAME_SCH, false ) )
1491 schFrame->OnModify();
1492
1493 // Mark PCB editor as dirty if open
1494 if( KIWAY_PLAYER* pcbFrame = Kiway().Player( FRAME_PCB_EDITOR, false ) )
1495 pcbFrame->OnModify();
1496 }
1497
1498 KICAD_SETTINGS* settings = kicadSettings();
1499
1501 && settings->m_PcmUpdateCheck )
1502 {
1503 if( !m_pcm )
1504 CreatePCM();
1505
1506 m_pcm->RunBackgroundUpdate();
1507 }
1508
1509#ifdef KICAD_UPDATE_CHECK
1510 if( !m_updateManager && settings->m_KiCadUpdateCheck )
1511 {
1512 m_updateManager = std::make_unique<UPDATE_MANAGER>();
1513 m_updateManager->CheckForUpdate( this );
1514 }
1515#endif
1516
1517 // This little diddy is needed to get the window put into the Mac dock icon's context menu.
1518 Raise();
1519}
1520
1521
1523{
1524 m_pcmButton = aButton;
1525
1527}
1528
1529
1531{
1532 if( m_pcmButton )
1533 {
1534 if( m_pcmUpdateCount > 0 )
1535 {
1536 m_pcmButton->SetShowBadge( true );
1537 m_pcmButton->SetBadgeText( wxString::Format( "%d", m_pcmUpdateCount ) );
1538 }
1539 else
1540 {
1541 m_pcmButton->SetShowBadge( false );
1542 }
1543
1544 m_pcmButton->Refresh();
1545 }
1546}
1547
1548
1550{
1551 // No idea why, but the same mechanism used in EDA_DRAW_FRAME doesn't work here
1552 // the only thing that seems to work is to blow it all up and start from scratch.
1553 m_auimgr.DetachPane( m_tbLeft );
1554 delete m_tbLeft;
1555 m_tbLeft = nullptr;
1557 m_auimgr.AddPane( m_tbLeft, EDA_PANE().HToolbar().Name( "TopMainToolbar" ).Left().Layer( 2 ) );
1558
1559 m_auimgr.Update();
1560}
1561
1562
1564{
1565 wxAuiPaneInfo& pane = m_auimgr.GetPane( m_historyPane );
1566 bool show = !pane.IsShown();
1567 pane.Show( show );
1568
1569 if( show )
1570 m_historyPane->RefreshHistory( Prj().GetProjectPath() );
1571
1572 m_auimgr.Update();
1573}
1574
1575
1577{
1578 if( !Kiway().PlayersClose( true ) )
1579 return;
1580
1581 if( Kiway().LocalHistory().RestoreCommit( Prj().GetProjectPath(), aHash, this ) )
1582 {
1583 m_restoredFromHistory = true; // Mark editors dirty when they reopen
1584 }
1585
1586 m_projectTreePane->ReCreateTreePrj();
1587 m_openSavedWindows = true;
1588 m_historyPane->RefreshHistory( Prj().GetProjectPath() );
1589}
1590
1591
1593{
1594 return m_historyPane && m_auimgr.GetPane( m_historyPane ).IsShown();
1595}
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.
const wxString PcbLegacyFileName()
void OnEditAdvancedCfg(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:44
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.
bool RestoreCommit(const wxString &aProjectPath, const wxString &aHash, wxWindow *aParent=nullptr)
Restore the project files to the state recorded by the given commit hash.
void WaitForPendingSave()
Block until any pending background save completes.
void RegisterSaver(const void *aSaverObject, const std::function< void(const wxString &, std::vector< HISTORY_FILE_DATA > &)> &aSaver)
Register a saver callback invoked during autosave history commits.
bool CommitFullProjectSnapshot(const wxString &aProjectPath, const wxString &aTitle)
Commit a snapshot of the entire project directory (excluding the .history directory and ignored trans...
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:120
void SetClosable(bool aYes)
void SetProjectTied(bool aYes)
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:528
void PreloadDesignBlockLibraries(KIWAY *aKiway)
Starts a background job to preload the global and project design block libraries.
Definition pgm_base.cpp:874
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.
Definition pgm_base.cpp:975
bool m_Quitting
Definition pgm_base.h:386
void RegisterLibraryLoadStatusBar(KISTATUSBAR *aStatusBar)
Register a status bar to receive library load warning messages.
Definition pgm_base.cpp:955
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:160
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:206
virtual void SetLockOverrideGranted(bool aGranted=true)
Definition project.h:164
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:61
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:42
@ 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: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_EASYEDA_PROJECT
Definition kicad_id.h:37
@ ID_IMPORT_ALTIUM_PROJECT
Definition kicad_id.h:39
@ ID_COMPARE_PROJECT_BRANCHES
Definition kicad_id.h:43
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:448
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