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