diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 78180b3679..c0328b34da 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -289,6 +289,7 @@ set( COMMON_SRCS exceptions.cpp executable_names.cpp filename_resolver.cpp + filehistory.cpp filter_reader.cpp footprint_filter.cpp footprint_info.cpp diff --git a/common/bin_mod.cpp b/common/bin_mod.cpp index 0950416f54..74b0302c9d 100644 --- a/common/bin_mod.cpp +++ b/common/bin_mod.cpp @@ -24,8 +24,9 @@ #include #include -#include +#include #include // for ID_FILE1 and FILE_HISTORY_SIZE +#include BIN_MOD::BIN_MOD( const char* aName ) : diff --git a/common/eda_base_frame.cpp b/common/eda_base_frame.cpp index fcb33235dd..55b3e3b8e4 100644 --- a/common/eda_base_frame.cpp +++ b/common/eda_base_frame.cpp @@ -42,6 +42,7 @@ #include #include #include +#include /// The default auto save interval is 10 minutes. @@ -561,10 +562,9 @@ void EDA_BASE_FRAME::PrintMsg( const wxString& text ) } -void EDA_BASE_FRAME::UpdateFileHistory( const wxString& FullFileName, - wxFileHistory* aFileHistory ) +void EDA_BASE_FRAME::UpdateFileHistory( const wxString& FullFileName, FILE_HISTORY* aFileHistory ) { - wxFileHistory* fileHistory = aFileHistory; + FILE_HISTORY* fileHistory = aFileHistory; if( !fileHistory ) fileHistory = &Kiface().GetFileHistory(); @@ -574,9 +574,9 @@ void EDA_BASE_FRAME::UpdateFileHistory( const wxString& FullFileName, wxString EDA_BASE_FRAME::GetFileFromHistory( int cmdId, const wxString& type, - wxFileHistory* aFileHistory ) + FILE_HISTORY* aFileHistory ) { - wxFileHistory* fileHistory = aFileHistory; + FILE_HISTORY* fileHistory = aFileHistory; if( !fileHistory ) fileHistory = &Kiface().GetFileHistory(); diff --git a/common/eda_draw_frame.cpp b/common/eda_draw_frame.cpp index 4268b5fd55..12dd6f83df 100644 --- a/common/eda_draw_frame.cpp +++ b/common/eda_draw_frame.cpp @@ -51,6 +51,7 @@ #include #include #include +#include ///@{ diff --git a/common/filehistory.cpp b/common/filehistory.cpp new file mode 100644 index 0000000000..ddba8e96d6 --- /dev/null +++ b/common/filehistory.cpp @@ -0,0 +1,145 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 Ian McInerney + * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include + +#include +using namespace std::placeholders; + + +FILE_HISTORY::FILE_HISTORY( size_t aMaxFiles, int aBaseFileId ) : + wxFileHistory( std::min( aMaxFiles, (size_t) MAX_FILE_HISTORY_SIZE ) ) +{ + SetBaseId( aBaseFileId ); +} + + +void FILE_HISTORY::SetMaxFiles( size_t aMaxFiles ) +{ + m_fileMaxFiles = std::min( aMaxFiles, (size_t) MAX_FILE_HISTORY_SIZE ); + + size_t numFiles = m_fileHistory.size(); + + while( numFiles > m_fileMaxFiles ) + RemoveFileFromHistory( --numFiles ); +} + + +void FILE_HISTORY::AddFileToHistory( const wxString &aFile ) +{ + wxFileHistory::AddFileToHistory( aFile ); + + // Iterate over each menu associated with this file history, and if it is one of our + // FILE_HISTORY_MENUs, we force it to be refreshed (so that the items are all in the + // correct locations). + for( wxList::compatibility_iterator node = m_fileMenus.GetFirst(); + node; node = node->GetNext() ) + { + wxMenu* menu = static_cast( node->GetData() ); + + FILE_HISTORY_MENU* fileMenu = dynamic_cast( menu ); + + if( fileMenu ) + fileMenu->RefreshMenu(); + } +} + + +SELECTION_CONDITION FILE_HISTORY::FileHistoryNotEmpty( const FILE_HISTORY& aHistory ) +{ + return std::bind( &FILE_HISTORY::isHistoryNotEmpty, _1, std::cref( aHistory ) ); +} + + +bool FILE_HISTORY::isHistoryNotEmpty( const SELECTION& aSelection, const FILE_HISTORY& aHistory ) +{ + return aHistory.GetCount() != 0; +} + + +FILE_HISTORY_MENU::FILE_HISTORY_MENU( FILE_HISTORY& aHistory, wxString aClearText ) : + ACTION_MENU( false ), + m_fileHistory( aHistory ), + m_clearText( aClearText ) +{ + m_fileHistory.UseMenu( this ); + buildMenu(); +} + + +FILE_HISTORY_MENU::~FILE_HISTORY_MENU() +{ + m_fileHistory.RemoveMenu( this ); +} + + +void FILE_HISTORY_MENU::RefreshMenu() +{ + // We have to manually delete all menu items before we rebuild the menu + for( int i = GetMenuItemCount() - 1; i >= 0; --i ) + Destroy( FindItemByPosition( i ) ); + + buildMenu(); +} + + +void FILE_HISTORY_MENU::buildMenu() +{ + if( m_fileHistory.GetCount() == 0 ) + { + // If the history is empty, we create an item to say there are no files + wxMenuItem* item = new wxMenuItem( this, wxID_ANY, _( "No Files" ) ); + + Append( item ); + Enable( item->GetId(), false ); + } + else + m_fileHistory.AddFilesToMenu( this ); + + wxMenuItem* clearItem = new wxMenuItem( this, ID_FILE_LIST_CLEAR, m_clearText ); + + AppendSeparator(); + Append( clearItem ); + Connect( ID_FILE_LIST_CLEAR, wxEVT_COMMAND_MENU_SELECTED, + wxMenuEventHandler( FILE_HISTORY_MENU::onClearEntries ), NULL, this ); +} + + +void FILE_HISTORY_MENU::onClearEntries( wxMenuEvent& aEvent ) +{ + while( m_fileHistory.GetCount() > 0 ) + m_fileHistory.RemoveFileFromHistory( 0 ); + + RefreshMenu(); +} + + +ACTION_MENU* FILE_HISTORY_MENU::create() const +{ + return new FILE_HISTORY_MENU( m_fileHistory, m_clearText ); +} diff --git a/common/pgm_base.cpp b/common/pgm_base.cpp index ea618762d3..bf8ae33e9c 100644 --- a/common/pgm_base.cpp +++ b/common/pgm_base.cpp @@ -110,24 +110,6 @@ LANGUAGE_DESCR LanguagesList[] = #define _(s) wxGetTranslation((s)) -FILE_HISTORY::FILE_HISTORY( size_t aMaxFiles, int aBaseFileId ) : - wxFileHistory( std::min( aMaxFiles, (size_t) MAX_FILE_HISTORY_SIZE ) ) -{ - SetBaseId( aBaseFileId ); -} - - -void FILE_HISTORY::SetMaxFiles( size_t aMaxFiles ) -{ - m_fileMaxFiles = std::min( aMaxFiles, (size_t) MAX_FILE_HISTORY_SIZE ); - - size_t numFiles = m_fileHistory.size(); - - while( numFiles > m_fileMaxFiles ) - RemoveFileFromHistory( --numFiles ); -} - - PGM_BASE::PGM_BASE() { m_pgm_checker = NULL; diff --git a/eeschema/menubar.cpp b/eeschema/menubar.cpp index 0fa73e18e7..971e3afa6a 100644 --- a/eeschema/menubar.cpp +++ b/eeschema/menubar.cpp @@ -24,6 +24,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #include #include @@ -52,27 +53,26 @@ void SCH_EDIT_FRAME::ReCreateMenuBar() //-- File menu ----------------------------------------------------------- // - CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, selTool ); - static ACTION_MENU* openRecentMenu; + CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, selTool ); + static FILE_HISTORY_MENU* openRecentMenu; if( Kiface().IsSingle() ) // not when under a project mgr { - // Add this menu to list menu managed by m_fileHistory - // (the file history will be updated when adding/removing files in history) - if( openRecentMenu ) - Kiface().GetFileHistory().RemoveMenu( openRecentMenu ); + FILE_HISTORY& fileHistory = Kiface().GetFileHistory(); - openRecentMenu = new ACTION_MENU( false ); - openRecentMenu->SetTool( selTool ); - openRecentMenu->SetTitle( _( "Open Recent" ) ); - openRecentMenu->SetIcon( recent_xpm ); - - Kiface().GetFileHistory().UseMenu( openRecentMenu ); - Kiface().GetFileHistory().AddFilesToMenu( openRecentMenu ); + // Create the menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentMenu ) + { + openRecentMenu = new FILE_HISTORY_MENU( fileHistory ); + openRecentMenu->SetTool( selTool ); + openRecentMenu->SetTitle( _( "Open Recent" ) ); + openRecentMenu->SetIcon( recent_xpm ); + } fileMenu->AddItem( ACTIONS::doNew, EE_CONDITIONS::ShowAlways ); fileMenu->AddItem( ACTIONS::open, EE_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentMenu, EE_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentMenu, FILE_HISTORY::FileHistoryNotEmpty( fileHistory ) ); fileMenu->AddSeparator(); } diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 59660ec32f..cd4922cd7b 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -317,6 +317,12 @@ SCH_EDIT_FRAME::~SCH_EDIT_FRAME() g_CurrentSheet = nullptr; g_ConnectionGraph = nullptr; g_RootSheet = NULL; + + // Since the file menu contains file history menus, we must ensure that the menu + // destructor is called before the file history objects are deleted since their destructor + // unregisters the menu from the history. + wxMenu* fileMenu = GetMenuBar()->Remove( 0 ); + delete fileMenu; } diff --git a/gerbview/gerbview_frame.cpp b/gerbview/gerbview_frame.cpp index be2dd0bc66..282d529283 100644 --- a/gerbview/gerbview_frame.cpp +++ b/gerbview/gerbview_frame.cpp @@ -226,6 +226,12 @@ GERBVIEW_FRAME::~GERBVIEW_FRAME() GetGerberLayout()->GetImagesList()->DeleteAllImages(); delete m_gerberLayout; + + // Since the file menu contains file history menus, we must ensure that the menu + // destructor is called before the file history objects are deleted since their destructor + // unregisters the menu from the history. + wxMenu* fileMenu = GetMenuBar()->Remove( 0 ); + delete fileMenu; } diff --git a/gerbview/gerbview_frame.h b/gerbview/gerbview_frame.h index 8b5eb8d7aa..f26429218f 100644 --- a/gerbview/gerbview_frame.h +++ b/gerbview/gerbview_frame.h @@ -22,7 +22,7 @@ #ifndef WX_GERBER_STRUCT_H #define WX_GERBER_STRUCT_H - +#include #include #include #include diff --git a/gerbview/menubar.cpp b/gerbview/menubar.cpp index 1f26cc220f..3ee60e232f 100644 --- a/gerbview/menubar.cpp +++ b/gerbview/menubar.cpp @@ -46,80 +46,77 @@ void GERBVIEW_FRAME::ReCreateMenuBar() //-- File menu ------------------------------------------------------- // - CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, selTool ); - static ACTION_MENU* openRecentGbrMenu; - static ACTION_MENU* openRecentDrlMenu; - static ACTION_MENU* openRecentJobMenu; - static ACTION_MENU* openRecentZipMenu; + CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, selTool ); + static FILE_HISTORY_MENU* openRecentGbrMenu; + static FILE_HISTORY_MENU* openRecentDrlMenu; + static FILE_HISTORY_MENU* openRecentJobMenu; + static FILE_HISTORY_MENU* openRecentZipMenu; + FILE_HISTORY& recentGbrFiles = Kiface().GetFileHistory(); - // Add this menu to list menu managed by m_fileHistory - // (the file history will be updated when adding/removing files in history) - if( openRecentGbrMenu ) - Kiface().GetFileHistory().RemoveMenu( openRecentGbrMenu ); - openRecentGbrMenu = new ACTION_MENU( false ); - openRecentGbrMenu->SetTool( selTool ); - openRecentGbrMenu->SetTitle( _( "Open Recent Gerber" ) ); - openRecentGbrMenu->SetIcon( recent_xpm ); + // Create the gerber file menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentGbrMenu ) + { + openRecentGbrMenu = + new FILE_HISTORY_MENU( recentGbrFiles, _( "Clear Recent Gerber Files" ) ); + openRecentGbrMenu->SetTool( selTool ); + openRecentGbrMenu->SetTitle( _( "Open Recent Gerber File" ) ); + openRecentGbrMenu->SetIcon( recent_xpm ); + } - Kiface().GetFileHistory().UseMenu( openRecentGbrMenu ); - Kiface().GetFileHistory().AddFilesToMenu(); + // Create the drill file menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentDrlMenu ) + { + openRecentDrlMenu = + new FILE_HISTORY_MENU( m_drillFileHistory, _( "Clear Recent Drill Files" ) ); + openRecentDrlMenu->SetTool( selTool ); + openRecentDrlMenu->SetTitle( _( "Open Recent Drill File" ) ); + openRecentDrlMenu->SetIcon( recent_xpm ); + } - // Add drill file menu and the drill file history - if( openRecentDrlMenu ) - m_drillFileHistory.RemoveMenu( openRecentDrlMenu ); + // Create the job file menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentJobMenu ) + { + openRecentJobMenu = + new FILE_HISTORY_MENU( m_jobFileHistory, _( "Clear Recent Job Files" ) ); + openRecentJobMenu->SetTool( selTool ); + openRecentJobMenu->SetTitle( _( "Open Recent Job File" ) ); + openRecentJobMenu->SetIcon( recent_xpm ); + } - openRecentDrlMenu = new ACTION_MENU( false ); - openRecentDrlMenu->SetTool( selTool ); - openRecentDrlMenu->SetTitle( _( "Open Recent Drill File" ) ); - openRecentDrlMenu->SetIcon( recent_xpm ); - - m_drillFileHistory.UseMenu( openRecentDrlMenu ); - m_drillFileHistory.AddFilesToMenu( ); - - // Add job file menu and the job file history - if( openRecentJobMenu ) - m_jobFileHistory.RemoveMenu( openRecentJobMenu ); - - openRecentJobMenu = new ACTION_MENU( false ); - openRecentJobMenu->SetTool( selTool ); - openRecentJobMenu->SetTitle( _( "Open Recent Job" ) ); - - openRecentJobMenu->SetIcon( recent_xpm ); - m_jobFileHistory.UseMenu( openRecentJobMenu ); - m_jobFileHistory.AddFilesToMenu( ); - - // Add zip file menu and the zip file history - if( openRecentZipMenu ) - m_zipFileHistory.RemoveMenu( openRecentZipMenu ); - - openRecentZipMenu = new ACTION_MENU( false ); - openRecentZipMenu->SetTool( selTool ); - openRecentZipMenu->SetTitle( _( "Open Recent Zip" ) ); - openRecentZipMenu->SetIcon( recent_xpm ); - - m_zipFileHistory.UseMenu( openRecentZipMenu ); - m_zipFileHistory.AddFilesToMenu( ); + // Create the zip file menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentZipMenu ) + { + openRecentZipMenu = + new FILE_HISTORY_MENU( m_zipFileHistory, _( "Clear Recent Zip Files" ) ); + openRecentZipMenu->SetTool( selTool ); + openRecentZipMenu->SetTitle( _( "Open Recent Zip File" ) ); + openRecentZipMenu->SetIcon( recent_xpm ); + } fileMenu->AddItem( wxID_FILE, _( "Open &Gerber File(s)..." ), _( "Open Gerber file(s) on the current layer. Previous data will be deleted" ), load_gerber_xpm, SELECTION_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentGbrMenu, SELECTION_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentGbrMenu, FILE_HISTORY::FileHistoryNotEmpty( recentGbrFiles ) ); fileMenu->AddItem( ID_GERBVIEW_LOAD_DRILL_FILE, _( "Open &Excellon Drill File(s)..." ), _( "Open Excellon drill file(s) on the current layer. Previous data will be deleted" ), gerbview_drill_file_xpm, SELECTION_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentDrlMenu, SELECTION_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentDrlMenu, FILE_HISTORY::FileHistoryNotEmpty( m_drillFileHistory ) ); fileMenu->AddItem( ID_GERBVIEW_LOAD_JOB_FILE, _( "Open Gerber &Job File..." ), _( "Open a Gerber job file and its associated gerber files" ), gerber_job_file_xpm, SELECTION_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentJobMenu, SELECTION_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentJobMenu, FILE_HISTORY::FileHistoryNotEmpty( m_jobFileHistory ) ); fileMenu->AddItem( ID_GERBVIEW_LOAD_ZIP_ARCHIVE_FILE, _( "Open &Zip Archive File..." ), _( "Open a zipped archive (Gerber and Drill) file" ), zip_xpm, SELECTION_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentZipMenu, SELECTION_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentZipMenu, FILE_HISTORY::FileHistoryNotEmpty( m_zipFileHistory ) ); fileMenu->AddSeparator(); fileMenu->AddItem( ID_GERBVIEW_ERASE_ALL, _( "Clear &All Layers" ), diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h index 0fdac576ac..2ac5f8e523 100644 --- a/include/eda_base_frame.h +++ b/include/eda_base_frame.h @@ -75,6 +75,7 @@ class ACTIONS; class PAGED_DIALOG; class DIALOG_EDIT_LIBRARY_TABLES; class PANEL_HOTKEYS_EDITOR; +class FILE_HISTORY; enum id_librarytype { @@ -328,7 +329,7 @@ public: virtual void SaveSettings( wxConfigBase* aCfg ); /** - * @return a base name prefix used in Load/Save settings to build the full name of keys + * @return a base name prefix used in Load/Save settings to build the full name of keys * used in config. * This is usually the name of the frame set by CTOR, except for frames shown in multiple * modes in which case the m_configName must be set to the base name so that a single @@ -365,27 +366,27 @@ public: * Fetches the file name from the file history list. * * This removes the selected file, if this file does not exist. The menu is also updated, - * if wxFileHistory::UseMenu was called at init time + * if FILE_HISTORY::UseMenu was called at init time * * @param cmdId The command ID associated with the \a aFileHistory object. * @param type Please document me! - * @param aFileHistory The wxFileHistory in use. If null, the main application file + * @param aFileHistory The FILE_HISTORY in use. If null, the main application file * history is used * @return a wxString containing the selected filename */ wxString GetFileFromHistory( int cmdId, const wxString& type, - wxFileHistory* aFileHistory = NULL ); + FILE_HISTORY* aFileHistory = NULL ); /** * Update the list of recently opened files. * - * The menu is also updated, if wxFileHistory::UseMenu was called at init time. + * The menu is also updated, if FILE_HISTORY::UseMenu was called at init time. * * @param FullFileName The full file name including the path. - * @param aFileHistory The wxFileHistory in use. + * @param aFileHistory The FILE_HISTORY in use. * If NULL, the main application file history is used. */ - void UpdateFileHistory( const wxString& FullFileName, wxFileHistory * aFileHistory = NULL ); + void UpdateFileHistory( const wxString& FullFileName, FILE_HISTORY* aFileHistory = NULL ); void SetMruPath( const wxString& aPath ) { m_mruPath = aPath; } @@ -436,14 +437,14 @@ public: /** * Update the status bar information. * - * The status bar can draw itself. This is not a drawing function per se, but rather - * updates lines of text held by the components within the status bar which is owned + * The status bar can draw itself. This is not a drawing function per se, but rather + * updates lines of text held by the components within the status bar which is owned * by the wxFrame. */ virtual void UpdateStatusBar() { } /** - * Update the toolbars (mostly settings/check buttons/checkboxes) with the current + * Update the toolbars (mostly settings/check buttons/checkboxes) with the current * controller state. */ virtual void SyncToolbars() { }; @@ -458,7 +459,7 @@ public: * Update menus, toolbars, local variables, etc. */ virtual void CommonSettingsChanged( bool aEnvVarsChanged ); - + /** * Notification to refresh the drawing canvas (if any). */ diff --git a/include/filehistory.h b/include/filehistory.h new file mode 100644 index 0000000000..5437bf41fc --- /dev/null +++ b/include/filehistory.h @@ -0,0 +1,125 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 Ian McInerney + * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef FILEHISTORY_H_ +#define FILEHISTORY_H_ + +#include +#include +#include +#include + + +/** + * This class implements a file history object to store a list of files, that can then + * be added to a menu. + * + * This class extends the wxWidgets wxFileHistory class to include KiCad specific items. + */ +class FILE_HISTORY : public wxFileHistory +{ +public: + /** + * Create a file history object to store a list of files and add them to a menu. + * + * @param aMaxFiles is the number of files to store in the history + * @param aBaseFileId is the ID to use for the first file menu item + */ + FILE_HISTORY( size_t aMaxFiles, int aBaseFileId ); + + /** + * Adds a file to the history. + * + * This function overrides the default wxWidgets method to iterate through all + * menus associated with the file history, and if they are of the FILE_HISTORY_MENU + * type, call their RefreshMenu() function to update the menu display. + * + * @param aFile is the filename of the file to add to the history. + */ + void AddFileToHistory( const wxString &aFile ) override; + + /** + * Update the number of files that will be contained inside the file history. + * + * @param aMaxFiles is the new number of files for the history + */ + void SetMaxFiles( size_t aMaxFiles ); + + /** + * Create a SELECTION_CONDITION that can be used to enable a menu item when the + * file history has items in it. + * + * @param aHistory is the file history to check for items + * @return the selection condition function + */ + static SELECTION_CONDITION FileHistoryNotEmpty( const FILE_HISTORY& aHistory ); + +private: + static bool isHistoryNotEmpty( const SELECTION& aSelection, const FILE_HISTORY& aHistory ); +}; + +/** + * This class implements a menu container for a file history. It adds in the ability to clear + * the file history through a menu item. + */ +class FILE_HISTORY_MENU : public ACTION_MENU +{ +public: + /** + * Create the file history menu. + * + * @param aHistory is the file history to use in the menu + * @param aClearText is the text to use for the menu item that clears the history. + */ + FILE_HISTORY_MENU( FILE_HISTORY& aHistory, wxString aClearText = _( "Clear Recent Files" ) ); + + ~FILE_HISTORY_MENU(); + + /** + * Refresh the menu. This removes all entries from the menu and readds them, to ensure that the + * clear menu item is at the bottom of the menu. + */ + void RefreshMenu(); + +private: + //! @copydoc ACTION_MENU::create() + ACTION_MENU* create() const override; + + /** + * Construct the menu by adding the file history and menu items. + */ + void buildMenu(); + + /** + * Event handler for when the clear menu item is activated. + * + * @param aEvent the menu event + */ + void onClearEntries( wxMenuEvent& aEvent ); + + FILE_HISTORY& m_fileHistory; + wxString m_clearText; +}; + +#endif diff --git a/include/id.h b/include/id.h index e0d4c99985..a127112b40 100644 --- a/include/id.h +++ b/include/id.h @@ -88,6 +88,7 @@ enum main_id ID_FILE, ID_FILE1, ID_FILEMAX = ID_FILE + MAX_FILE_HISTORY_SIZE, + ID_FILE_LIST_CLEAR, ID_MENU_CANVAS_OPENGL, ID_MENU_CANVAS_CAIRO, diff --git a/include/pgm_base.h b/include/pgm_base.h index 5602df4c88..7abe53b8ab 100644 --- a/include/pgm_base.h +++ b/include/pgm_base.h @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -94,15 +93,6 @@ struct LANGUAGE_DESCR }; -class FILE_HISTORY : public wxFileHistory -{ -public: - FILE_HISTORY( size_t aMaxFiles, int aBaseFileId ); - - void SetMaxFiles( size_t aMaxFiles ); -}; - - // inter program module calling #define VTBL_ENTRY virtual diff --git a/kicad/kicad.cpp b/kicad/kicad.cpp index 6b497503df..1fc4da3e41 100644 --- a/kicad/kicad.cpp +++ b/kicad/kicad.cpp @@ -34,11 +34,12 @@ #include #include +#include #include #include #include -#include #include +#include #include diff --git a/kicad/kicad_manager_frame.cpp b/kicad/kicad_manager_frame.cpp index 9ea1948499..246b33d1e4 100644 --- a/kicad/kicad_manager_frame.cpp +++ b/kicad/kicad_manager_frame.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -173,6 +174,12 @@ KICAD_MANAGER_FRAME::~KICAD_MANAGER_FRAME() delete m_toolManager; m_auimgr.UnInit(); + + // Since the file menu contains file history menus, we must ensure that the menu + // destructor is called before the file history objects are deleted since their destructor + // unregisters the menu from the history. + wxMenu* fileMenu = GetMenuBar()->Remove( 0 ); + delete fileMenu; } diff --git a/kicad/menubar.cpp b/kicad/menubar.cpp index 2a38246637..2a12cf5853 100644 --- a/kicad/menubar.cpp +++ b/kicad/menubar.cpp @@ -25,6 +25,7 @@ */ #include +#include #include #include #include @@ -45,26 +46,25 @@ void KICAD_MANAGER_FRAME::ReCreateMenuBar() //-- File menu ----------------------------------------------------------- // - CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, controlTool ); - static ACTION_MENU* openRecentMenu; + CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, controlTool ); + FILE_HISTORY& fileHistory = PgmTop().GetFileHistory(); + static FILE_HISTORY_MENU* openRecentMenu; - // Before deleting, remove the menus managed by m_fileHistory - // (the file history will be updated when adding/removing files in history) - if( openRecentMenu ) - PgmTop().GetFileHistory().RemoveMenu( openRecentMenu ); - - openRecentMenu = new ACTION_MENU( false ); - openRecentMenu->SetTool( controlTool ); - openRecentMenu->SetTitle( _( "Open Recent" ) ); - openRecentMenu->SetIcon( recent_xpm ); - - PgmTop().GetFileHistory().UseMenu( openRecentMenu ); - PgmTop().GetFileHistory().AddFilesToMenu( openRecentMenu ); + // Create the menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentMenu ) + { + openRecentMenu = new FILE_HISTORY_MENU( fileHistory, _( "Clear Recent Projects" ) ); + openRecentMenu->SetTool( controlTool ); + openRecentMenu->SetTitle( _( "Open Recent" ) ); + openRecentMenu->SetIcon( recent_xpm ); + } fileMenu->AddItem( KICAD_MANAGER_ACTIONS::newProject, SELECTION_CONDITIONS::ShowAlways ); fileMenu->AddItem( KICAD_MANAGER_ACTIONS::newFromTemplate, SELECTION_CONDITIONS::ShowAlways ); fileMenu->AddItem( KICAD_MANAGER_ACTIONS::openProject, SELECTION_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentMenu, SELECTION_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentMenu, + FILE_HISTORY::FileHistoryNotEmpty( fileHistory ) ); fileMenu->AddSeparator(); fileMenu->AddItem( ACTIONS::saveAs, SELECTION_CONDITIONS::ShowAlways ); diff --git a/pagelayout_editor/menubar.cpp b/pagelayout_editor/menubar.cpp index 7d698d7536..ec517fec10 100644 --- a/pagelayout_editor/menubar.cpp +++ b/pagelayout_editor/menubar.cpp @@ -23,6 +23,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #include #include @@ -47,28 +48,26 @@ void PL_EDITOR_FRAME::ReCreateMenuBar() return GetScreen() && GetScreen()->IsModify(); }; - static ACTION_MENU* openRecentMenu; // Open Recent submenu, static to remember this menu + static FILE_HISTORY_MENU* openRecentMenu; // Open Recent submenu, static to remember this menu + FILE_HISTORY& recentFiles = Kiface().GetFileHistory(); - // Before deleting, remove the menus managed by m_fileHistory - // (the file history will be updated when adding/removing files in history - if( openRecentMenu ) - Kiface().GetFileHistory().RemoveMenu( openRecentMenu ); + // Create the menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentMenu ) + { + openRecentMenu = new FILE_HISTORY_MENU( recentFiles ); + openRecentMenu->SetTool( selTool ); + openRecentMenu->SetTitle( _( "Open Recent" ) ); + openRecentMenu->SetIcon( recent_xpm ); + } //-- File menu ------------------------------------------------------- // CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, selTool ); - openRecentMenu = new ACTION_MENU( false ); - openRecentMenu->SetTool( selTool ); - openRecentMenu->SetTitle( _( "Open Recent" ) ); - openRecentMenu->SetIcon( recent_xpm ); - - Kiface().GetFileHistory().UseMenu( openRecentMenu ); - Kiface().GetFileHistory().AddFilesToMenu(); - fileMenu->AddItem( ACTIONS::doNew, SELECTION_CONDITIONS::ShowAlways ); fileMenu->AddItem( ACTIONS::open, SELECTION_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentMenu, SELECTION_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentMenu, FILE_HISTORY::FileHistoryNotEmpty( recentFiles ) ); fileMenu->AddSeparator(); fileMenu->AddItem( ACTIONS::save, modifiedDocumentCondition ); diff --git a/pagelayout_editor/pl_editor_frame.cpp b/pagelayout_editor/pl_editor_frame.cpp index 5af58b4ec4..7acf2be88c 100644 --- a/pagelayout_editor/pl_editor_frame.cpp +++ b/pagelayout_editor/pl_editor_frame.cpp @@ -198,6 +198,15 @@ PL_EDITOR_FRAME::PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) : } +PL_EDITOR_FRAME::~PL_EDITOR_FRAME() +{ + // Since the file menu contains file history menus, we must ensure that the menu + // destructor is called before the file history objects are deleted since their destructor + // unregisters the menu from the history. + wxMenu* fileMenu = GetMenuBar()->Remove( 0 ); + delete fileMenu; +} + void PL_EDITOR_FRAME::setupTools() { // Create the manager and dispatcher & route draw panel events to the dispatcher diff --git a/pagelayout_editor/pl_editor_frame.h b/pagelayout_editor/pl_editor_frame.h index 5848b1c9de..0ee4d115ab 100644 --- a/pagelayout_editor/pl_editor_frame.h +++ b/pagelayout_editor/pl_editor_frame.h @@ -66,7 +66,7 @@ private: public: PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ); - ~PL_EDITOR_FRAME() {} + ~PL_EDITOR_FRAME(); PROPERTIES_FRAME* GetPropertiesFrame() { return m_propertiesPagelayout; } diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp index 9c6c7dcc6e..febb1c81fa 100644 --- a/pcbnew/menubar_pcb_editor.cpp +++ b/pcbnew/menubar_pcb_editor.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -58,27 +59,26 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() //-- File menu ----------------------------------------------------------- // CONDITIONAL_MENU* fileMenu = new CONDITIONAL_MENU( false, selTool ); - static ACTION_MENU* openRecentMenu; + static FILE_HISTORY_MENU* openRecentMenu; auto& disp_opt = GetDisplayOptions(); if( Kiface().IsSingle() ) // not when under a project mgr { - // Add this menu to list menu managed by m_fileHistory - // (the file history will be updated when adding/removing files in history) - if( openRecentMenu ) - Kiface().GetFileHistory().RemoveMenu( openRecentMenu ); + FILE_HISTORY& fileHistory = Kiface().GetFileHistory(); - openRecentMenu = new ACTION_MENU( false ); - openRecentMenu->SetTool( selTool ); - openRecentMenu->SetTitle( _( "Open Recent" ) ); - openRecentMenu->SetIcon( recent_xpm ); - - Kiface().GetFileHistory().UseMenu( openRecentMenu ); - Kiface().GetFileHistory().AddFilesToMenu( openRecentMenu ); + // Create the menu if it does not exist. Adding a file to/from the history + // will automatically refresh the menu. + if( !openRecentMenu ) + { + openRecentMenu = new FILE_HISTORY_MENU( fileHistory ); + openRecentMenu->SetTool( selTool ); + openRecentMenu->SetTitle( _( "Open Recent" ) ); + openRecentMenu->SetIcon( recent_xpm ); + } fileMenu->AddItem( ACTIONS::doNew, SELECTION_CONDITIONS::ShowAlways ); fileMenu->AddItem( ACTIONS::open, SELECTION_CONDITIONS::ShowAlways ); - fileMenu->AddMenu( openRecentMenu, SELECTION_CONDITIONS::ShowAlways ); + fileMenu->AddMenu( openRecentMenu, FILE_HISTORY::FileHistoryNotEmpty( fileHistory ) ); fileMenu->AddItem( PCB_ACTIONS::appendBoard, SELECTION_CONDITIONS::ShowAlways ); fileMenu->AddItem( ID_IMPORT_NON_KICAD_BOARD, diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 683f568669..a738d8567f 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -336,6 +336,11 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : PCB_EDIT_FRAME::~PCB_EDIT_FRAME() { + // Since the file menu contains file history menus, we must ensure that the menu + // destructor is called before the file history objects are deleted since their destructor + // unregisters the menu from the history. + wxMenu* fileMenu = GetMenuBar()->Remove( 0 ); + delete fileMenu; }