From 686b768a3d6a3dbbe889c43e81b67f91182cd28d Mon Sep 17 00:00:00 2001 From: Mark Roszko Date: Thu, 19 Dec 2019 14:11:11 +0000 Subject: [PATCH] Add shutdown blocking on Windows for pcbnew, eeschema and pleditor ADDED: Block shutdown/logoff on Windows when contents have been modified --- common/eda_base_frame.cpp | 42 +++++++++++++++++++++++++++ cvpcb/cvpcb_mainframe.cpp | 15 ++++++++-- eeschema/files-io.cpp | 2 ++ eeschema/libedit/lib_edit_frame.cpp | 19 ++++++++++-- eeschema/sch_edit_frame.cpp | 14 ++++++++- include/eda_base_frame.h | 18 ++++++++++++ pagelayout_editor/pl_editor_frame.cpp | 18 ++++++++++++ pcbnew/footprint_edit_frame.cpp | 16 ++++++++-- pcbnew/pcb_edit_frame.cpp | 19 ++++++++++-- 9 files changed, 151 insertions(+), 12 deletions(-) diff --git a/common/eda_base_frame.cpp b/common/eda_base_frame.cpp index 55b3e3b8e4..43c6cfe0fc 100644 --- a/common/eda_base_frame.cpp +++ b/common/eda_base_frame.cpp @@ -159,6 +159,48 @@ void EDA_BASE_FRAME::windowClosing( wxCloseEvent& event ) EDA_BASE_FRAME::~EDA_BASE_FRAME() { delete m_autoSaveTimer; + + if( SupportsShutdownBlockReason() ) + { + RemoveShutdownBlockReason(); + } +} + + +bool EDA_BASE_FRAME::SupportsShutdownBlockReason() +{ +#if defined( _WIN32 ) + return true; +#else + return false; +#endif +} + + +void EDA_BASE_FRAME::RemoveShutdownBlockReason() +{ +#if defined( _WIN32 ) + // Windows: Destroys any block reason that may have existed + ShutdownBlockReasonDestroy( GetHandle() ); +#endif +} + + +void EDA_BASE_FRAME::SetShutdownBlockReason( const wxString& aReason ) +{ +#if defined( _WIN32 ) + // Windows: sets up the pretty message on the shutdown page on why it's being "blocked" + // This is used in conjunction with handling WM_QUERYENDSESSION (wxCloseEvent) + // ShutdownBlockReasonCreate does not block by itself + + ShutdownBlockReasonDestroy( GetHandle() ); // Destroys any existing or nonexisting reason + + if( !ShutdownBlockReasonCreate( GetHandle(), aReason.wc_str() ) ) + { + // Nothing bad happens if this fails, at worst it uses a generic application is preventing shutdown message + wxLogDebug( wxT( "ShutdownBlockReasonCreate failed to set reason: %s" ), aReason ); + } +#endif } diff --git a/cvpcb/cvpcb_mainframe.cpp b/cvpcb/cvpcb_mainframe.cpp index 79d3d98ae6..0038206ff3 100644 --- a/cvpcb/cvpcb_mainframe.cpp +++ b/cvpcb/cvpcb_mainframe.cpp @@ -178,6 +178,8 @@ CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) : // Ensure the toolbars are sync'd properly so the filtering options display correct SyncToolbars(); + + SetShutdownBlockReason( _( "Symbol to footprint changes are unsaved" ) ); } @@ -302,15 +304,22 @@ void CVPCB_MAINFRAME::setupEventHandlers() } -void CVPCB_MAINFRAME::OnCloseWindow( wxCloseEvent& Event ) +void CVPCB_MAINFRAME::OnCloseWindow( wxCloseEvent& aEvent ) { if( m_modified ) { + // Shutdown blocks must be determined and vetoed as early as possible + if( SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION ) + { + aEvent.Veto(); + return; + } + if( !HandleUnsavedChanges( this, _( "Symbol to Footprint links have been modified. " "Save changes?" ), [&]()->bool { return SaveFootprintAssociation( false ); } ) ) { - Event.Veto(); + aEvent.Veto(); return; } } @@ -326,7 +335,7 @@ void CVPCB_MAINFRAME::OnCloseWindow( wxCloseEvent& Event ) // Skip the close event. Looks like needed to have the close event sent to the // root class EDA_BASE_FRAME, and save config - Event.Skip(); + aEvent.Skip(); } diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 2ab60d24e4..e03e9ab4b1 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -271,6 +271,8 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL ); Prj().SchSymbolLibTable(); + SetShutdownBlockReason( _( "Schematic file changes are unsaved" ) ); + if( is_new ) { // mark new, unsaved file as modified. diff --git a/eeschema/libedit/lib_edit_frame.cpp b/eeschema/libedit/lib_edit_frame.cpp index fc03450b9c..30af356ab5 100644 --- a/eeschema/libedit/lib_edit_frame.cpp +++ b/eeschema/libedit/lib_edit_frame.cpp @@ -189,6 +189,8 @@ LIB_EDIT_FRAME::LIB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : GetCanvas()->GetView()->SetBoundary( bbox ); m_toolManager->RunAction( ACTIONS::zoomFitScreen, true ); + + SetShutdownBlockReason( _( "Library changes are unsaved" ) ); } @@ -232,12 +234,25 @@ void LIB_EDIT_FRAME::setupTools() } -void LIB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) +void LIB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& aEvent ) { + // Shutdown blocks must be determined and vetoed as early as possible + if( SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION ) + { + for( const auto& libNickname : m_libMgr->GetLibraryNames() ) + { + if( m_libMgr->IsLibraryModified( libNickname ) ) + { + aEvent.Veto(); + return; + } + } + } + if( saveAllLibraries( true ) ) Destroy(); else - Event.Veto(); + aEvent.Veto(); } diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 53e0af391a..54e2a454ab 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -301,6 +301,9 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ): DefaultExecFlags(); UpdateTitle(); + + // Default shutdown reason until a file is loaded + SetShutdownBlockReason( _( "New schematic file is unsaved" ) ); } @@ -497,6 +500,16 @@ void SCH_EDIT_FRAME::SaveUndoItemInUndoList( SCH_ITEM* aItem, bool aAppend ) void SCH_EDIT_FRAME::OnCloseWindow( wxCloseEvent& aEvent ) { + SCH_SHEET_LIST sheetList( g_RootSheet ); + + // Shutdown blocks must be determined and vetoed as early as possible + if( SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION + && sheetList.IsModified() ) + { + aEvent.Veto(); + return; + } + if( Kiface().IsSingle() ) { LIB_EDIT_FRAME* libeditFrame = (LIB_EDIT_FRAME*) Kiway().Player( FRAME_SCH_LIB_EDITOR, false ); @@ -518,7 +531,6 @@ void SCH_EDIT_FRAME::OnCloseWindow( wxCloseEvent& aEvent ) if( simFrame && !simFrame->Close() ) // Can close the simulator? return; - SCH_SHEET_LIST sheetList( g_RootSheet ); if( sheetList.IsModified() ) { diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h index 2ac5f8e523..fca09f5c08 100644 --- a/include/eda_base_frame.h +++ b/include/eda_base_frame.h @@ -466,6 +466,24 @@ public: virtual void RefreshCanvas() { }; const wxString& GetAboutTitle() const { return m_AboutTitle; } + + /** + * Sets the block reason why the window/application is preventing OS shutdown. + * This should be set far ahead of any close event. + * + * This is mainly intended for Windows platforms where this is a native feature. + */ + void SetShutdownBlockReason( const wxString& reason ); + + /** + * Removes any shutdown block reason set + */ + void RemoveShutdownBlockReason(); + + /** + * Whether or not the window supports setting a shutdown block reason + */ + bool SupportsShutdownBlockReason(); }; diff --git a/pagelayout_editor/pl_editor_frame.cpp b/pagelayout_editor/pl_editor_frame.cpp index 7acf2be88c..083e04739c 100644 --- a/pagelayout_editor/pl_editor_frame.cpp +++ b/pagelayout_editor/pl_editor_frame.cpp @@ -264,6 +264,14 @@ void PL_EDITOR_FRAME::OnExit( wxCommandEvent& aEvent ) void PL_EDITOR_FRAME::OnCloseWindow( wxCloseEvent& aEvent ) { + // Shutdown blocks must be determined and vetoed as early as possible + if( SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION + && GetScreen()->IsModify() ) + { + aEvent.Veto(); + return; + } + if( GetScreen()->IsModify() ) { wxFileName filename = GetCurrFileName(); @@ -764,6 +772,16 @@ void PL_EDITOR_FRAME::OnNewPageLayout() UpdateTitleAndInfo(); m_toolManager->RunAction( ACTIONS::zoomFitScreen, true ); + + if( GetCurrFileName().IsEmpty() ) + { + // Default shutdown reason until a file is loaded + SetShutdownBlockReason( _( "New page layout file is unsaved" ) ); + } + else + { + SetShutdownBlockReason( _( "Page layout changes are unsaved" ) ); + } } diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index 90de7925b5..7ca26f766b 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -223,6 +223,9 @@ FOOTPRINT_EDIT_FRAME::FOOTPRINT_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent, updateTitle(); InitExitKey(); + // Default shutdown reason until a file is loaded + SetShutdownBlockReason( _( "Footprint changes are unsaved" ) ); + Raise(); // On some window managers, this is needed Show( true ); } @@ -444,17 +447,24 @@ const BOX2I FOOTPRINT_EDIT_FRAME::GetDocumentExtents() const } -void FOOTPRINT_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) +void FOOTPRINT_EDIT_FRAME::OnCloseWindow( wxCloseEvent& aEvent ) { if( GetScreen()->IsModify() && GetBoard()->GetFirstModule() ) { + // Shutdown blocks must be determined and vetoed as early as possible + if( SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION ) + { + aEvent.Veto(); + return; + } + wxString footprintName = GetBoard()->GetFirstModule()->GetFPID().GetLibItemName(); wxString msg = _( "Save changes to \"%s\" before closing?" ); if( !HandleUnsavedChanges( this, wxString::Format( msg, footprintName ), [&]() -> bool { return SaveFootprint( GetBoard()->GetFirstModule() ); } ) ) { - Event.Veto(); + aEvent.Veto(); return; } } @@ -470,7 +480,7 @@ void FOOTPRINT_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) Clear_Pcb( false ); // Close the editor - Event.Skip(); + aEvent.Skip(); } diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 503620b133..5e69a02dbc 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -310,6 +310,9 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : GetCanvas()->GetView()->SetScale( GetZoomLevelCoeff() / GetScreen()->GetZoom() ); ActivateGalCanvas(); + // Default shutdown reason until a file is loaded + SetShutdownBlockReason( _( "New PCB file is unsaved" ) ); + // disable Export STEP item if kicad2step does not exist wxString strK2S = Pgm().GetExecutablePath(); @@ -464,8 +467,16 @@ void PCB_EDIT_FRAME::OnQuit( wxCommandEvent& event ) } -void PCB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) +void PCB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& aEvent ) { + // Shutdown blocks must be determined and vetoed as early as possible + if( SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION + && GetScreen()->IsModify() && !GetBoard()->IsEmpty() ) + { + aEvent.Veto(); + return; + } + // First close the DRC dialog. // For some reason, if the board editor frame is destroyed when the DRC // dialog currently open, Pcbnew crashes, At least on Windows. @@ -483,7 +494,7 @@ void PCB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ), [&]()->bool { return Files_io_from_id( ID_SAVE_BOARD ); } ) ) { - Event.Veto(); + aEvent.Veto(); return; } } @@ -538,7 +549,7 @@ void PCB_EDIT_FRAME::OnCloseWindow( wxCloseEvent& Event ) Show( false ); // Close frame: - Event.Skip(); + aEvent.Skip(); } @@ -672,6 +683,8 @@ void PCB_EDIT_FRAME::onBoardLoaded() SetMsgPanel( GetBoard() ); SetStatusText( wxEmptyString ); + + SetShutdownBlockReason( _( "PCB file changes are unsaved" ) ); }