Full-project automatic backup system

Fixes https://gitlab.com/kicad/code/kicad/-/issues/4763
This commit is contained in:
Jon Evans 2020-07-03 09:40:31 -04:00
parent 5226d6b4df
commit b1b9cef5fb
18 changed files with 1585 additions and 231 deletions

View File

@ -414,6 +414,7 @@ set( COMMON_SRCS
settings/settings_manager.cpp
project/net_settings.cpp
project/project_archiver.cpp
project/project_file.cpp
project/project_local_settings.cpp

View File

@ -125,6 +125,13 @@ bool PANEL_COMMON_SETTINGS::TransferDataToWindow()
m_otherPDFViewer->SetValue( !Pgm().UseSystemPdfBrowser() );
m_PDFViewerPath->SetValue( Pgm().GetPdfBrowserName() );
m_cbBackupEnabled->SetValue( commonSettings->m_Backup.enabled );
m_cbBackupAutosave->SetValue( commonSettings->m_Backup.backup_on_autosave );
m_backupLimitTotalFiles->SetValue( commonSettings->m_Backup.limit_total_files );
m_backupLimitDailyFiles->SetValue( commonSettings->m_Backup.limit_daily_files );
m_backupMinInterval->SetValue( commonSettings->m_Backup.min_interval / 60 );
m_backupLimitTotalSize->SetValue( commonSettings->m_Backup.limit_total_size / ( 1024 * 1024 ) );
return true;
}
@ -153,6 +160,13 @@ bool PANEL_COMMON_SETTINGS::TransferDataFromWindow()
commonSettings->m_Input.prefer_select_to_drag = m_PreferSelectToDrag->GetValue();
commonSettings->m_Input.warp_mouse_on_move = m_warpMouseOnMove->GetValue();
commonSettings->m_Backup.enabled = m_cbBackupEnabled->GetValue();
commonSettings->m_Backup.backup_on_autosave = m_cbBackupAutosave->GetValue();
commonSettings->m_Backup.limit_total_files = m_backupLimitTotalFiles->GetValue();
commonSettings->m_Backup.limit_daily_files = m_backupLimitDailyFiles->GetValue();
commonSettings->m_Backup.min_interval = m_backupMinInterval->GetValue() * 60;
commonSettings->m_Backup.limit_total_size = m_backupLimitTotalSize->GetValue() * 1024 * 1024;
Pgm().SetEditorName( m_textEditorPath->GetValue() );
Pgm().SetPdfBrowserName( m_PDFViewerPath->GetValue() );

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Nov 6 2019)
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -189,7 +189,75 @@ PANEL_COMMON_SETTINGS_BASE::PANEL_COMMON_SETTINGS_BASE( wxWindow* parent, wxWind
sbSizer4->Add( m_NonImmediateActions, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
rightSizer->Add( sbSizer4, 1, wxEXPAND|wxALL, 5 );
rightSizer->Add( sbSizer4, 0, wxEXPAND|wxALL, 5 );
wxStaticBoxSizer* sbSizer41;
sbSizer41 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Project Backup") ), wxVERTICAL );
wxGridBagSizer* gbSizer3;
gbSizer3 = new wxGridBagSizer( 0, 0 );
gbSizer3->SetFlexibleDirection( wxBOTH );
gbSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_cbBackupEnabled = new wxCheckBox( sbSizer41->GetStaticBox(), wxID_ANY, _("Automatically backup projects"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbBackupEnabled->SetToolTip( _("Automatically create backup archives of the current project when saving files") );
gbSizer3->Add( m_cbBackupEnabled, wxGBPosition( 0, 0 ), wxGBSpan( 1, 3 ), wxALL, 5 );
m_cbBackupAutosave = new wxCheckBox( sbSizer41->GetStaticBox(), wxID_ANY, _("Create backups when auto save occurs"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbBackupAutosave->SetToolTip( _("Create backups when the auto save feature is enabled. If not checked, backups will only be created when you manually save a file.") );
gbSizer3->Add( m_cbBackupAutosave, wxGBPosition( 1, 0 ), wxGBSpan( 1, 3 ), wxALL, 5 );
m_staticText9 = new wxStaticText( sbSizer41->GetStaticBox(), wxID_ANY, _("Maximum backups to keep:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText9->Wrap( -1 );
m_staticText9->SetToolTip( _("How many backup files total to keep (set to 0 for no limit)") );
gbSizer3->Add( m_staticText9, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_backupLimitTotalFiles = new wxSpinCtrl( sbSizer41->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 1000, 0 );
gbSizer3->Add( m_backupLimitTotalFiles, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALL, 5 );
m_staticText10 = new wxStaticText( sbSizer41->GetStaticBox(), wxID_ANY, _("Maximum backups per day:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText10->Wrap( -1 );
m_staticText10->SetToolTip( _("How many backup files to keep each day (set to 0 for no limit)") );
gbSizer3->Add( m_staticText10, wxGBPosition( 3, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_backupLimitDailyFiles = new wxSpinCtrl( sbSizer41->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 1000, 0 );
gbSizer3->Add( m_backupLimitDailyFiles, wxGBPosition( 3, 1 ), wxGBSpan( 1, 1 ), wxALL, 5 );
m_staticText11 = new wxStaticText( sbSizer41->GetStaticBox(), wxID_ANY, _("Minimum time between backups:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText11->Wrap( -1 );
m_staticText11->SetToolTip( _("Number of minutes since the last backup before another will be created the next time you save (set to 0 for no minimum)") );
gbSizer3->Add( m_staticText11, wxGBPosition( 4, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_backupMinInterval = new wxSpinCtrl( sbSizer41->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 3600, 0 );
gbSizer3->Add( m_backupMinInterval, wxGBPosition( 4, 1 ), wxGBSpan( 1, 1 ), wxALL, 5 );
m_staticText15 = new wxStaticText( sbSizer41->GetStaticBox(), wxID_ANY, _("minutes"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText15->Wrap( -1 );
gbSizer3->Add( m_staticText15, wxGBPosition( 4, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_staticText16 = new wxStaticText( sbSizer41->GetStaticBox(), wxID_ANY, _("Maximum total backup size:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText16->Wrap( -1 );
m_staticText16->SetToolTip( _("If the total size of backup files grows above this limit, old backups will be deleted (set to 0 for no limit)") );
gbSizer3->Add( m_staticText16, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_backupLimitTotalSize = new wxSpinCtrl( sbSizer41->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 1000, 0 );
gbSizer3->Add( m_backupLimitTotalSize, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALL, 5 );
m_staticText17 = new wxStaticText( sbSizer41->GetStaticBox(), wxID_ANY, _("MB"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText17->Wrap( -1 );
gbSizer3->Add( m_staticText17, wxGBPosition( 5, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxALL, 5 );
sbSizer41->Add( gbSizer3, 1, wxALL|wxEXPAND, 5 );
rightSizer->Add( sbSizer41, 1, wxALL|wxEXPAND, 5 );
bPanelSizer->Add( rightSizer, 0, wxEXPAND|wxALL, 5 );

View File

@ -1701,7 +1701,7 @@
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">1</property>
<property name="proportion">0</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Editing</property>
@ -1904,6 +1904,815 @@
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Project Backup</property>
<property name="minimum_size"></property>
<property name="name">sbSizer41</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxGridBagSizer" expanded="1">
<property name="empty_cell_size"></property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols"></property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">gbSizer3</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="vgap">0</property>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">3</property>
<property name="column">0</property>
<property name="flag">wxALL</property>
<property name="row">0</property>
<property name="rowspan">1</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Automatically backup projects</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_cbBackupEnabled</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Automatically create backup archives of the current project when saving files</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">3</property>
<property name="column">0</property>
<property name="flag">wxALL</property>
<property name="row">1</property>
<property name="rowspan">1</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Create backups when auto save occurs</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_cbBackupAutosave</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Create backups when the auto save feature is enabled. If not checked, backups will only be created when you manually save a file.</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="row">2</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Maximum backups to keep:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText9</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">How many backup files total to keep (set to 0 for no limit)</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxALL</property>
<property name="row">2</property>
<property name="rowspan">1</property>
<object class="wxSpinCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="initial">0</property>
<property name="max">1000</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min">0</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_backupLimitTotalFiles</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxSP_ARROW_KEYS</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="row">3</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Maximum backups per day:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText10</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">How many backup files to keep each day (set to 0 for no limit)</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxALL</property>
<property name="row">3</property>
<property name="rowspan">1</property>
<object class="wxSpinCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="initial">0</property>
<property name="max">1000</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min">0</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_backupLimitDailyFiles</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxSP_ARROW_KEYS</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="row">4</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Minimum time between backups:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText11</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Number of minutes since the last backup before another will be created the next time you save (set to 0 for no minimum)</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxALL</property>
<property name="row">4</property>
<property name="rowspan">1</property>
<object class="wxSpinCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="initial">0</property>
<property name="max">3600</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min">0</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_backupMinInterval</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxSP_ARROW_KEYS</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">2</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="row">4</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">minutes</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText15</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="row">5</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Maximum total backup size:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText16</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">If the total size of backup files grows above this limit, old backups will be deleted (set to 0 for no limit)</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxALL</property>
<property name="row">5</property>
<property name="rowspan">1</property>
<object class="wxSpinCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="initial">0</property>
<property name="max">1000</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min">0</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_backupLimitTotalSize</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxSP_ARROW_KEYS</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="gbsizeritem" expanded="1">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">2</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="row">5</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">MB</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText17</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Nov 6 2019)
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -65,6 +65,18 @@ class PANEL_COMMON_SETTINGS_BASE : public wxPanel
wxCheckBox* m_PreferSelectToDrag;
wxCheckBox* m_warpMouseOnMove;
wxCheckBox* m_NonImmediateActions;
wxCheckBox* m_cbBackupEnabled;
wxCheckBox* m_cbBackupAutosave;
wxStaticText* m_staticText9;
wxSpinCtrl* m_backupLimitTotalFiles;
wxStaticText* m_staticText10;
wxSpinCtrl* m_backupLimitDailyFiles;
wxStaticText* m_staticText11;
wxSpinCtrl* m_backupMinInterval;
wxStaticText* m_staticText15;
wxStaticText* m_staticText16;
wxSpinCtrl* m_backupLimitTotalSize;
wxStaticText* m_staticText17;
// Virtual event handlers, overide them in your derived class
virtual void OnTextEditorClick( wxCommandEvent& event ) { event.Skip(); }

View File

@ -0,0 +1,282 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <wx/dir.h>
#include <wx/filedlg.h>
#include <wx/fs_zip.h>
#include <wx/uri.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <macros.h>
#include <project/project_archiver.h>
#include <reporter.h>
#include <wildcards_and_files_ext.h>
#define ZipFileExtension wxT( "zip" )
PROJECT_ARCHIVER::PROJECT_ARCHIVER()
{
}
// Unarchive Files code comes from wxWidgets sample/archive/archive.cpp
static bool CopyStreamData( wxInputStream& inputStream, wxOutputStream& outputStream,
wxFileOffset size )
{
wxChar buf[128 * 1024];
int readSize = 128 * 1024;
wxFileOffset copiedData = 0;
for (;;)
{
if (size != -1 && copiedData + readSize > size)
readSize = size - copiedData;
inputStream.Read( buf, readSize );
size_t actuallyRead = inputStream.LastRead();
outputStream.Write( buf, actuallyRead );
if( outputStream.LastWrite() != actuallyRead )
{
wxLogError( "Failed to output data" );
//return false;
}
if (size == -1)
{
if (inputStream.Eof())
break;
}
else
{
copiedData += actuallyRead;
if( copiedData >= size )
break;
}
}
return true;
}
bool PROJECT_ARCHIVER::Unarchive( const wxString& aSrcFile, const wxString& aDestDir,
REPORTER& aReporter )
{
wxFileInputStream stream( aSrcFile );
if (!stream.IsOk())
{
aReporter.Report( _( "Could not open archive file\n" ), RPT_SEVERITY_ERROR );
return false;
}
const wxArchiveClassFactory* archiveClassFactory =
wxArchiveClassFactory::Find( aSrcFile, wxSTREAM_FILEEXT );
if( !archiveClassFactory )
{
aReporter.Report( _( "Invalid archive file format\n" ), RPT_SEVERITY_ERROR );
return false;
}
wxScopedPtr<wxArchiveInputStream> archiveStream( archiveClassFactory->NewStream( stream ) );
wxString fileStatus;
for( wxArchiveEntry* entry = archiveStream->GetNextEntry(); entry;
entry = archiveStream->GetNextEntry() )
{
fileStatus.Printf( _( "Extracting file \"%s\"\n" ), entry->GetName() );
aReporter.Report( fileStatus, RPT_SEVERITY_INFO );
wxString fullname = aDestDir + entry->GetName();
// Ensure the target directory exists and created it if not
wxString t_path = wxPathOnly( fullname );
if( !wxDirExists( t_path ) )
{
// To create t_path, we need to create all subdirs from unzipDir
// to t_path.
wxFileName pathToCreate;
pathToCreate.AssignDir( t_path );
pathToCreate.MakeRelativeTo( aDestDir );
// Create the list of subdirs candidates
wxArrayString subDirs;
subDirs = pathToCreate.GetDirs();
pathToCreate.AssignDir( aDestDir );
for( size_t ii = 0; ii < subDirs.Count(); ii++ )
{
pathToCreate.AppendDir( subDirs[ii] );
wxString currPath = pathToCreate.GetPath();
if( !wxDirExists( currPath ) )
wxMkdir( currPath );
}
}
wxTempFileOutputStream outputFileStream( fullname );
if( CopyStreamData( *archiveStream, outputFileStream, entry->GetSize() ) )
outputFileStream.Commit();
else
aReporter.Report( _( "Error extracting file!\n" ), RPT_SEVERITY_ERROR );
}
aReporter.Report( wxT( "Extracted project\n" ), RPT_SEVERITY_INFO );
return true;
}
bool PROJECT_ARCHIVER::Archive( const wxString& aSrcDir, const wxString& aDestFile,
REPORTER& aReporter, bool aVerbose )
{
// List of file extensions to save.
static const wxChar* extensionList[] = {
wxT( "*.pro" ),
wxT( "*.kicad_pro" ),
wxT( "*.kicad_prl" ),
wxT( "*.sch" ), // Legacy schematic files
wxT( "*.kicad_sch" ), // Schematic files
wxT( "*.lib" ), wxT( "*.dcm" ), // Legacy schematic library files
wxT( "*.kicad_sym" ), // schematic library files
wxT( "*.cmp" ),
wxT( "*.brd" ), wxT( "*.kicad_pcb" ), // Brd files
wxT( "*.mod" ), wxT( "*.kicad_mod" ), // fp files
wxT( "*.gb?" ), wxT( "*.gbrjob" ), // Gerber files
wxT( "*.gko" ), wxT( "*.gm1" ),
wxT( "*.gm2" ), wxT( "*.g?" ),
wxT( "*.gp1" ), wxT( "*.gp2" ),
wxT( "*.gpb" ), wxT( "*.gpt" ),
wxT( "*.gt?" ),
wxT( "*.pos" ), wxT( "*.drl" ), wxT( "*.nc" ), wxT( "*.xnc" ), // Fab files
wxT( "*.d356" ), wxT( "*.rpt" ),
wxT( "*.stp" ), wxT( "*.step" ), // 3d files
wxT( "*.wrl" ),
wxT( "*.net" ), wxT( "*.py" ),
wxT( "*.pdf" ), wxT( "*.txt" ), wxT( "*.kicad_wks" ),
wxT( "fp-lib-table" ), wxT( "sym-lib-table" )
};
bool success = true;
wxString msg;
wxString oldCwd = wxGetCwd();
wxSetWorkingDirectory( aSrcDir );
wxFFileOutputStream ostream( aDestFile );
if( !ostream.IsOk() ) // issue to create the file. Perhaps not writable dir
{
msg.Printf( _( "Unable to create archive file \"%s\"\n" ), aDestFile );
aReporter.Report( msg, RPT_SEVERITY_ERROR );
return false;
}
wxZipOutputStream zipstream( ostream, -1, wxConvUTF8 );
// Build list of filenames to put in zip archive
wxString currFilename;
wxArrayString files;
for( unsigned ii = 0; ii < arrayDim( extensionList ); ii++ )
wxDir::GetAllFiles( aSrcDir, &files, extensionList[ii] );
files.Sort();
unsigned long uncompressedBytes = 0;
for( unsigned ii = 0; ii < files.GetCount(); ii++ )
{
wxFileSystem fsfile;
wxFileName curr_fn( files[ii] );
curr_fn.MakeRelativeTo( aSrcDir );
currFilename = curr_fn.GetFullPath();
// Read input file and add it to the zip file:
wxFSFile* infile = fsfile.OpenFile( wxFileSystem::FileNameToURL( curr_fn ) );
if( infile )
{
zipstream.PutNextEntry( currFilename, infile->GetModificationTime() );
infile->GetStream()->Read( zipstream );
zipstream.CloseEntry();
uncompressedBytes += infile->GetStream()->GetSize();
if( aVerbose )
{
msg.Printf( _( "Archive file \"%s\"\n" ), currFilename );
aReporter.Report( msg, RPT_SEVERITY_INFO );
}
delete infile;
}
else
{
if( aVerbose )
{
msg.Printf( _( "Archive file \"%s\": Failed!\n" ), currFilename );
aReporter.Report( msg, RPT_SEVERITY_ERROR );
}
success = false;
}
}
auto reportSize =
[]( unsigned long aSize ) -> wxString
{
constexpr float KB = 1024.0;
constexpr float MB = KB * 1024.0;
if( aSize >= MB )
return wxString::Format( wxT( "%0.2f MB" ), aSize / MB );
else if( aSize >= KB )
return wxString::Format( wxT( "%0.2f KB" ), aSize / KB );
else
return wxString::Format( wxT( "%lu bytes" ), aSize );
};
size_t zipBytesCnt = ostream.GetSize();
if( zipstream.Close() )
{
msg.Printf( _( "Zip archive \"%s\" created (%s uncompressed, %s compressed)\n" ), aDestFile,
reportSize( uncompressedBytes ), reportSize( zipBytesCnt ) );
aReporter.Report( msg, RPT_SEVERITY_INFO );
}
else
{
msg.Printf( wxT( "Unable to create archive \"%s\"\n" ), aDestFile );
aReporter.Report( msg, RPT_SEVERITY_ERROR );
success = false;
}
wxSetWorkingDirectory( oldCwd );
return success;
}

View File

@ -59,6 +59,23 @@ COMMON_SETTINGS::COMMON_SETTINGS() :
m_params.emplace_back( new PARAM<bool>( "appearance.use_icons_in_menus",
&m_Appearance.use_icons_in_menus, defaultUseIconsInMenus ) );
m_params.emplace_back( new PARAM<bool>( "auto_backup.enabled", &m_Backup.enabled, true ) );
m_params.emplace_back( new PARAM<bool>(
"auto_backup.backup_on_autosave", &m_Backup.backup_on_autosave, false ) );
m_params.emplace_back(
new PARAM<int>( "auto_backup.limit_total_files", &m_Backup.limit_total_files, 25 ) );
m_params.emplace_back( new PARAM<unsigned long long>(
"auto_backup.limit_total_size", &m_Backup.limit_total_size, 104857600 ) );
m_params.emplace_back(
new PARAM<int>( "auto_backup.limit_daily_files", &m_Backup.limit_daily_files, 5 ) );
m_params.emplace_back(
new PARAM<int>( "auto_backup.min_interval", &m_Backup.min_interval, 300 ) );
m_params.emplace_back( new PARAM<bool>( "environment.show_warning_dialog",
&m_Env.show_warning_dialog, false ) );

View File

@ -30,6 +30,7 @@
#include <gestfich.h>
#include <macros.h>
#include <project.h>
#include <project/project_archiver.h>
#include <project/project_file.h>
#include <project/project_local_settings.h>
#include <settings/app_settings.h>
@ -823,3 +824,218 @@ bool SETTINGS_MANAGER::unloadProjectFile( PROJECT* aProject, bool aSave )
return true;
}
wxString SETTINGS_MANAGER::GetProjectBackupsPath() const
{
return Prj().GetProjectPath() + Prj().GetProjectName() + PROJECT_BACKUPS_DIR_SUFFIX;
}
wxString SETTINGS_MANAGER::backupDateTimeFormat = wxT( "%Y-%m-%d_%H%M%S" );
bool SETTINGS_MANAGER::BackupProject( REPORTER& aReporter ) const
{
wxDateTime timestamp = wxDateTime::Now();
wxString fileName = wxString::Format( wxT( "%s-%s" ), Prj().GetProjectName(),
timestamp.Format( backupDateTimeFormat ) );
wxFileName target;
target.SetPath( GetProjectBackupsPath() );
target.SetName( fileName );
target.SetExt( ArchiveFileExtension );
wxDir dir( target.GetPath() );
if( !target.DirExists() && !wxMkdir( target.GetPath() ) )
{
wxLogTrace( traceSettings, "Could not create project backup path %s", target.GetPath() );
return false;
}
if( !target.IsDirWritable() )
{
wxLogTrace( traceSettings, "Backup directory %s is not writeable", target.GetPath() );
return false;
}
wxLogTrace( traceSettings, "Backing up project to %s", target.GetPath() );
PROJECT_ARCHIVER archiver;
return archiver.Archive( Prj().GetProjectPath(), target.GetFullPath(), aReporter );
}
class VECTOR_INSERT_TRAVERSER : public wxDirTraverser
{
public:
VECTOR_INSERT_TRAVERSER( std::vector<wxString>& aVec,
std::function<bool( const wxString& )> aCond ) :
m_files( aVec ),
m_condition( aCond )
{
}
wxDirTraverseResult OnFile( const wxString& aFile ) override
{
if( m_condition( aFile ) )
m_files.emplace_back( aFile );
return wxDIR_CONTINUE;
}
wxDirTraverseResult OnDir( const wxString& aDirName ) override
{
return wxDIR_CONTINUE;
}
private:
std::vector<wxString>& m_files;
std::function<bool( const wxString& )> m_condition;
};
bool SETTINGS_MANAGER::TriggerBackupIfNeeded( REPORTER& aReporter ) const
{
COMMON_SETTINGS::AUTO_BACKUP settings = GetCommonSettings()->m_Backup;
if( !settings.enabled )
return true;
wxString prefix = Prj().GetProjectName() + '-';
auto modTime =
[&prefix]( const wxString& aFile )
{
wxDateTime dt;
wxString fn( wxFileName( aFile ).GetName() );
fn.Replace( prefix, "" );
dt.ParseFormat( fn, backupDateTimeFormat );
return dt;
};
// Project not saved yet
if( Prj().GetProjectPath().empty() )
return true;
wxString backupPath = GetProjectBackupsPath();
if( !wxDirExists( backupPath ) )
{
wxLogTrace( traceSettings, "Backup path %s doesn't exist, creating it", backupPath );
if( !wxMkdir( backupPath ) )
{
wxLogTrace( traceSettings, "Could not create backups path! Skipping backup" );
return false;
}
}
wxDir dir( backupPath );
if( !dir.IsOpened() )
{
wxLogTrace( traceSettings, "Could not open project backups path %s", dir.GetName() );
return false;
}
std::vector<wxString> files;
VECTOR_INSERT_TRAVERSER traverser( files,
[&modTime]( const wxString& aFile )
{
return modTime( aFile ).IsValid();
} );
dir.Traverse( traverser, wxT( "*.zip" ) );
// Sort newest-first
std::sort( files.begin(), files.end(),
[&]( const wxString& aFirst, const wxString& aSecond ) -> bool
{
wxDateTime first = modTime( aFirst );
wxDateTime second = modTime( aSecond );
return first.GetTicks() > second.GetTicks();
} );
// Do we even need to back up?
if( !files.empty() )
{
wxDateTime lastTime = modTime( files[0] );
if( lastTime.IsValid() )
{
wxTimeSpan delta = wxDateTime::Now() - modTime( files[0] );
if( delta.IsShorterThan( wxTimeSpan::Seconds( settings.min_interval ) ) )
return true;
}
}
// Now that we know a backup is needed, apply the retention policy
// Step 1: if we're over the total file limit, remove the oldest
if( !files.empty() && settings.limit_total_files > 0 )
{
while( files.size() > static_cast<size_t>( settings.limit_total_files ) )
{
wxRemoveFile( files.back() );
files.pop_back();
}
}
// Step 2: Stay under the total size limit
if( settings.limit_total_size > 0 )
{
wxULongLong totalSize = 0;
for( const wxString& file : files )
totalSize += wxFileName::GetSize( file );
while( !files.empty() && totalSize > static_cast<wxULongLong>( settings.limit_total_size ) )
{
totalSize -= wxFileName::GetSize( files.back() );
wxRemoveFile( files.back() );
files.pop_back();
}
}
// Step 3: Stay under the daily limit
if( settings.limit_daily_files > 0 && files.size() > 1 )
{
wxDateTime day = modTime( files[0] );
int num = 1;
wxASSERT( day.IsValid() );
std::vector<wxString> filesToDelete;
for( size_t i = 1; i < files.size(); i++ )
{
wxDateTime dt = modTime( files[i] );
if( dt.IsSameDate( day ) )
{
num++;
if( num > settings.limit_daily_files )
filesToDelete.emplace_back( files[i] );
}
else
{
day = dt;
num = 1;
}
}
for( const wxString& file : filesToDelete )
wxRemoveFile( file );
}
return BackupProject( aReporter );
}

View File

@ -132,6 +132,8 @@ const std::string GerberJobFileExtension( "gbrjob" );
const std::string HtmlFileExtension( "html" );
const std::string EquFileExtension( "equ" );
const std::string ArchiveFileExtension( "zip" );
const std::string LegacyPcbFileExtension( "brd" );
const std::string KiCadPcbFileExtension( "kicad_pcb" );
const std::string PageLayoutDescrFileExtension( "kicad_wks" );

View File

@ -44,6 +44,7 @@
#include <schematic.h>
#include <wildcards_and_files_ext.h>
#include <project_rescue.h>
#include <reporter.h>
#include <eeschema_config.h>
#include <eeschema_settings.h>
#include <sch_legacy_plugin.h>
@ -56,6 +57,7 @@
#include <tool/actions.h>
#include <tools/sch_editor_control.h>
#include <project/project_file.h>
#include <settings/common_settings.h>
#include <settings/settings_manager.h>
#include <netlist.h>
#include <widgets/infobar.h>
@ -720,6 +722,11 @@ bool SCH_EDIT_FRAME::SaveProject()
Pgm().GetSettingsManager().SaveProject();
WX_STRING_REPORTER backupReporter( &msg );
if( !GetSettingsManager()->TriggerBackupIfNeeded( backupReporter ) )
SetStatusText( msg, 0 );
UpdateTitle();
return success;
@ -765,8 +772,13 @@ bool SCH_EDIT_FRAME::doAutoSave()
}
if( autoSaveOk )
{
m_autoSaveState = false;
if( GetSettingsManager()->GetCommonSettings()->m_Backup.backup_on_autosave )
GetSettingsManager()->TriggerBackupIfNeeded( NULL_REPORTER::GetInstance() );
}
return autoSaveOk;
}

View File

@ -298,10 +298,6 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ):
SCH_EDIT_FRAME::~SCH_EDIT_FRAME()
{
GetSettingsManager()->SaveProject();
Schematic().SetTemplateFieldNames( nullptr );
Schematic().Reset();
// Shutdown all running tools
if( m_toolManager )
m_toolManager->ShutdownAllTools();
@ -563,6 +559,10 @@ void SCH_EDIT_FRAME::OnCloseWindow( wxCloseEvent& aEvent )
// all sub sheets are deleted, only the main sheet is usable
GetCurrentSheet().clear();
GetSettingsManager()->SaveProject();
Schematic().SetTemplateFieldNames( nullptr );
Schematic().Reset();
Destroy();
}

View File

@ -0,0 +1,61 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_PROJECT_ARCHIVER_H
#define KICAD_PROJECT_ARCHIVER_H
#include <wx/string.h>
class PROJECT;
class REPORTER;
class SETTINGS_MANAGER;
class PROJECT_ARCHIVER
{
public:
PROJECT_ARCHIVER();
~PROJECT_ARCHIVER() = default;
/**
* Creates an archive of the project
* @param aSrcFile is the full path to the project to be archived
* @param aDestFile is the full path to the zip file to be created
* @param aReporter is used to report status
* @param aVerbose controls the verbosity of reported status messages
* @return true if the archive was created successfully
*/
bool Archive( const wxString& aSrcDir, const wxString& aDestFile, REPORTER& aReporter,
bool aVerbose = true );
/**
* Extracts an archive of the current project over existing files
* Warning: this will overwrite files in the project directory. Use with care. The caller is
* responsible for doing any reloading of state after taking this action.
* @param aSrcFile is the full path to the archive to extract
* @param aDestDir is the target directory to unarchive to
* @param aReporter is used to report status
* @return true if the archive was created successfully
*/
bool Unarchive( const wxString& aSrcFile, const wxString& aDestDir, REPORTER& aReporter );
};
#endif // KICAD_PROJECT_ARCHIVER_H

View File

@ -34,6 +34,18 @@ public:
bool use_icons_in_menus;
};
struct AUTO_BACKUP
{
bool enabled; ///< Automatically back up the project when files are saved
bool backup_on_autosave; ///< Trigger a backup on autosave
int limit_total_files; ///< Maximum number of backup archives to retain
int limit_daily_files; ///< Maximum files to keep per day, 0 for unlimited
int min_interval; ///< Minimum time, in seconds, between subsequent backups
/// Maximum total size of backups (bytes), 0 for unlimited
unsigned long long limit_total_size;
};
struct ENVIRONMENT
{
bool show_warning_dialog;
@ -93,6 +105,8 @@ private:
public:
APPEARANCE m_Appearance;
AUTO_BACKUP m_Backup;
ENVIRONMENT m_Env;
INPUT m_Input;

View File

@ -28,6 +28,7 @@ class COLOR_SETTINGS;
class COMMON_SETTINGS;
class PROJECT;
class PROJECT_FILE;
class REPORTER;
class SETTINGS_MANAGER
@ -136,7 +137,7 @@ public:
* Retrieves the common settings shared by all applications
* @return a pointer to a loaded COMMON_SETTINGS
*/
COMMON_SETTINGS* GetCommonSettings() { return m_common_settings; }
COMMON_SETTINGS* GetCommonSettings() const { return m_common_settings; }
/**
* Returns the path a given settings file should be loaded from / stored to.
@ -217,6 +218,25 @@ public:
*/
bool SaveProject( const wxString& aFullPath = wxEmptyString );
/**
* @return the full path to where project backups should be stored
*/
wxString GetProjectBackupsPath() const;
/**
* Creates a backup archive of the current project
* @param aReporter is used for progress reporting
* @return true if everything succeeded
*/
bool BackupProject( REPORTER& aReporter ) const;
/**
* Calls BackupProject if a new backup is needed according to the current backup policy.
* @param aReporter is used for progress reporting
* @return if everything succeeded
*/
bool TriggerBackupIfNeeded( REPORTER& aReporter ) const;
/**
* Checks if a given path is probably a valid KiCad configuration directory.
* Actually it just checks if a file called "kicad_common" exists, because that's probably
@ -334,8 +354,7 @@ private:
/// Loaded project files, mapped according to project full name
std::map<wxString, PROJECT_FILE*> m_project_files;
/// A list of project settings objects for each loaded project
// std::map<wxString, std::vector<PROJECT_SETTINGS*>> m_project_settings;
static wxString backupDateTimeFormat;
};
#endif

View File

@ -125,6 +125,8 @@ extern const std::string GerberJobFileExtension;
extern const std::string HtmlFileExtension;
extern const std::string EquFileExtension;
extern const std::string ArchiveFileExtension;
extern const std::string LegacyPcbFileExtension;
extern const std::string KiCadPcbFileExtension;
#define PcbFileExtension KiCadPcbFileExtension // symlink choice

View File

@ -26,24 +26,17 @@
* @file kicad/files-io.cpp
*/
#include <wx/dir.h>
#include <wx/fs_zip.h>
#include <wx/uri.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <confirm.h>
#include <kiway.h>
#include "pgm_kicad.h"
#include "wildcards_and_files_ext.h"
#include <pgm_kicad.h>
#include <project/project_archiver.h>
#include <reporter.h>
#include <settings/settings_manager.h>
#include <wildcards_and_files_ext.h>
#include "kicad_manager_frame.h"
#define ZipFileExtension wxT( "zip" )
void KICAD_MANAGER_FRAME::OnFileHistory( wxCommandEvent& event )
{
wxFileName projFileName = GetFileFromHistory( event.GetId(), _( "KiCad project file" ) );
@ -60,50 +53,11 @@ void KICAD_MANAGER_FRAME::OnClearFileHistory( wxCommandEvent& aEvent )
}
// Unarchive Files code comes from wxWidgets sample/archive/archive.cpp
static bool CopyStreamData( wxInputStream& inputStream, wxOutputStream& outputStream, wxFileOffset size )
{
wxChar buf[128 * 1024];
int readSize = 128 * 1024;
wxFileOffset copiedData = 0;
for (;;)
{
if (size != -1 && copiedData + readSize > size)
readSize = size - copiedData;
inputStream.Read( buf, readSize );
size_t actuallyRead = inputStream.LastRead();
outputStream.Write( buf, actuallyRead );
if( outputStream.LastWrite() != actuallyRead )
{
wxLogError( "Failed to output data" );
//return false;
}
if (size == -1)
{
if (inputStream.Eof())
break;
}
else
{
copiedData += actuallyRead;
if( copiedData >= size )
break;
}
}
return true;
}
void KICAD_MANAGER_FRAME::OnUnarchiveFiles( wxCommandEvent& event )
{
wxFileName fn = GetProjectFileName();
wxFileName fn = Prj().GetProjectFullName();
fn.SetExt( ZipFileExtension );
fn.SetExt( ArchiveFileExtension );
wxFileDialog zipfiledlg( this, _( "Unzip Project" ), fn.GetPath(),
fn.GetFullName(), ZipFileWildcard(),
@ -124,105 +78,37 @@ void KICAD_MANAGER_FRAME::OnUnarchiveFiles( wxCommandEvent& event )
msg.Printf( _( "Unzipping project in \"%s\"\n" ), unzipDir );
PrintMsg( msg );
wxString archiveFileName = zipfiledlg.GetPath();
wxFileInputStream fileInputStream( archiveFileName );
if (!fileInputStream.IsOk())
return;
const wxArchiveClassFactory* archiveClassFactory =
wxArchiveClassFactory::Find( archiveFileName, wxSTREAM_FILEEXT );
if( archiveClassFactory )
if( unzipDir == Prj().GetProjectPath() )
{
wxScopedPtr<wxArchiveInputStream> archiveStream( archiveClassFactory->NewStream(fileInputStream) );
for (wxArchiveEntry* entry = archiveStream->GetNextEntry(); entry;
entry = archiveStream->GetNextEntry())
{
PrintMsg( wxString::Format( _( "Extract file \"%s\"" ), entry->GetName() ) );
wxString fullname = unzipDir + entry->GetName();
// Ensure the target directory exists and created it if not
wxString t_path = wxPathOnly( fullname );
if( !wxDirExists( t_path ) )
{
// To create t_path, we need to create all subdirs from unzipDir
// to t_path.
wxFileName pathToCreate;
pathToCreate.AssignDir( t_path );
pathToCreate.MakeRelativeTo( unzipDir );
// Create the list of subdirs candidates
wxArrayString subDirs;
subDirs = pathToCreate.GetDirs();
pathToCreate.AssignDir( unzipDir );
for( size_t ii = 0; ii < subDirs.Count(); ii++ )
{
pathToCreate.AppendDir( subDirs[ii] );
wxString currPath = pathToCreate.GetPath();
if( !wxDirExists( currPath ) )
wxMkdir( currPath );
}
}
wxTempFileOutputStream outputFileStream( fullname );
if( CopyStreamData( *archiveStream, outputFileStream, entry->GetSize() ) )
{
outputFileStream.Commit();
PrintMsg( _( " OK\n" ) );
}
else
PrintMsg( _( " *ERROR*\n" ) );
}
if( !Kiway().PlayersClose( false ) )
return;
}
PrintMsg( wxT( "** end **\n" ) );
WX_TEXT_CTRL_REPORTER reporter( m_messagesBox );
PROJECT_ARCHIVER archiver;
archiver.Unarchive( zipfiledlg.GetPath(), unzipDir, reporter );
if( unzipDir == Prj().GetProjectPath() )
{
wxString prjPath = Prj().GetProjectFullName();
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
mgr.UnloadProject( &Prj(), false );
mgr.LoadProject( prjPath );
RefreshProjectTree();
}
}
void KICAD_MANAGER_FRAME::OnArchiveFiles( wxCommandEvent& event )
{
// List of file extensions to save.
static const wxChar* extensionList[] = {
wxT( "*.pro" ),
wxT( "*.kicad_pro" ),
wxT( "*.kicad_prl" ),
wxT( "*.sch" ), // Legacy schematic files
wxT( "*.kicad_sch" ), // Schematic files
wxT( "*.lib" ), wxT( "*.dcm" ), // Legacy schematic library files
wxT( "*.kicad_sym" ), // schematic library files
wxT( "*.cmp" ),
wxT( "*.brd" ), wxT( "*.kicad_pcb" ), // Brd files
wxT( "*.mod" ), wxT( "*.kicad_mod" ), // fp files
wxT( "*.gb?" ), wxT( "*.gbrjob" ), // Gerber files
wxT( "*.gko" ), wxT( "*.gm1" ),
wxT( "*.gm2" ), wxT( "*.g?" ),
wxT( "*.gp1" ), wxT( "*.gp2" ),
wxT( "*.gpb" ), wxT( "*.gpt" ),
wxT( "*.gt?" ),
wxT( "*.pos" ), wxT( "*.drl" ), wxT( "*.nc" ), wxT( "*.xnc" ), // Fab files
wxT( "*.d356" ), wxT( "*.rpt" ),
wxT( "*.stp" ), wxT( "*.step" ), // 3d files
wxT( "*.wrl" ),
wxT( "*.net" ), wxT( "*.py" ),
wxT( "*.pdf" ), wxT( "*.txt" ), wxT( "*.kicad_wks" ),
wxT( "fp-lib-table" ), wxT( "sym-lib-table" )
};
wxString msg;
wxFileName fileName = GetProjectFileName();
wxString oldCwd = wxGetCwd();
fileName.SetExt( wxT( "zip" ) );
fileName.SetExt( ArchiveFileExtension );
wxFileDialog dlg( this, _( "Archive Project Files" ),
fileName.GetPath(), fileName.GetFullName(),
@ -231,7 +117,7 @@ void KICAD_MANAGER_FRAME::OnArchiveFiles( wxCommandEvent& event )
if( dlg.ShowModal() == wxID_CANCEL )
return;
wxFileName zip = dlg.GetPath();
wxFileName zipFile = dlg.GetPath();
wxString currdirname = fileName.GetPathWithSep();
wxDir dir( currdirname );
@ -239,80 +125,9 @@ void KICAD_MANAGER_FRAME::OnArchiveFiles( wxCommandEvent& event )
if( !dir.IsOpened() ) // wxWidgets display a error message on issue.
return;
wxSetWorkingDirectory( currdirname );
WX_TEXT_CTRL_REPORTER reporter( m_messagesBox );
// Prepare the zip file
wxString zipfilename = zip.GetFullPath();
PROJECT_ARCHIVER archiver;
wxFFileOutputStream ostream( zipfilename );
if( !ostream.IsOk() ) // issue to create the file. Perhaps not writable dir
{
wxMessageBox( wxString::Format( _( "Unable to create zip archive file \"%s\"" ),
zipfilename ) );
return;
}
wxZipOutputStream zipstream( ostream );
// Build list of filenames to put in zip archive
wxString currFilename;
wxArrayString files;
for( unsigned ii = 0; ii < arrayDim( extensionList ); ii++ )
wxDir::GetAllFiles( currdirname, &files, extensionList[ii] );
files.Sort();
int zipBytesCnt = 0;
for( unsigned ii = 0; ii < files.GetCount(); ii++ )
{
wxFileSystem fsfile;
wxFileName curr_fn( files[ii] );
curr_fn.MakeRelativeTo( currdirname );
currFilename = curr_fn.GetFullPath();
msg.Printf( _( "Archive file \"%s\"" ), currFilename );
PrintMsg( msg );
// Read input file and add it to the zip file:
wxFSFile* infile = fsfile.OpenFile( currFilename );
if( infile )
{
zipstream.PutNextEntry( currFilename, infile->GetModificationTime() );
infile->GetStream()->Read( zipstream );
zipstream.CloseEntry();
int zippedsize = zipstream.GetSize() - zipBytesCnt;
zipBytesCnt = zipstream.GetSize();
PrintMsg( wxT(" ") );
msg.Printf( _( "(%lu bytes, compressed %d bytes)\n" ),
(unsigned long)infile->GetStream()->GetSize(), zippedsize );
PrintMsg( msg );
delete infile;
}
else
PrintMsg( _( " >>Error\n" ) );
}
zipBytesCnt = ostream.GetSize();
if( zipstream.Close() )
{
msg.Printf( _( "\nZip archive \"%s\" created (%d bytes)" ),
zipfilename, zipBytesCnt );
PrintMsg( msg );
PrintMsg( wxT( "\n** end **\n" ) );
}
else
{
msg.Printf( wxT( "Unable to create archive \"%s\", abort\n" ),
zipfilename );
PrintMsg( msg );
}
wxSetWorkingDirectory( oldCwd );
archiver.Archive( currdirname, zipFile.GetFullPath(), reporter );
}

View File

@ -36,6 +36,7 @@
#include <kiway_player.h>
#include <launch_ext.h>
#include <panel_hotkeys_editor.h>
#include <reporter.h>
#include <settings/common_settings.h>
#include <settings/settings_manager.h>
#include <tool/action_toolbar.h>
@ -335,8 +336,11 @@ void KICAD_MANAGER_FRAME::LoadProject( const wxFileName& aProjectFileName )
// Save the project file for the currently loaded project.
if( m_active_project )
{
Pgm().GetSettingsManager().SaveProject();
Pgm().GetSettingsManager().UnloadProject( &Prj() );
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
mgr.TriggerBackupIfNeeded( NULL_REPORTER::GetInstance() );
mgr.SaveProject();
mgr.UnloadProject( &Prj() );
}
m_active_project = true;
@ -541,9 +545,6 @@ void KICAD_MANAGER_FRAME::InstallPreferences( PAGED_DIALOG* aParent,
}
void KICAD_MANAGER_FRAME::PrintPrjInfo()
{
wxString msg = wxString::Format( _( "Project name:\n%s\n" ),

View File

@ -51,6 +51,7 @@
#include <ratsnest/ratsnest_data.h>
#include <wx/wupdlock.h>
#include <settings/common_settings.h>
#include <settings/settings_manager.h>
#include <project/project_file.h>
#include <project/project_local_settings.h>
@ -782,6 +783,11 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool addToHistory,
return false;
}
WX_STRING_REPORTER backupReporter( &upperTxt );
if( GetSettingsManager()->TriggerBackupIfNeeded( backupReporter ) )
upperTxt.clear();
GetBoard()->SetFileName( pcbFileName.GetFullPath() );
UpdateTitle();
@ -797,7 +803,6 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool addToHistory,
if( autoSaveFileName.FileExists() )
wxRemoveFile( autoSaveFileName.GetFullPath() );
lowerTxt.Printf( _( "Wrote board file: \"%s\"" ), pcbFileName.GetFullPath() );
AppendMsgPanel( upperTxt, lowerTxt, CYAN );
@ -898,6 +903,10 @@ bool PCB_EDIT_FRAME::doAutoSave()
GetBoard()->SetFileName( tmpFileName.GetFullPath() );
UpdateTitle();
m_autoSaveState = false;
if( GetSettingsManager()->GetCommonSettings()->m_Backup.backup_on_autosave )
GetSettingsManager()->TriggerBackupIfNeeded( NULL_REPORTER::GetInstance() );
return true;
}