summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/check_version.cpp22
-rw-r--r--ui/check_version.h4
-rw-r--r--ui/column_attr.h12
-rw-r--r--ui/custom_grid.cpp61
-rw-r--r--ui/custom_grid.h4
-rw-r--r--ui/folder_pair.h127
-rw-r--r--ui/gui_generated.cpp77
-rw-r--r--ui/gui_generated.h16
-rw-r--r--ui/main_dlg.cpp632
-rw-r--r--ui/main_dlg.h60
-rw-r--r--ui/msg_popup.cpp2
-rw-r--r--ui/progress_indicator.cpp20
-rw-r--r--ui/small_dlgs.cpp58
-rw-r--r--ui/switch_to_gui.h4
-rw-r--r--ui/sync_cfg.cpp6
-rw-r--r--ui/tree_view.cpp143
-rw-r--r--ui/tree_view.h35
17 files changed, 720 insertions, 563 deletions
diff --git a/ui/check_version.cpp b/ui/check_version.cpp
index f01239da..b736b361 100644
--- a/ui/check_version.cpp
+++ b/ui/check_version.cpp
@@ -5,22 +5,25 @@
// **************************************************************************
#include "check_version.h"
-#include <memory>
+//#include <memory>
#include <zen/string_tools.h>
#include <zen/i18n.h>
#include <zen/utf.h>
#include <wx/msgdlg.h>
-#include <wx/protocol/http.h>
-#include <wx/sstream.h>
-#include <wx/utils.h>
#include <wx/timer.h>
+#include <wx/utils.h>
#include "msg_popup.h"
#include "../version/version.h"
-//#include "../lib/ffs_paths.h"
+////#include "../lib/ffs_paths.h"
#include <zen/scope_guard.h>
#ifdef FFS_WIN
+#include <zen/win.h> //tame wininet include
#include <wininet.h>
+
+#elif defined FFS_LINUX || defined FFS_MAC
+#include <wx/protocol/http.h>
+#include <wx/sstream.h>
#endif
using namespace zen;
@@ -158,7 +161,7 @@ GetVerResult getOnlineVersion(wxString& version) //empty string on error;
{
wxHTTP webAccess;
webAccess.SetHeader(L"content-type", L"text/html; charset=utf-8");
- webAccess.SetTimeout(timeout); //default: 10 minutes(WTF are they thinking???)...
+ webAccess.SetTimeout(timeout); //default: 10 minutes(WTF are these wxWidgets people thinking???)...
if (webAccess.Connect(server)) //will *not* fail for non-reachable url here!
{
@@ -245,7 +248,7 @@ void zen::checkForUpdateNow(wxWindow* parent)
}
-void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck)
+void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, const std::function<void()>& onBeforeInternetAccess)
{
if (lastUpdateCheck != -1)
{
@@ -268,9 +271,10 @@ void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck)
// break;
// }
//}
- //else
- if (wxGetLocalTime() >= lastUpdateCheck + 7 * 24 * 3600) //check weekly
+ //else
+ if (wxGetLocalTime() >= lastUpdateCheck + 7 * 24 * 3600) //check weekly
{
+ onBeforeInternetAccess(); //notify client before (potentially) blocking some time
wxString onlineVersion;
switch (getOnlineVersion(onlineVersion))
{
diff --git a/ui/check_version.h b/ui/check_version.h
index a61cc98c..d2e7220f 100644
--- a/ui/check_version.h
+++ b/ui/check_version.h
@@ -7,14 +7,14 @@
#ifndef UPDATEVERSION_H_INCLUDED
#define UPDATEVERSION_H_INCLUDED
+#include <functional>
#include <wx/window.h>
namespace zen
{
void checkForUpdateNow(wxWindow* parent);
-
-void checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck);
+void checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, const std::function<void()>& onBeforeInternetAccess); //-1: check never
}
#endif // UPDATEVERSION_H_INCLUDED
diff --git a/ui/column_attr.h b/ui/column_attr.h
index ab196cb1..5f0d79aa 100644
--- a/ui/column_attr.h
+++ b/ui/column_attr.h
@@ -76,7 +76,8 @@ enum ColumnTypeMiddle
enum ColumnTypeNavi
{
COL_TYPE_NAVI_BYTES,
- COL_TYPE_NAVI_DIRECTORY
+ COL_TYPE_NAVI_DIRECTORY,
+ COL_TYPE_NAVI_ITEM_COUNT
};
@@ -93,15 +94,16 @@ struct ColumnAttributeNavi
const bool defaultValueShowPercentage = true;
-const ColumnTypeNavi defaultValueLastSortColumn = COL_TYPE_NAVI_DIRECTORY; //remember sort on navigation panel
-const bool defaultValueLastSortAscending = true; //
+const ColumnTypeNavi defaultValueLastSortColumn = COL_TYPE_NAVI_BYTES; //remember sort on navigation panel
+const bool defaultValueLastSortAscending = false; //
inline
std::vector<ColumnAttributeNavi> getDefaultColumnAttributesNavi()
{
std::vector<ColumnAttributeNavi> attr;
- attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_DIRECTORY, -60, 1, true)); //stretch to full width and substract sum of fixed size widths
- attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_BYTES, 60, 0, true)); //GTK needs a few pixels width more
+ attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_DIRECTORY, -120, 1, true)); //stretch to full width and substract sum of fixed size widths
+ attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_ITEM_COUNT, 60, 0, true));
+ attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_BYTES, 60, 0, true)); //GTK needs a few pixels width more
return attr;
}
}
diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp
index 006ad834..35d3248d 100644
--- a/ui/custom_grid.cpp
+++ b/ui/custom_grid.cpp
@@ -695,11 +695,11 @@ class GridDataLeft : public GridDataRim<LEFT_SIDE>
public:
GridDataLeft(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataRim<LEFT_SIDE>(gridDataView, grid) {}
- void setNavigationMarker(std::vector<const HierarchyObject*>&& markedFiles,
- std::vector<const HierarchyObject*>&& markedContainer)
+ void setNavigationMarker(hash_set<const FileSystemObject*>&& markedFilesAndLinks,
+ hash_set<const HierarchyObject*>&& markedContainer)
{
- markedFiles_ .swap(markedFiles);
- markedContainer_.swap(markedContainer);
+ markedFilesAndLinks_.swap(markedFilesAndLinks);
+ markedContainer_ .swap(markedContainer);
}
private:
@@ -714,33 +714,26 @@ private:
{
if (const FileSystemObject* fsObj = getRawData(row))
{
- if (dynamic_cast<const FileMapping*>(fsObj) || dynamic_cast<const SymLinkMapping*>(fsObj))
- {
- for (auto it = markedFiles_.begin(); it != markedFiles_.end(); ++it)
- if (*it == &(fsObj->parent())) //mark files/links wich have the given parent
- return true;
- }
- else if (auto dirObj = dynamic_cast<const DirMapping*>(fsObj))
+ if (markedFilesAndLinks_.find(fsObj) != markedFilesAndLinks_.end()) //mark files/links directly
+ return true;
+
+ if (auto dirObj = dynamic_cast<const DirMapping*>(fsObj))
{
- for (auto it = markedContainer_.begin(); it != markedContainer_.end(); ++it)
- if (*it == dirObj) //mark directories which *are* the given HierarchyObject*
- return true;
+ if (markedContainer_.find(dirObj) != markedContainer_.end()) //mark directories which *are* the given HierarchyObject*
+ return true;
}
- for (auto it = markedContainer_.begin(); it != markedContainer_.end(); ++it)
+ //mark all objects which have the HierarchyObject as *any* matching ancestor
+ const HierarchyObject* parent = &(fsObj->parent());
+ for (;;)
{
- //mark all objects which have the HierarchyObject as *any* matching ancestor
- const HierarchyObject* parent = &(fsObj->parent());
- for (;;)
- {
- if (*it == parent)
- return true;
+ if (markedContainer_.find(parent) != markedContainer_.end())
+ return true;
- if (auto dirObj = dynamic_cast<const DirMapping*>(parent))
- parent = &(dirObj->parent());
- else
- break;
- }
+ if (auto dirObj = dynamic_cast<const DirMapping*>(parent))
+ parent = &(dirObj->parent());
+ else
+ break;
}
}
return false;
@@ -758,8 +751,8 @@ private:
}
}
- std::vector<const HierarchyObject*> markedFiles_; //mark files/symlinks directly within a container
- std::vector<const HierarchyObject*> markedContainer_; //mark full container including all child-objects
+ hash_set<const FileSystemObject*> markedFilesAndLinks_; //mark files/symlinks directly within a container
+ hash_set<const HierarchyObject*> markedContainer_; //mark full container including all child-objects
//DO NOT DEREFERENCE!!!! NOT GUARANTEED TO BE VALID!!!
};
@@ -837,7 +830,8 @@ public:
}
//update highlight and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows)
- onMouseMovement(refGrid().getMainWin().ScreenToClient(wxGetMousePosition()));
+ wxPoint clientPos = refGrid().getMainWin().ScreenToClient(wxGetMousePosition());
+ onMouseMovement(clientPos);
}
void onMouseMovement(const wxPoint& clientPos)
@@ -859,7 +853,8 @@ public:
refreshCell(refGrid(), highlight->row_, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE));
//show custom tooltip
- showToolTip(row, refGrid().getMainWin().ClientToScreen(clientPos));
+ if (refGrid().getMainWin().GetClientRect().Contains(clientPos)) //cursor might have moved outside visible client area
+ showToolTip(row, refGrid().getMainWin().ClientToScreen(clientPos));
}
else
onMouseLeave();
@@ -1623,11 +1618,11 @@ void gridview::refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
void gridview::setNavigationMarker(Grid& gridLeft,
- std::vector<const HierarchyObject*>&& markedFiles,
- std::vector<const HierarchyObject*>&& markedContainer)
+ hash_set<const FileSystemObject*>&& markedFilesAndLinks,
+ hash_set<const HierarchyObject*>&& markedContainer)
{
if (auto* provLeft = dynamic_cast<GridDataLeft*>(gridLeft.getDataProvider()))
- provLeft->setNavigationMarker(std::move(markedFiles), std::move(markedContainer));
+ provLeft->setNavigationMarker(std::move(markedFilesAndLinks), std::move(markedContainer));
else
assert(false);
gridLeft.Refresh();
diff --git a/ui/custom_grid.h b/ui/custom_grid.h
index 6381c8c0..c2e653ef 100644
--- a/ui/custom_grid.h
+++ b/ui/custom_grid.h
@@ -31,8 +31,8 @@ void refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight);
//mark rows selected in navigation/compressed tree and navigate to leading object
void setNavigationMarker(Grid& gridLeft,
- std::vector<const HierarchyObject*>&& markedFiles, //mark files/symlinks directly within a container
- std::vector<const HierarchyObject*>&& markedContainer); //mark full container including child-objects
+ hash_set<const FileSystemObject*>&& markedFilesAndLinks,//mark files/symlinks directly within a container
+ hash_set<const HierarchyObject*>&& markedContainer); //mark full container including child-objects
}
wxBitmap getSyncOpImage(SyncOperation syncOp);
diff --git a/ui/folder_pair.h b/ui/folder_pair.h
index 5a629ff5..801cd606 100644
--- a/ui/folder_pair.h
+++ b/ui/folder_pair.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef FOLDERPAIR_H_INCLUDED
-#define FOLDERPAIR_H_INCLUDED
+#ifndef FOLDERPAIR_H_89341750847252345
+#define FOLDERPAIR_H_89341750847252345
#include <wx/event.h>
#include <wx/menu.h>
@@ -30,10 +30,6 @@ public:
typedef std::shared_ptr<const CompConfig> AltCompCfgPtr;
typedef std::shared_ptr<const SyncConfig> AltSyncCfgPtr;
- AltCompCfgPtr getAltCompConfig() const { return altCompConfig; }
- AltSyncCfgPtr getAltSyncConfig() const { return altSyncConfig; }
- FilterConfig getAltFilterConfig() const { return localFilter; }
-
void setConfig(AltCompCfgPtr compConfig, AltSyncCfgPtr syncCfg, const FilterConfig& filter)
{
altCompConfig = compConfig;
@@ -42,6 +38,27 @@ public:
refreshButtons();
}
+ AltCompCfgPtr getAltCompConfig() const { return altCompConfig; }
+ AltSyncCfgPtr getAltSyncConfig() const { return altSyncConfig; }
+ FilterConfig getAltFilterConfig() const { return localFilter; }
+
+
+ FolderPairPanelBasic(GuiPanel& basicPanel) : //takes reference on basic panel to be enhanced
+ basicPanel_(basicPanel)
+ {
+ //register events for removal of alternate configuration
+ basicPanel_.m_bpButtonAltCompCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfgContext ), nullptr, this);
+ basicPanel_.m_bpButtonAltSyncCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfgContext ), nullptr, this);
+ basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfgContext), nullptr, this);
+
+ basicPanel_.m_bpButtonAltCompCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfg ), nullptr, this);
+ basicPanel_.m_bpButtonAltSyncCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfg ), nullptr, this);
+ basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfg), nullptr, this);
+
+ basicPanel_.m_bpButtonRemovePair->SetBitmapLabel(getResourceImage(L"item_delete"));
+ }
+
+private:
void refreshButtons()
{
if (altCompConfig.get())
@@ -79,69 +96,72 @@ public:
}
}
-protected:
- FolderPairPanelBasic(GuiPanel& basicPanel) : //takes reference on basic panel to be enhanced
- basicPanel_(basicPanel)
- {
- //register events for removal of alternate configuration
- basicPanel_.m_bpButtonAltCompCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfgContext ), nullptr, this);
- basicPanel_.m_bpButtonAltSyncCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfgContext ), nullptr, this);
- basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfgContext), nullptr, this);
-
- basicPanel_.m_bpButtonAltCompCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltCompCfg ), nullptr, this);
- basicPanel_.m_bpButtonAltSyncCfg-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfg ), nullptr, this);
- basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfg), nullptr, this);
-
- basicPanel_.m_bpButtonRemovePair->SetBitmapLabel(getResourceImage(L"item_delete"));
- }
-
- virtual void removeAltCompCfg()
- {
- altCompConfig.reset();
- refreshButtons();
- }
-
- virtual void removeAltSyncCfg()
- {
- altSyncConfig.reset();
- refreshButtons();
- }
-
- virtual void removeLocalFilterCfg()
- {
- localFilter = FilterConfig();
- refreshButtons();
- }
-
-private:
void OnAltCompCfgContext(wxCommandEvent& event)
{
+ auto removeAltCompCfg = [&]
+ {
+ this->altCompConfig.reset(); //"this->" galore: workaround GCC compiler bugs
+ this->refreshButtons();
+ this->onAltCompCfgChange();
+ };
+
ContextMenu menu;
- menu.addItem(_("Remove alternate settings"), [this] { this->removeAltCompCfg(); }, nullptr, altCompConfig.get() != nullptr);
+ menu.addItem(_("Remove alternate settings"), removeAltCompCfg, nullptr, altCompConfig.get() != nullptr);
menu.popup(basicPanel_);
}
void OnAltSyncCfgContext(wxCommandEvent& event)
{
+ auto removeAltSyncCfg = [&]
+ {
+ this->altSyncConfig.reset();
+ this->refreshButtons();
+ this->onAltSyncCfgChange();
+ };
+
ContextMenu menu;
- menu.addItem(_("Remove alternate settings"), [this] { this->removeAltSyncCfg(); }, nullptr, altSyncConfig.get() != nullptr);
+ menu.addItem(_("Remove alternate settings"), removeAltSyncCfg, nullptr, altSyncConfig.get() != nullptr);
menu.popup(basicPanel_);
}
void OnLocalFilterCfgContext(wxCommandEvent& event)
{
+ auto removeLocalFilterCfg = [&]
+ {
+ this->localFilter = FilterConfig();
+ this->refreshButtons();
+ this->onLocalFilterCfgChange();
+ };
+
+ std::unique_ptr<FilterConfig>& filterCfgOnClipboard = getFilterCfgOnClipboardRef();
+
+ auto copyFilter = [&] { filterCfgOnClipboard = make_unique<FilterConfig>(this->localFilter); };
+ auto pasteFilter = [&]
+ {
+ if (filterCfgOnClipboard)
+ {
+ this->localFilter = *filterCfgOnClipboard;
+ this->refreshButtons();
+ this->onLocalFilterCfgChange();
+ }
+ };
+
ContextMenu menu;
- menu.addItem(_("Clear filter settings"), [this] { this->removeLocalFilterCfg(); }, nullptr, !isNullFilter(localFilter));
+ menu.addItem(_("Clear filter settings"), removeLocalFilterCfg, nullptr, !isNullFilter(localFilter));
+ menu.addSeparator();
+ menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(localFilter));
+ menu.addItem( _("Paste"), pasteFilter, nullptr, filterCfgOnClipboard.get() != nullptr);
menu.popup(basicPanel_);
}
virtual MainConfiguration getMainConfig() const = 0;
virtual wxWindow* getParentWindow() = 0;
+ virtual std::unique_ptr<FilterConfig>& getFilterCfgOnClipboardRef() = 0;
- virtual void OnAltCompCfgChange() = 0;
- virtual void OnAltSyncCfgChange() = 0;
- virtual void OnLocalFilterCfgChange() {};
+ virtual void onAltCompCfgChange() = 0;
+ virtual void onAltSyncCfgChange() = 0;
+ virtual void onLocalFilterCfgChange() = 0;
void OnAltCompCfg(wxCommandEvent& event)
{
@@ -153,8 +173,7 @@ private:
{
altCompConfig = std::make_shared<CompConfig>(cmpCfg);
refreshButtons();
-
- OnAltCompCfgChange();
+ onAltCompCfgChange();
}
}
@@ -173,8 +192,7 @@ private:
{
altSyncConfig = std::make_shared<SyncConfig>(syncCfg);
refreshButtons();
-
- OnAltSyncCfgChange();
+ onAltSyncCfgChange();
}
}
@@ -188,8 +206,7 @@ private:
{
localFilter = localFiltTmp;
refreshButtons();
-
- OnLocalFilterCfgChange();
+ onLocalFilterCfgChange();
}
}
@@ -203,6 +220,4 @@ private:
}
-#endif // FOLDERPAIR_H_INCLUDED
-
-
+#endif //FOLDERPAIR_H_89341750847252345
diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp
index 6ce104e7..db1f03da 100644
--- a/ui/gui_generated.cpp
+++ b/ui/gui_generated.cpp
@@ -89,8 +89,15 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_menuItemManual = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&Content") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL );
m_menuHelp->Append( m_menuItemManual );
- m_menuItemCheckVer = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("&Check for new version") ) , wxEmptyString, wxITEM_NORMAL );
- m_menuHelp->Append( m_menuItemCheckVer );
+ m_menuCheckVersion = new wxMenu();
+ m_menuItemCheckVersionNow = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("&Check now") ) , wxEmptyString, wxITEM_NORMAL );
+ m_menuCheckVersion->Append( m_menuItemCheckVersionNow );
+
+ m_menuItemCheckVersionAuto = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK );
+ m_menuCheckVersion->Append( m_menuItemCheckVersionAuto );
+ m_menuItemCheckVersionAuto->Check( true );
+
+ m_menuHelp->Append( -1, _("Check for new version"), m_menuCheckVersion );
m_menuHelp->AppendSeparator();
@@ -854,7 +861,8 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
this->Connect( m_menuItemGlobSett->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuGlobalSettings ) );
this->Connect( m_menuItem5->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) );
this->Connect( m_menuItemManual->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) );
- this->Connect( m_menuItemCheckVer->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) );
+ this->Connect( m_menuItemCheckVersionNow->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) );
+ this->Connect( m_menuItemCheckVersionAuto->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersionAutomatically ) );
this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) );
m_buttonCompare->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this );
m_bpButtonCmpConfig->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this );
@@ -921,6 +929,7 @@ MainDialogGenerated::~MainDialogGenerated()
this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) );
this->Disconnect( wxID_HELP, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) );
this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) );
+ this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersionAutomatically ) );
this->Disconnect( wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuAbout ) );
m_buttonCompare->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCompare ), NULL, this );
m_bpButtonCmpConfig->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnCmpSettings ), NULL, this );
@@ -1378,13 +1387,13 @@ SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID
m_buttonClose->SetDefault();
m_buttonClose->Enable( false );
- bSizer28->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+ bSizer28->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1,30 ), 0 );
- bSizer28->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+ bSizer28->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
m_buttonAbort = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 );
- bSizer28->Add( m_buttonAbort, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+ bSizer28->Add( m_buttonAbort, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
bSizerRoot->Add( bSizer28, 0, wxALIGN_RIGHT, 5 );
@@ -1769,7 +1778,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const
fgSizer1->Add( m_toggleBtnAutomatic, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
- m_staticTextAutomatic = new wxStaticText( m_panel37, wxID_ANY, _("Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically."), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextAutomatic = new wxStaticText( m_panel37, wxID_ANY, _("Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextAutomatic->Wrap( 450 );
fgSizer1->Add( m_staticTextAutomatic, 0, wxALIGN_CENTER_VERTICAL, 5 );
@@ -1910,7 +1919,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const
bSizer180->Add( m_toggleBtnRecycler, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
m_toggleBtnVersioning = new wxToggleButton( m_panel37, wxID_ANY, _("Versioning"), wxDefaultPosition, wxDefaultSize, 0 );
- m_toggleBtnVersioning->SetToolTip( _("Move time-stamped files into specified folder") );
+ m_toggleBtnVersioning->SetToolTip( _("Move files to user-defined folder") );
bSizer180->Add( m_toggleBtnVersioning, 0, wxALIGN_CENTER_VERTICAL, 5 );
@@ -1966,38 +1975,45 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const
bSizerConfig = new wxBoxSizer( wxVERTICAL );
- m_staticText90 = new wxStaticText( m_panel37, wxID_ANY, _("Configuration"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText90->Wrap( -1 );
- bSizerConfig->Add( m_staticText90, 0, wxBOTTOM, 5 );
+ wxBoxSizer* bSizer18011;
+ bSizer18011 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_staticTextHeaderCategory1 = new wxStaticText( m_panel37, wxID_ANY, _("Category"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
+ m_staticTextHeaderCategory1->Wrap( -1 );
+ bSizer18011->Add( m_staticTextHeaderCategory1, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer18011->Add( 5, 0, 0, 0, 5 );
+
+ m_staticTextHeaderAction1 = new wxStaticText( m_panel37, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );
+ m_staticTextHeaderAction1->Wrap( -1 );
+ bSizer18011->Add( m_staticTextHeaderAction1, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+
+ bSizerConfig->Add( bSizer18011, 0, wxEXPAND, 5 );
bSizerConfig->Add( 0, 5, 0, 0, 5 );
m_bitmapDatabase = new wxStaticBitmap( m_panel37, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 );
- bSizerConfig->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL, 10 );
+ bSizerConfig->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP, 10 );
- sbSizerSyncDirections = new wxBoxSizer( wxVERTICAL );
+ wxBoxSizer* sbSizerKeepWidthStable;
+ sbSizerKeepWidthStable = new wxBoxSizer( wxHORIZONTAL );
- wxBoxSizer* bSizer1801;
- bSizer1801 = new wxBoxSizer( wxHORIZONTAL );
- m_staticTextHeaderCategory = new wxStaticText( m_panel37, wxID_ANY, _("Category"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
- m_staticTextHeaderCategory->Wrap( -1 );
- m_staticTextHeaderCategory->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ sbSizerKeepWidthStable->Add( 45, 0, 0, 0, 5 );
- bSizer1801->Add( m_staticTextHeaderCategory, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+ sbSizerKeepWidthStable->Add( 5, 0, 0, 0, 5 );
- bSizer1801->Add( 5, 0, 0, 0, 5 );
- m_staticTextHeaderAction = new wxStaticText( m_panel37, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT );
- m_staticTextHeaderAction->Wrap( -1 );
- m_staticTextHeaderAction->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ sbSizerKeepWidthStable->Add( 46, 0, 0, 0, 5 );
- bSizer1801->Add( m_staticTextHeaderAction, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+ bSizerConfig->Add( sbSizerKeepWidthStable, 0, 0, 5 );
- sbSizerSyncDirections->Add( bSizer1801, 0, wxEXPAND, 5 );
+ sbSizerSyncDirections = new wxBoxSizer( wxVERTICAL );
bSizerLeftOnly = new wxBoxSizer( wxHORIZONTAL );
@@ -2099,9 +2115,6 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const
bSizerConfig->Add( sbSizerSyncDirections, 0, wxEXPAND, 5 );
- bSizerConfig->Add( 0, 0, 1, wxEXPAND, 5 );
-
-
bSizer181->Add( bSizerConfig, 0, wxALL|wxEXPAND, 5 );
@@ -2913,8 +2926,8 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer22;
bSizer22 = new wxBoxSizer( wxHORIZONTAL );
- m_button9 = new wxButton( this, wxID_DEFAULT, _("&Default"), wxDefaultPosition, wxSize( -1,30 ), 0 );
- bSizer22->Add( m_button9, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+ m_buttonClear = new wxButton( this, wxID_DEFAULT, _("&Clear"), wxDefaultPosition, wxSize( -1,30 ), 0 );
+ bSizer22->Add( m_buttonClear, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
bSizer22->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 );
@@ -2946,7 +2959,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_choiceUnitTimespan->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this );
m_choiceUnitMinSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this );
m_choiceUnitMaxSize->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this );
- m_button9->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnDefault ), NULL, this );
+ m_buttonClear->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnClear ), NULL, this );
m_buttonOk->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnApply ), NULL, this );
m_button17->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnCancel ), NULL, this );
}
@@ -2961,7 +2974,7 @@ FilterDlgGenerated::~FilterDlgGenerated()
m_choiceUnitTimespan->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this );
m_choiceUnitMinSize->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this );
m_choiceUnitMaxSize->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( FilterDlgGenerated::OnUpdateChoice ), NULL, this );
- m_button9->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnDefault ), NULL, this );
+ m_buttonClear->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnClear ), NULL, this );
m_buttonOk->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnApply ), NULL, this );
m_button17->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnCancel ), NULL, this );
diff --git a/ui/gui_generated.h b/ui/gui_generated.h
index 0972f194..1f034dc2 100644
--- a/ui/gui_generated.h
+++ b/ui/gui_generated.h
@@ -82,7 +82,9 @@ protected:
wxMenuItem* m_menuItemGlobSett;
wxMenu* m_menuHelp;
wxMenuItem* m_menuItemManual;
- wxMenuItem* m_menuItemCheckVer;
+ wxMenu* m_menuCheckVersion;
+ wxMenuItem* m_menuItemCheckVersionNow;
+ wxMenuItem* m_menuItemCheckVersionAuto;
wxMenuItem* m_menuItemAbout;
wxBoxSizer* bSizerPanelHolder;
wxPanel* m_panelTopButtons;
@@ -188,6 +190,7 @@ protected:
virtual void OnMenuExportFileList( wxCommandEvent& event ) { event.Skip(); }
virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMenuCheckVersion( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnMenuCheckVersionAutomatically( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMenuAbout( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCmpSettings( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCompSettingsContext( wxMouseEvent& event ) { event.Skip(); }
@@ -482,11 +485,10 @@ protected:
wxButton* m_buttonSelectDirVersioning;
wxStaticLine* m_staticline31;
wxBoxSizer* bSizerConfig;
- wxStaticText* m_staticText90;
+ wxStaticText* m_staticTextHeaderCategory1;
+ wxStaticText* m_staticTextHeaderAction1;
wxStaticBitmap* m_bitmapDatabase;
wxBoxSizer* sbSizerSyncDirections;
- wxStaticText* m_staticTextHeaderCategory;
- wxStaticText* m_staticTextHeaderAction;
wxBoxSizer* bSizerLeftOnly;
wxStaticBitmap* m_bitmapLeftOnly;
wxBitmapButton* m_bpButtonLeftOnly;
@@ -730,7 +732,7 @@ protected:
wxSpinCtrl* m_spinCtrlMaxSize;
wxChoice* m_choiceUnitMaxSize;
wxStaticLine* m_staticline16;
- wxButton* m_button9;
+ wxButton* m_buttonClear;
wxButton* m_buttonOk;
wxButton* m_button17;
@@ -739,14 +741,14 @@ protected:
virtual void OnHelp( wxCommandEvent& event ) { event.Skip(); }
virtual void OnUpdateNameFilter( wxCommandEvent& event ) { event.Skip(); }
virtual void OnUpdateChoice( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnDefault( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnClear( wxCommandEvent& event ) { event.Skip(); }
virtual void OnApply( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
public:
- FilterDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Configure filter"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
+ FilterDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Configure filter"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
~FilterDlgGenerated();
};
diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp
index 26036817..cb05e086 100644
--- a/ui/main_dlg.cpp
+++ b/ui/main_dlg.cpp
@@ -13,7 +13,7 @@
#include <zen/format_unit.h>
#include <zen/file_handling.h>
#include <zen/serialize.h>
-#include <zen/file_id.h>
+//#include <zen/file_id.h>
#include <zen/thread.h>
#include <wx+/context_menu.h>
#include <wx+/string_conv.h>
@@ -54,9 +54,9 @@ namespace
{
struct wxClientHistoryData: public wxClientData //we need a wxClientData derived class to tell wxWidgets to take object ownership!
{
- wxClientHistoryData(const wxString& cfgFile, int lastUseIndex) : cfgFile_(cfgFile), lastUseIndex_(lastUseIndex) {}
+ wxClientHistoryData(const Zstring& cfgFile, int lastUseIndex) : cfgFile_(cfgFile), lastUseIndex_(lastUseIndex) {}
- wxString cfgFile_;
+ Zstring cfgFile_;
int lastUseIndex_; //support sorting history by last usage, the higher the index the more recent the usage
};
@@ -99,7 +99,7 @@ public:
case xmlAccess::MERGE_BATCH:
case xmlAccess::MERGE_GUI:
case xmlAccess::MERGE_GUI_BATCH:
- mainDlg_.loadConfiguration(droppedFiles);
+ mainDlg_.loadConfiguration(toZ(droppedFiles));
return false;
case xmlAccess::MERGE_OTHER:
@@ -142,34 +142,13 @@ public:
mainDlg(mainDialog) {}
private:
- virtual wxWindow* getParentWindow()
- {
- return &mainDlg;
- }
-
virtual MainConfiguration getMainConfig() const { return mainDlg.getConfig().mainCfg; }
- virtual void OnAltCompCfgChange() { mainDlg.applyCompareConfig(); }
- virtual void OnAltSyncCfgChange() { mainDlg.applySyncConfig (); }
-
- virtual void removeAltCompCfg()
- {
- FolderPairPanelBasic<GuiPanel>::removeAltCompCfg();
- mainDlg.applyCompareConfig();
- }
-
- virtual void removeAltSyncCfg()
- {
- FolderPairPanelBasic<GuiPanel>::removeAltSyncCfg();
- mainDlg.applySyncConfig();
- }
+ virtual wxWindow* getParentWindow() { return &mainDlg; }
+ virtual std::unique_ptr<FilterConfig>& getFilterCfgOnClipboardRef() { return mainDlg.filterCfgOnClipboard; }
- virtual void OnLocalFilterCfgChange() { mainDlg.applyFilterConfig(); } //re-apply filter
-
- virtual void removeLocalFilterCfg()
- {
- FolderPairPanelBasic<GuiPanel>::removeLocalFilterCfg();
- mainDlg.applyFilterConfig(); //update filter
- }
+ virtual void onAltCompCfgChange () { mainDlg.applyCompareConfig(); }
+ virtual void onAltSyncCfgChange () { mainDlg.applySyncConfig(); }
+ virtual void onLocalFilterCfgChange() { mainDlg.applyFilterConfig(); } //re-apply filter
MainDialog& mainDlg;
};
@@ -193,18 +172,18 @@ public:
dirNameRight.Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog);
}
- void setValues(const wxString& leftDir,
- const wxString& rightDir,
+ void setValues(const Zstring& leftDir,
+ const Zstring& rightDir,
AltCompCfgPtr cmpCfg,
AltSyncCfgPtr syncCfg,
const FilterConfig& filter)
{
setConfig(cmpCfg, syncCfg, filter);
- dirNameLeft.setName(leftDir);
- dirNameRight.setName(rightDir);
+ dirNameLeft .setName(toWx(leftDir));
+ dirNameRight.setName(toWx(rightDir));
}
- wxString getLeftDir () const { return dirNameLeft .getName(); }
- wxString getRightDir() const { return dirNameRight.getName(); }
+ Zstring getLeftDir () const { return toZ(dirNameLeft .getName()); }
+ Zstring getRightDir() const { return toZ(dirNameRight.getName()); }
private:
//support for drag and drop
@@ -240,18 +219,18 @@ public:
dirNameRight.Connect(EVENT_ON_DIR_MANUAL_CORRECTION, wxCommandEventHandler(MainDialog::onDirManualCorrection), nullptr, &mainDialog);
}
- void setValues(const wxString& leftDir,
- const wxString& rightDir,
+ void setValues(const Zstring& leftDir,
+ const Zstring& rightDir,
AltCompCfgPtr cmpCfg,
AltSyncCfgPtr syncCfg,
const FilterConfig& filter)
{
setConfig(cmpCfg, syncCfg, filter);
- dirNameLeft.setName(leftDir);
- dirNameRight.setName(rightDir);
+ dirNameLeft .setName(toWx(leftDir));
+ dirNameRight.setName(toWx(rightDir));
}
- wxString getLeftDir () const { return dirNameLeft .getName(); }
- wxString getRightDir() const { return dirNameRight.getName(); }
+ Zstring getLeftDir () const { return toZ(dirNameLeft .getName()); }
+ Zstring getRightDir() const { return toZ(dirNameRight.getName()); }
private:
//support for drag and drop
@@ -328,12 +307,13 @@ xmlAccess::XmlGlobalSettings retrieveGlobalCfgFromDisk() //blocks on GUI on erro
XmlGlobalSettings globalCfg;
try
{
- if (fileExists(toZ(getGlobalConfigFile())))
+ if (fileExists(getGlobalConfigFile()))
readConfig(globalCfg); //throw FfsXmlError
//else: globalCfg already has default values
}
catch (const FfsXmlError& e)
{
+ assert(false);
if (e.getSeverity() != FfsXmlError::WARNING) //ignore parsing errors: should be migration problems only *cross-fingers*
wxMessageBox(e.toString(), _("Error"), wxOK | wxICON_ERROR);
}
@@ -342,12 +322,12 @@ xmlAccess::XmlGlobalSettings retrieveGlobalCfgFromDisk() //blocks on GUI on erro
}
-void MainDialog::create(const std::vector<wxString>& cfgFileNames)
+void MainDialog::create(const std::vector<Zstring>& cfgFileNames)
{
using namespace xmlAccess;
const XmlGlobalSettings globalSettings = retrieveGlobalCfgFromDisk();
- std::vector<wxString> filenames;
+ std::vector<Zstring> filenames;
if (!cfgFileNames.empty()) //1. this one has priority
filenames = cfgFileNames;
else //FFS default startup: use last used selection
@@ -359,11 +339,9 @@ void MainDialog::create(const std::vector<wxString>& cfgFileNames)
RunUntilFirstHit<NullType> findFirstMissing;
- std::for_each(filenames.begin(), filenames.end(),
- [&](const wxString& filename)
+ std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename)
{
- const Zstring filenameFmt = toZ(filename); //convert to Zstring first: we don't want to pass wxString by value and risk MT issues!
- findFirstMissing.addJob([=] { return filenameFmt.empty() /*ever empty??*/ || !fileExists(filenameFmt) ? zen::make_unique<NullType>() : nullptr; });
+ findFirstMissing.addJob([=] { return filename.empty() /*ever empty??*/ || !fileExists(filename) ? zen::make_unique<NullType>() : nullptr; });
});
//potentially slow network access: give all checks 500ms to finish
const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //false: time elapsed
@@ -374,7 +352,7 @@ void MainDialog::create(const std::vector<wxString>& cfgFileNames)
if (filenames.empty())
{
- if (zen::fileExists(zen::toZ(lastRunConfigName()))) //3. try to load auto-save config
+ if (zen::fileExists(lastRunConfigName())) //3. try to load auto-save config
filenames.push_back(lastRunConfigName());
}
}
@@ -382,10 +360,19 @@ void MainDialog::create(const std::vector<wxString>& cfgFileNames)
XmlGuiConfig guiCfg; //structure to receive gui settings with default values
bool loadCfgSuccess = false;
- if (!filenames.empty())
+ if (filenames.empty())
+ {
+ //add default exclusion filter: this is only ever relevant when creating new configurations!
+ //a default XmlGuiConfig does not need these user-specific exclusions!
+ Zstring& excludeFilter = guiCfg.mainCfg.globalFilter.excludeFilter;
+ if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n")))
+ excludeFilter += Zstr("\n");
+ excludeFilter += globalSettings.gui.defaultExclusionFilter;
+ }
+ else
try
{
- readAnyConfig(toZ(filenames), guiCfg); //throw FfsXmlError
+ readAnyConfig(filenames, guiCfg); //throw FfsXmlError
loadCfgSuccess = true;
}
catch (const FfsXmlError& error)
@@ -396,6 +383,7 @@ void MainDialog::create(const std::vector<wxString>& cfgFileNames)
else
wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR);
}
+
const bool startComparisonImmediately = !cfgFileNames.empty() && loadCfgSuccess;
//------------------------------------------------------------------------------------------
@@ -407,12 +395,12 @@ void MainDialog::create(const std::vector<wxString>& cfgFileNames)
void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg,
bool startComparison)
{
- create_impl(guiCfg, std::vector<wxString>(), retrieveGlobalCfgFromDisk(), startComparison);
+ create_impl(guiCfg, std::vector<Zstring>(), retrieveGlobalCfgFromDisk(), startComparison);
}
void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<wxString>& referenceFiles,
+ const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison)
{
@@ -421,7 +409,7 @@ void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg,
void MainDialog::create_impl(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<wxString>& referenceFiles,
+ const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison)
{
@@ -442,7 +430,7 @@ void MainDialog::create_impl(const xmlAccess::XmlGuiConfig& guiCfg,
MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<wxString>& referenceFiles,
+ const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison) :
MainDialogGenerated(nullptr),
@@ -479,34 +467,34 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
//caption required for all panes that can be manipulated by the users => used by context menu
auiMgr.AddPane(m_panelTopButtons,
- wxAuiPaneInfo().Name(wxT("Panel1")).Layer(4).Top().Caption(_("Main bar")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(-1, m_panelTopButtons->GetSize().GetHeight()));
+ wxAuiPaneInfo().Name(L"Panel1").Layer(4).Top().Caption(_("Main bar")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(-1, m_panelTopButtons->GetSize().GetHeight()));
//note: min height is calculated incorrectly by wxAuiManager if panes with and without caption are in the same row => use smaller min-size
compareStatus = make_unique<CompareProgressDialog>(*this); //integrate the compare status panel (in hidden state)
auiMgr.AddPane(compareStatus->getAsWindow(),
- wxAuiPaneInfo().Name(wxT("Panel9")).Layer(4).Top().Row(1).CaptionVisible(false).PaneBorder(false).Hide()); //name "CmpStatus" used by context menu
+ wxAuiPaneInfo().Name(L"Panel9").Layer(4).Top().Row(1).CaptionVisible(false).PaneBorder(false).Hide()); //name "CmpStatus" used by context menu
auiMgr.AddPane(m_panelDirectoryPairs,
- wxAuiPaneInfo().Name(wxT("Panel2")).Layer(2).Top().Row(2).Caption(_("Folder pairs")).CaptionVisible(false).PaneBorder(false).Gripper());
+ wxAuiPaneInfo().Name(L"Panel2").Layer(2).Top().Row(2).Caption(_("Folder pairs")).CaptionVisible(false).PaneBorder(false).Gripper());
auiMgr.AddPane(m_panelCenter,
- wxAuiPaneInfo().Name(wxT("Panel3")).CenterPane().PaneBorder(false));
+ wxAuiPaneInfo().Name(L"Panel3").CenterPane().PaneBorder(false));
auiMgr.AddPane(m_gridNavi,
- wxAuiPaneInfo().Name(L"Panel10").Left().Layer(3).Caption(_("Overview")).MinSize(230, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below
+ wxAuiPaneInfo().Name(L"Panel10").Left().Layer(3).Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below
auiMgr.AddPane(m_panelConfig,
- wxAuiPaneInfo().Name(wxT("Panel4")).Layer(4).Bottom().Row(1).Position(0).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight()));
+ wxAuiPaneInfo().Name(L"Panel4").Layer(4).Bottom().Row(1).Position(0).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight()));
auiMgr.AddPane(m_panelFilter,
- wxAuiPaneInfo().Name(wxT("Panel5")).Layer(4).Bottom().Row(1).Position(1).Caption(_("Filter files")).MinSize(m_bpButtonFilter->GetSize().GetWidth(), m_panelFilter->GetSize().GetHeight()));
+ wxAuiPaneInfo().Name(L"Panel5").Layer(4).Bottom().Row(1).Position(1).Caption(_("Filter files")).MinSize(m_bpButtonFilter->GetSize().GetWidth(), m_panelFilter->GetSize().GetHeight()));
auiMgr.AddPane(m_panelViewFilter,
- wxAuiPaneInfo().Name(wxT("Panel6")).Layer(4).Bottom().Row(1).Position(2).Caption(_("Select view")).MinSize(m_bpButtonShowDoNothing->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight()));
+ wxAuiPaneInfo().Name(L"Panel6").Layer(4).Bottom().Row(1).Position(2).Caption(_("Select view")).MinSize(m_bpButtonShowDoNothing->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight()));
auiMgr.AddPane(m_panelStatistics,
- wxAuiPaneInfo().Name(wxT("Panel7")).Layer(4).Bottom().Row(1).Position(3).Caption(_("Statistics")).MinSize(m_bitmapData->GetSize().GetWidth() + m_staticTextData->GetSize().GetWidth(), m_panelStatistics->GetSize().GetHeight()));
+ wxAuiPaneInfo().Name(L"Panel7").Layer(4).Bottom().Row(1).Position(3).Caption(_("Statistics")).MinSize(m_bitmapData->GetSize().GetWidth() + m_staticTextData->GetSize().GetWidth(), m_panelStatistics->GetSize().GetHeight()));
auiMgr.Update();
@@ -632,7 +620,16 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
setMenuItemImage(m_menuItemAbout, getResourceImage(L"aboutSmall"));
if (!manualProgramUpdateRequired())
- m_menuItemCheckVer->Enable(false);
+ {
+ m_menuItemCheckVersionNow ->Enable(false);
+ m_menuItemCheckVersionAuto->Enable(false);
+
+ //wxFormbuilder doesn't give us a wxMenuItem for m_menuCheckVersion, so we need this abomination:
+ wxMenuItemList& items = m_menuHelp->GetMenuItems();
+ for (auto it = items.begin(); it != items.end(); ++it)
+ if ((*it)->GetSubMenu() == m_menuCheckVersion)
+ (*it)->Enable(false);
+ }
//create language selection menu
std::for_each(zen::ExistingTranslations::get().begin(), ExistingTranslations::get().end(),
@@ -664,7 +661,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
setupFileDrop(*m_gridNavi);
m_gridNavi->Connect(EVENT_DROP_FILE, FileDropEventHandler(MainDialog::onNaviPanelFilesDropped), nullptr, this);
- Connect(wxEVT_IDLE, wxEventHandler(MainDialog::OnIdleEvent), nullptr, this);
+ timerForAsyncTasks.Connect(wxEVT_TIMER, wxEventHandler(MainDialog::onProcessAsyncTasks), nullptr, this);
//Connect(wxEVT_SIZE, wxSizeEventHandler(MainDialog::OnResize), nullptr, this);
//Connect(wxEVT_MOVE, wxSizeEventHandler(MainDialog::OnResize), nullptr, this);
@@ -760,7 +757,7 @@ MainDialog::~MainDialog()
try //save "LastRun.ffs_gui"
{
- xmlAccess::writeConfig(getConfig(), toZ(lastRunConfigName())); //throw FfsXmlError
+ xmlAccess::writeConfig(getConfig(), lastRunConfigName()); //throw FfsXmlError
}
//don't annoy users on read-only drives: it's enough to show a single error message when saving global config
catch (const xmlAccess::FfsXmlError&) {}
@@ -781,7 +778,7 @@ void MainDialog::onQueryEndSession()
try { xmlAccess::writeConfig(getGlobalCfgBeforeExit()); }
catch (const xmlAccess::FfsXmlError&) {} //we try our best do to something useful in this extreme situation - no reason to notify or even log errors here!
- try { xmlAccess::writeConfig(getConfig(), toZ(lastRunConfigName())); }
+ try { xmlAccess::writeConfig(getConfig(), lastRunConfigName()); }
catch (const xmlAccess::FfsXmlError&) {}
}
@@ -818,11 +815,12 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe
//--------------------------------------------------------------------------------
//load list of last used configuration files
- std::vector<wxString> cfgFileNames = globalSettings.gui.cfgFileHistory;
+ std::vector<Zstring> cfgFileNames = globalSettings.gui.cfgFileHistory;
std::reverse(cfgFileNames.begin(), cfgFileNames.end()); //list is stored with last used files first in xml, however addFileToCfgHistory() needs them last!!!
cfgFileNames.push_back(lastRunConfigName()); //make sure <Last session> is always part of history list (if existing)
addFileToCfgHistory(cfgFileNames);
+ removeObsoleteCfgHistoryItems(cfgFileNames); //remove non-existent items (we need this only on startup)
//--------------------------------------------------------------------------------
//load list of last used folders
@@ -849,6 +847,8 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe
//if MainDialog::onQueryEndSession() is called while comparison is active, this panel is saved and restored as "visible"
auiMgr.GetPane(compareStatus->getAsWindow()).Hide();
+ m_menuItemCheckVersionAuto->Check(globalCfg.gui.lastUpdateCheck != -1);
+
auiMgr.Update();
}
@@ -876,14 +876,14 @@ xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
//--------------------------------------------------------------------------------
//write list of last used configuration files
- std::map<int, wxString> historyDetail; //(cfg-file/last use index)
+ std::map<int, Zstring> historyDetail; //(cfg-file/last use index)
for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i)
if (auto clientString = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
historyDetail.insert(std::make_pair(clientString->lastUseIndex_, clientString->cfgFile_));
//sort by last use; put most recent items *first* (looks better in xml than the reverse)
- std::vector<wxString> history;
- std::transform(historyDetail.rbegin(), historyDetail.rend(), std::back_inserter(history), [](const std::pair<int, wxString>& item) { return item.second; });
+ std::vector<Zstring> history;
+ std::transform(historyDetail.rbegin(), historyDetail.rend(), std::back_inserter(history), [](const std::pair<int, Zstring>& item) { return item.second; });
if (history.size() > globalSettings.gui.cfgFileHistMax) //erase oldest elements
history.resize(globalSettings.gui.cfgFileHistMax);
@@ -952,7 +952,7 @@ namespace
typedef Zbase<wchar_t> zxString; //guaranteed exponential growth
}
-void MainDialog::copySelectionToClipboard()
+void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRefs)
{
try
{
@@ -983,8 +983,8 @@ void MainDialog::copySelectionToClipboard()
}
};
- addSelection(*m_gridMainL);
- addSelection(*m_gridMainR);
+ for (auto it = gridRefs.begin(); it != gridRefs.end(); ++it)
+ addSelection(**it);
//finally write to clipboard
if (!clipboardString.empty())
@@ -996,7 +996,7 @@ void MainDialog::copySelectionToClipboard()
}
catch (const std::bad_alloc& e)
{
- wxMessageBox(_("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what()), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(_("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what()), _("Error"), wxOK | wxICON_ERROR, this);
}
}
@@ -1023,9 +1023,9 @@ std::vector<FileSystemObject*> MainDialog::getGridSelection(bool fromLeft, bool
std::vector<FileSystemObject*> MainDialog::getTreeSelection() const
{
- const std::vector<size_t>& sel = m_gridNavi->getSelectedRows();
-
std::vector<FileSystemObject*> output;
+
+ const std::vector<size_t>& sel = m_gridNavi->getSelectedRows();
std::for_each(sel.begin(), sel.end(),
[&](size_t row)
{
@@ -1042,12 +1042,7 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const
else if (auto dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
output.push_back(&(dir->dirObj_));
else if (auto file = dynamic_cast<const TreeView::FilesNode*>(node.get()))
- {
- //does a "little more" than what is shown on main grid: we return ALL files
- HierarchyObject& parent = file->firstFile_.parent();
- std::transform(parent.refSubFiles().begin(), parent.refSubFiles().end(), std::back_inserter(output), [](FileSystemObject& fsObj) { return &fsObj; });
- std::transform(parent.refSubLinks().begin(), parent.refSubLinks().end(), std::back_inserter(output), [](FileSystemObject& fsObj) { return &fsObj; });
- }
+ output.insert(output.end(), file->filesAndLinks_.begin(), file->filesAndLinks_.end());
}
});
return output;
@@ -1266,8 +1261,8 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std:
Zstring fallbackDir;
if (selectionTmp.empty())
fallbackDir = leftSide ?
- getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir())) :
- getFormattedDirectoryName(toZ(firstFolderPair->getRightDir()));
+ getFormattedDirectoryName(firstFolderPair->getLeftDir()) :
+ getFormattedDirectoryName(firstFolderPair->getRightDir());
else
fallbackDir = leftSide ?
@@ -1350,12 +1345,11 @@ void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView,
setText(*m_staticTextStatusRightFiles, replaceCpy(_P("1 file", "%x files", filesOnRightView), L"%x", toGuiString(filesOnRightView), false));
setText(*m_staticTextStatusRightBytes, filesizeToShortString(to<Int64>(filesizeRightView)));
-
//fill middle text (considering flashStatusInformation())
- if (!oldStatusMsg)
+ if (oldStatusMsgs.empty())
setText(*m_staticTextStatusMiddle, statusMiddleNew);
else
- *oldStatusMsg = statusMiddleNew;
+ oldStatusMsgs.front() = statusMiddleNew;
m_panelStatusBar->Layout();
}
@@ -1379,36 +1373,42 @@ void MainDialog::setStatusBarFullText(const wxString& msg)
void MainDialog::flashStatusInformation(const wxString& text)
{
- if (!oldStatusMsg)
- oldStatusMsg = make_unique<wxString>(m_staticTextStatusMiddle->GetLabel());
+ oldStatusMsgs.push_back(m_staticTextStatusMiddle->GetLabel());
- lastStatusChange = wxGetLocalTimeMillis();
m_staticTextStatusMiddle->SetLabel(text);
m_staticTextStatusMiddle->SetForegroundColour(wxColour(31, 57, 226)); //highlight color: blue
m_panelStatusBar->Layout();
-
//if (needLayoutUpdate) auiMgr.Update(); -> not needed here, this is called anyway in updateGui()
+
+ asyncTasks.add2([] { boost::this_thread::sleep(boost::posix_time::millisec(2500)); },
+ [this] { this->restoreStatusInformation(); });
+ startProcessingAsyncTasks();
}
-void MainDialog::OnIdleEvent(wxEvent& event)
+void MainDialog::restoreStatusInformation()
{
- //small routine to restore status information after some time
- if (oldStatusMsg) //check if there is some work to do
+ if (!oldStatusMsgs.empty())
{
- wxMilliClock_t currentTime = wxGetLocalTimeMillis();
- if (numeric::dist(currentTime, lastStatusChange) > 2500) //restore after two seconds
- {
- lastStatusChange = currentTime;
+ wxString oldMsg = oldStatusMsgs.back();
+ oldStatusMsgs.pop_back();
- m_staticTextStatusMiddle->SetLabel(*oldStatusMsg);
+ if (oldStatusMsgs.empty()) //restore original status text
+ {
+ m_staticTextStatusMiddle->SetLabel(oldMsg);
m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color
m_panelStatusBar->Layout();
- oldStatusMsg.reset();
}
}
+}
- event.Skip();
+
+void MainDialog::onProcessAsyncTasks(wxEvent& event)
+{
+ //schedule and run long-running tasks asynchronously
+ asyncTasks.evalResults(); //process results on GUI queue
+ if (asyncTasks.empty())
+ timerForAsyncTasks.Stop();
}
@@ -1569,7 +1569,17 @@ void MainDialog::onTreeButtonEvent(wxKeyEvent& event)
}
if (event.ControlDown())
- ;
+ switch (keyCode)
+ {
+ case 'C':
+ case WXK_INSERT: //CTRL + C || CTRL + INS
+ {
+ std::vector<const Grid*> gridRefs;
+ gridRefs.push_back(m_gridNavi);
+ copySelectionToClipboard(gridRefs);
+ }
+ return;
+ }
else if (event.AltDown())
switch (keyCode)
{
@@ -1646,8 +1656,13 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide)
{
case 'C':
case WXK_INSERT: //CTRL + C || CTRL + INS
- copySelectionToClipboard();
- return; // -> swallow event! don't allow default grid commands!
+ {
+ std::vector<const Grid*> gridRefs;
+ gridRefs.push_back(m_gridMainL);
+ gridRefs.push_back(m_gridMainR);
+ copySelectionToClipboard(gridRefs);
+ }
+ return; // -> swallow event! don't allow default grid commands!
}
else if (event.AltDown())
@@ -1813,7 +1828,11 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
leadRow = gridDataView->findRowFirstChild(&(dir->dirObj_));
}
else if (const TreeView::FilesNode* files = dynamic_cast<const TreeView::FilesNode*>(node.get()))
- leadRow = gridDataView->findRowDirect(files->firstFile_.getId());
+ {
+ assert(!files->filesAndLinks_.empty());
+ if (!files->filesAndLinks_.empty())
+ leadRow = gridDataView->findRowDirect(files->filesAndLinks_[0]->getId());
+ }
}
if (leadRow >= 0)
@@ -1829,8 +1848,8 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
}
//get selection on navigation tree and set corresponding markers on main grid
- std::vector<const HierarchyObject*> markedFiles; //mark files/symlinks directly within a container
- std::vector<const HierarchyObject*> markedContainer; //mark full container including child-objects
+ hash_set<const FileSystemObject*> markedFilesAndLinks; //mark files/symlinks directly
+ hash_set<const HierarchyObject*> markedContainer; //mark full container including child-objects
const std::vector<size_t>& selection = m_gridNavi->getSelectedRows();
std::for_each(selection.begin(), selection.end(),
@@ -1839,15 +1858,15 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
if (std::unique_ptr<TreeView::Node> node = treeDataView->getLine(row))
{
if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
- markedContainer.push_back(&(root->baseMap_));
+ markedContainer.insert(&(root->baseMap_));
else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
- markedContainer.push_back(&(dir->dirObj_));
+ markedContainer.insert(&(dir->dirObj_));
else if (const TreeView::FilesNode* files = dynamic_cast<const TreeView::FilesNode*>(node.get()))
- markedFiles.push_back(&(files->firstFile_.parent()));
+ markedFilesAndLinks.insert(files->filesAndLinks_.begin(), files->filesAndLinks_.end());
}
});
- gridview::setNavigationMarker(*m_gridMainL, std::move(markedFiles), std::move(markedContainer));
+ gridview::setNavigationMarker(*m_gridMainL, std::move(markedFilesAndLinks), std::move(markedContainer));
event.Skip();
}
@@ -2068,11 +2087,11 @@ void MainDialog::excludeExtension(const Zstring& extension)
//add to filter config
Zstring& excludeFilter = currentCfg.mainCfg.globalFilter.excludeFilter;
- if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr(";")))
+ if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr(";")) && !endsWith(excludeFilter, Zstr("\n")))
excludeFilter += Zstr("\n");
excludeFilter += newExclude + Zstr(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line
- updateFilterButtons();
+ updateGlobalFilterButton();
//do not fully apply filter, just exclude new items
std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); });
@@ -2093,7 +2112,7 @@ void MainDialog::excludeShortname(const FileSystemObject& fsObj)
excludeFilter += Zstr("\n");
excludeFilter += newExclude;
- updateFilterButtons();
+ updateGlobalFilterButton();
//do not fully apply filter, just exclude new items
std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); });
@@ -2127,7 +2146,7 @@ void MainDialog::excludeItems(const std::vector<FileSystemObject*>& selection)
excludeFilter += Zstr("\n");
excludeFilter += newExclude;
- updateFilterButtons();
+ updateGlobalFilterButton();
//do not fully apply filter, just exclude new items
std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); });
@@ -2305,9 +2324,9 @@ void MainDialog::OnSyncSettingsContext(wxMouseEvent& event)
const auto currentVar = getConfig().mainCfg.syncCfg.directionCfg.var;
menu.addRadio(_("<- Two way ->"), [&] { setVariant(DirectionConfig::AUTOMATIC); }, currentVar == DirectionConfig::AUTOMATIC);
- menu.addRadio(_("Mirror ->>") , [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR);
- menu.addRadio(_("Update ->") , [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE);
- menu.addRadio(_("Custom") , [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM);
+ menu.addRadio(_("Mirror ->>") , [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR);
+ menu.addRadio(_("Update ->") , [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE);
+ menu.addRadio(_("Custom") , [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM);
menu.popup(*this);
}
@@ -2315,7 +2334,7 @@ void MainDialog::OnSyncSettingsContext(wxMouseEvent& event)
void MainDialog::onNaviPanelFilesDropped(FileDropEvent& event)
{
- loadConfiguration(event.getFiles());
+ loadConfiguration(toZ(event.getFiles()));
event.Skip();
}
@@ -2335,31 +2354,17 @@ void MainDialog::onDirManualCorrection(wxCommandEvent& event)
}
-wxString getFormattedHistoryElement(const wxString& filename)
+wxString getFormattedHistoryElement(const Zstring& filename)
{
- wxString output = afterLast(filename, utfCvrtTo<wxString>(FILE_NAME_SEPARATOR));
- if (endsWith(output, L".ffs_gui"))
- output = beforeLast(output, L'.');
- return output;
+ Zstring output = afterLast(filename, FILE_NAME_SEPARATOR);
+ if (endsWith(output, Zstr(".ffs_gui")))
+ output = beforeLast(output, Zstr('.'));
+ return utfCvrtTo<wxString>(output);
}
-void MainDialog::addFileToCfgHistory(const std::vector<wxString>& filenames)
+void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filenames)
{
- //check existence of all config files in parallel!
- std::list<boost::unique_future<bool>> fileEx;
- std::for_each(filenames.begin(), filenames.end(),
- [&](const wxString& filename)
- {
- const Zstring file = toZ(filename); //convert to Zstring first: we don't want to pass wxString by value and risk MT issues!
- fileEx.push_back(zen::async2<bool>([=]() { return zen::fileExists(file); }));
- });
-
- //potentially slow network access: give all checks 500ms to finish
- wait_for_all_timed(fileEx.begin(), fileEx.end(), boost::posix_time::milliseconds(500));
-
- //------------------------------------------------------------------------------------------
-
//determine highest "last use" index number of m_listBoxHistory
int lastUseIndexMax = 0;
for (int i = 0; i < static_cast<int>(m_listBoxHistory->GetCount()); ++i)
@@ -2369,39 +2374,36 @@ void MainDialog::addFileToCfgHistory(const std::vector<wxString>& filenames)
std::deque<bool> selections(m_listBoxHistory->GetCount()); //items to select after update of history list
- auto futIter = fileEx.begin();
- for (auto it = filenames.begin(); it != filenames.end(); ++it, ++futIter)
- if (!futIter->is_ready() || futIter->get()) //only existing files should be included in the list (and also those with no result yet)
- {
- const wxString& filename = *it;
+ for (auto it = filenames.begin(); it != filenames.end(); ++it)
+ {
+ const Zstring& filename = *it;
- warn_static("perf!!!!? samePhysicalFile : andere setllen?")
+ //Do we need to additionally check for aliases of the same physical files here? (and aliases for lastRunConfigName?)
- auto findItem = [&]() -> int
- {
- const int itemCount = static_cast<int>(m_listBoxHistory->GetCount());
- for (int i = 0; i < itemCount; ++i)
- if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
- if (samePhysicalFile(toZ(filename), toZ(histData->cfgFile_)))
- return i;
- return -1;
- };
-
- const int itemPos = findItem();
- if (itemPos >= 0) //update
- {
- if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(itemPos)))
- histData->lastUseIndex_ = ++lastUseIndexMax;
- selections[itemPos] = true;
- }
- else //insert
- {
- const wxString label = samePhysicalFile(toZ(lastRunConfigName()), toZ(filename)) ? //give default config file a different name
- _("<Last session>") : getFormattedHistoryElement(filename);
- const int newPos = m_listBoxHistory->Append(label, new wxClientHistoryData(filename, ++lastUseIndexMax)); //*insert* into sorted list
- selections.insert(selections.begin() + newPos, true);
- }
+ const int itemPos = [&]() -> int
+ {
+ const int itemCount = static_cast<int>(m_listBoxHistory->GetCount());
+ for (int i = 0; i < itemCount; ++i)
+ if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
+ if (EqualFilename()(filename, histData->cfgFile_))
+ return i;
+ return -1;
+ }();
+
+ if (itemPos >= 0) //update
+ {
+ if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(itemPos)))
+ histData->lastUseIndex_ = ++lastUseIndexMax;
+ selections[itemPos] = true;
}
+ else //insert
+ {
+ const wxString label = EqualFilename()(filename, lastRunConfigName()) ? //give default config file a different name
+ _("<Last session>") : getFormattedHistoryElement(filename);
+ const int newPos = m_listBoxHistory->Append(label, new wxClientHistoryData(filename, ++lastUseIndexMax)); //*insert* into sorted list
+ selections.insert(selections.begin() + newPos, true);
+ }
+ }
assert(selections.size() == m_listBoxHistory->GetCount());
@@ -2418,9 +2420,61 @@ void MainDialog::addFileToCfgHistory(const std::vector<wxString>& filenames)
}
+void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filenames)
+{
+ //don't use wxString: NOT thread-safe! (e.g. non-atomic ref-count)
+
+ auto getMissingFilesAsync = [filenames]() -> std::vector<Zstring>
+ {
+ //boost::this_thread::sleep(boost::posix_time::millisec(5000));
+
+ //check existence of all config files in parallel!
+ std::list<boost::unique_future<bool>> fileEx;
+
+ for (auto it = filenames.begin(); it != filenames.end(); ++it) //avoid VC11 compiler issues with std::for_each
+ {
+ const Zstring filename = *it; //don't reference iterator in lambda!
+ fileEx.push_back(zen::async2<bool>([=] { return fileExists(filename); }));
+ }
+
+ //potentially slow network access => limit maximum wait time!
+ wait_for_all_timed(fileEx.begin(), fileEx.end(), boost::posix_time::milliseconds(1000));
+
+ std::vector<Zstring> missingFiles;
+
+ auto itFut = fileEx.begin();
+ for (auto it = filenames.begin(); it != filenames.end(); ++it, ++itFut)
+ if (itFut->is_ready() && !itFut->get()) //remove only files that are confirmed to be non-existent
+ missingFiles.push_back(*it);
+
+ return missingFiles;
+ };
+
+ asyncTasks.add(getMissingFilesAsync,
+ [this](const std::vector<Zstring>& files) { removeCfgHistoryItems(files); });
+ startProcessingAsyncTasks();
+}
+
+
+void MainDialog::removeCfgHistoryItems(const std::vector<Zstring>& filenames)
+{
+ std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename)
+ {
+ const int histSize = m_listBoxHistory->GetCount();
+ for (int i = 0; i < histSize; ++i)
+ if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
+ if (EqualFilename()(filename, histData->cfgFile_))
+ {
+ m_listBoxHistory->Delete(i);
+ break;
+ }
+ });
+}
+
+
void MainDialog::updateUnsavedCfgStatus()
{
- const wxString activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString();
+ const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring();
const bool haveUnsavedCfg = lastConfigurationSaved != getConfig();
@@ -2446,15 +2500,15 @@ void MainDialog::updateUnsavedCfgStatus()
title += L'*';
if (!activeCfgFilename.empty())
- title += activeCfgFilename;
+ title += toWx(activeCfgFilename);
else if (activeConfigFiles.size() > 1)
{
#ifdef _MSC_VER
#pragma warning(disable:4428) // VC wrongly issues warning C4428: universal-character-name encountered in source
#endif
const wchar_t* EM_DASH = L" \u2014 ";
- title += xmlAccess::extractJobName(toZ(activeConfigFiles[0]));
- std::for_each(activeConfigFiles.begin() + 1, activeConfigFiles.end(), [&](const wxString& filename) { title += EM_DASH + xmlAccess::extractJobName(toZ(filename)); });
+ title += xmlAccess::extractJobName(activeConfigFiles[0]);
+ std::for_each(activeConfigFiles.begin() + 1, activeConfigFiles.end(), [&](const Zstring& filename) { title += EM_DASH + xmlAccess::extractJobName(filename); });
}
else
title += L"FreeFileSync - " + _("Folder Comparison and Synchronization");
@@ -2465,7 +2519,7 @@ void MainDialog::updateUnsavedCfgStatus()
void MainDialog::OnConfigSave(wxCommandEvent& event)
{
- const wxString activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString();
+ const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring();
//if we work on a single named configuration document: save directly if changed
//else: always show file dialog
@@ -2473,7 +2527,7 @@ void MainDialog::OnConfigSave(wxCommandEvent& event)
{
using namespace xmlAccess;
- switch (getXmlType(utfCvrtTo<Zstring>(activeCfgFilename))) //throw()
+ switch (getXmlType(activeCfgFilename)) //throw()
{
case XML_TYPE_GUI:
trySaveConfig(&activeCfgFilename);
@@ -2504,18 +2558,18 @@ void MainDialog::OnSaveAsBatchJob(wxCommandEvent& event)
}
-bool MainDialog::trySaveConfig(const wxString* fileNameGui) //return true if saved successfully
+bool MainDialog::trySaveConfig(const Zstring* fileNameGui) //return true if saved successfully
{
- wxString targetFilename;
+ Zstring targetFilename;
if (fileNameGui)
{
targetFilename = *fileNameGui;
- assert(endsWith(targetFilename, L".ffs_gui"));
+ assert(endsWith(targetFilename, Zstr(".ffs_gui")));
}
else
{
- wxString defaultFileName = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : L"SyncSettings.ffs_gui";
+ wxString defaultFileName = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? toWx(activeConfigFiles[0]) : L"SyncSettings.ffs_gui";
//attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config!
if (endsWith(defaultFileName, L".ffs_batch"))
replace(defaultFileName, L".ffs_batch", L".ffs_gui", false);
@@ -2528,14 +2582,14 @@ bool MainDialog::trySaveConfig(const wxString* fileNameGui) //return true if sav
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
return false;
- targetFilename = filePicker.GetPath();
+ targetFilename = toZ(filePicker.GetPath());
}
const xmlAccess::XmlGuiConfig guiCfg = getConfig();
try
{
- xmlAccess::writeConfig(guiCfg, toZ(targetFilename)); //throw FfsXmlError
+ xmlAccess::writeConfig(guiCfg, targetFilename); //throw FfsXmlError
setLastUsedConfig(targetFilename, guiCfg);
flashStatusInformation(_("Configuration saved!"));
@@ -2549,25 +2603,25 @@ bool MainDialog::trySaveConfig(const wxString* fileNameGui) //return true if sav
}
-bool MainDialog::trySaveBatchConfig(const wxString* fileNameBatch)
+bool MainDialog::trySaveBatchConfig(const Zstring* fileNameBatch)
{
//essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "hideExcludedItems, showSyncAction" is negliable
const xmlAccess::XmlGuiConfig guiCfg = getConfig();
- wxString targetFilename;
+ Zstring targetFilename;
xmlAccess::XmlBatchConfig batchCfg;
if (fileNameBatch)
{
targetFilename = *fileNameBatch;
- batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, toZ(*fileNameBatch));
- assert(endsWith(targetFilename, L".ffs_batch"));
+ batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, *fileNameBatch);
+ assert(endsWith(targetFilename, Zstr(".ffs_batch")));
}
else
{
- const wxString activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString();
- batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, toZ(activeCfgFilename));
+ const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring();
+ batchCfg = convertGuiToBatchPreservingExistingBatch(guiCfg, activeCfgFilename);
//let user change batch config: this should change batch-exclusive settings only, else the "setLastUsedConfig" below would be somewhat of a lie
if (!customizeBatchConfig(this,
@@ -2576,7 +2630,7 @@ bool MainDialog::trySaveBatchConfig(const wxString* fileNameBatch)
globalCfg.gui.onCompletionHistoryMax))
return false;
- wxString defaultFileName = !activeCfgFilename.empty() ? activeCfgFilename : L"BatchRun.ffs_batch";
+ wxString defaultFileName = !activeCfgFilename.empty() ? toWx(activeCfgFilename) : L"BatchRun.ffs_batch";
//attention: activeConfigFiles may be a *.ffs_gui file! We don't want to overwrite it with a BATCH config!
if (endsWith(defaultFileName, L".ffs_gui"))
replace(defaultFileName, L".ffs_gui", L".ffs_batch");
@@ -2589,12 +2643,12 @@ bool MainDialog::trySaveBatchConfig(const wxString* fileNameBatch)
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
return false;
- targetFilename = filePicker.GetPath();
+ targetFilename = toZ(filePicker.GetPath());
}
try
{
- xmlAccess::writeConfig(batchCfg, toZ(targetFilename)); //throw FfsXmlError
+ xmlAccess::writeConfig(batchCfg, targetFilename); //throw FfsXmlError
setLastUsedConfig(targetFilename, guiCfg); //[!] behave as if we had saved guiCfg
flashStatusInformation(_("Configuration saved!"));
@@ -2612,7 +2666,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
{
if (lastConfigurationSaved != getConfig())
{
- const wxString activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString();
+ const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring();
//notify user about changed settings
if (globalCfg.optDialogs.popupOnConfigChange)
@@ -2623,8 +2677,8 @@ bool MainDialog::saveOldConfig() //return false on user abort
switch (showQuestionDlg(this,
ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_NO | ReturnQuestionDlg::BUTTON_CANCEL,
- replaceCpy(_("Do you want to save changes to %x?"), L"%x", fmtFileName(afterLast(utfCvrtTo<Zstring>(activeCfgFilename), FILE_NAME_SEPARATOR))),
- QuestConfig().setCaption(activeCfgFilename).
+ replaceCpy(_("Do you want to save changes to %x?"), L"%x", fmtFileName(afterLast(activeCfgFilename, FILE_NAME_SEPARATOR))),
+ QuestConfig().setCaption(toWx(activeCfgFilename)).
setLabelYes(_("&Save")).
setLabelNo(_("Do&n't save")).
showCheckBox(neverSave, _("Never save changes"))))
@@ -2632,7 +2686,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
case ReturnQuestionDlg::BUTTON_YES:
using namespace xmlAccess;
- switch (getXmlType(utfCvrtTo<Zstring>(activeCfgFilename))) //throw()
+ switch (getXmlType(activeCfgFilename)) //throw()
{
case XML_TYPE_GUI:
return trySaveConfig(&activeCfgFilename);
@@ -2654,7 +2708,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
}
//discard current reference file(s), this ensures next app start will load <last session> instead of the original non-modified config selection
- setLastUsedConfig(std::vector<wxString>(), lastConfigurationSaved);
+ setLastUsedConfig(std::vector<Zstring>(), lastConfigurationSaved);
//this seems to make theoretical sense also: the job of this function is to make sure current (volatile) config and reference file name are in sync
// => if user does not save cfg, it is not attached to a physical file names anymore!
}
@@ -2664,11 +2718,11 @@ bool MainDialog::saveOldConfig() //return false on user abort
void MainDialog::OnConfigLoad(wxCommandEvent& event)
{
- const wxString activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString();
+ const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring();
wxFileDialog filePicker(this,
wxEmptyString,
- beforeLast(activeCfgFilename, utfCvrtTo<wxString>(FILE_NAME_SEPARATOR)), //set default dir: empty string if "activeConfigFiles" is empty or has no path separator
+ toWx(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR)), //set default dir: empty string if "activeConfigFiles" is empty or has no path separator
wxEmptyString,
wxString(L"FreeFileSync (*.ffs_gui;*.ffs_batch)|*.ffs_gui;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*",
wxFD_OPEN | wxFD_MULTIPLE);
@@ -2677,9 +2731,9 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event)
{
wxArrayString tmp;
filePicker.GetPaths(tmp);
- std::vector<wxString> fileNames(tmp.begin(), tmp.end());
+ std::vector<wxString> filenames(tmp.begin(), tmp.end());
- loadConfiguration(fileNames);
+ loadConfiguration(toZ(filenames));
}
}
@@ -2689,7 +2743,16 @@ void MainDialog::OnConfigNew(wxCommandEvent& event)
if (!saveOldConfig()) //notify user about changed settings
return;
- setConfig(xmlAccess::XmlGuiConfig(), std::vector<wxString>());
+ xmlAccess::XmlGuiConfig newConfig;
+
+ //add default exclusion filter: this is only ever relevant when creating new configurations!
+ //a default XmlGuiConfig does not need these user-specific exclusions!
+ Zstring& excludeFilter = newConfig.mainCfg.globalFilter.excludeFilter;
+ if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n")))
+ excludeFilter += Zstr("\n");
+ excludeFilter += globalCfg.gui.defaultExclusionFilter;
+
+ setConfig(newConfig, std::vector<Zstring>());
}
@@ -2698,7 +2761,7 @@ void MainDialog::OnLoadFromHistory(wxCommandEvent& event)
wxArrayInt selections;
m_listBoxHistory->GetSelections(selections);
- std::vector<wxString> filenames;
+ std::vector<Zstring> filenames;
std::for_each(selections.begin(), selections.end(),
[&](int pos)
{
@@ -2709,7 +2772,7 @@ void MainDialog::OnLoadFromHistory(wxCommandEvent& event)
if (!filenames.empty())
loadConfiguration(filenames);
- //user changed m_listBoxHistory selection so it's this method's responsibility to synchronize with activeConfigFiles
+ //user changed m_listBoxHistory selection so it's this method's responsibility to synchronize with activeConfigFiles:
//- if user cancelled saving old config
//- there's an error loading new config
//- filenames is empty and user tried to unselect the current config
@@ -2722,9 +2785,8 @@ void MainDialog::OnLoadFromHistoryDoubleClick(wxCommandEvent& event)
wxArrayInt selections;
m_listBoxHistory->GetSelections(selections);
- std::vector<wxString> filenames;
- std::for_each(selections.begin(), selections.end(),
- [&](int pos)
+ std::vector<Zstring> filenames;
+ std::for_each(selections.begin(), selections.end(), [&](int pos)
{
if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(pos)))
filenames.push_back(histData->cfgFile_);
@@ -2744,7 +2806,7 @@ void MainDialog::OnLoadFromHistoryDoubleClick(wxCommandEvent& event)
}
-bool MainDialog::loadConfiguration(const std::vector<wxString>& filenames)
+bool MainDialog::loadConfiguration(const std::vector<Zstring>& filenames)
{
if (filenames.empty())
return true;
@@ -2757,7 +2819,7 @@ bool MainDialog::loadConfiguration(const std::vector<wxString>& filenames)
try
{
//allow reading batch configurations also
- xmlAccess::readAnyConfig(toZ(filenames), newGuiCfg); //throw FfsXmlError
+ xmlAccess::readAnyConfig(filenames, newGuiCfg); //throw FfsXmlError
setConfig(newGuiCfg, filenames);
//flashStatusInformation(_("Configuration loaded!")); -> irrelevant!?
@@ -2783,14 +2845,8 @@ void MainDialog::deleteSelectedCfgHistoryItems()
m_listBoxHistory->GetSelections(tmp);
std::set<int> selections(tmp.begin(), tmp.end()); //sort ascending!
-
- int shift = 0;
- std::for_each(selections.begin(), selections.end(),
- [&](int pos)
- {
- m_listBoxHistory->Delete(pos + shift);
- --shift;
- });
+ //delete starting with high positions:
+ std::for_each(selections.rbegin(), selections.rend(), [&](int pos) { m_listBoxHistory->Delete(pos); });
//set active selection on next element to allow "batch-deletion" by holding down DEL key
if (!selections.empty() && m_listBoxHistory->GetCount() > 0)
@@ -2876,15 +2932,15 @@ void MainDialog::onSetSyncDirection(SyncDirectionEvent& event)
}
-void MainDialog::setLastUsedConfig(const wxString& filename, const xmlAccess::XmlGuiConfig& guiConfig)
+void MainDialog::setLastUsedConfig(const Zstring& filename, const xmlAccess::XmlGuiConfig& guiConfig)
{
- std::vector<wxString> filenames;
+ std::vector<Zstring> filenames;
filenames.push_back(filename);
setLastUsedConfig(filenames, guiConfig);
}
-void MainDialog::setLastUsedConfig(const std::vector<wxString>& filenames,
+void MainDialog::setLastUsedConfig(const std::vector<Zstring>& filenames,
const xmlAccess::XmlGuiConfig& guiConfig)
{
activeConfigFiles = filenames;
@@ -2896,7 +2952,7 @@ void MainDialog::setLastUsedConfig(const std::vector<wxString>& filenames,
}
-void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector<wxString>& referenceFiles)
+void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector<Zstring>& referenceFiles)
{
currentCfg = newGuiCfg;
@@ -2905,11 +2961,11 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::
//(re-)set view filter buttons
setViewFilterDefault();
- updateFilterButtons();
+ updateGlobalFilterButton();
//set first folder pair
- firstFolderPair->setValues(toWx(currentCfg.mainCfg.firstPair.leftDirectory),
- toWx(currentCfg.mainCfg.firstPair.rightDirectory),
+ firstFolderPair->setValues(currentCfg.mainCfg.firstPair.leftDirectory,
+ currentCfg.mainCfg.firstPair.rightDirectory,
currentCfg.mainCfg.firstPair.altCmpConfig,
currentCfg.mainCfg.firstPair.altSyncConfig,
currentCfg.mainCfg.firstPair.localFilter);
@@ -2946,8 +3002,8 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::
inline
FolderPairEnh getEnhancedPair(const DirectoryPair* panel)
{
- return FolderPairEnh(toZ(panel->getLeftDir()),
- toZ(panel->getRightDir()),
+ return FolderPairEnh(panel->getLeftDir(),
+ panel->getRightDir(),
panel->getAltCompConfig(),
panel->getAltSyncConfig(),
panel->getAltFilterConfig());
@@ -2961,8 +3017,8 @@ xmlAccess::XmlGuiConfig MainDialog::getConfig() const
//load settings whose ownership lies not in currentCfg:
//first folder pair
- guiCfg.mainCfg.firstPair = FolderPairEnh(toZ(firstFolderPair->getLeftDir()),
- toZ(firstFolderPair->getRightDir()),
+ guiCfg.mainCfg.firstPair = FolderPairEnh(firstFolderPair->getLeftDir(),
+ firstFolderPair->getRightDir(),
firstFolderPair->getAltCompConfig(),
firstFolderPair->getAltSyncConfig(),
firstFolderPair->getAltFilterConfig());
@@ -2979,9 +3035,9 @@ xmlAccess::XmlGuiConfig MainDialog::getConfig() const
}
-const wxString& MainDialog::lastRunConfigName()
+const Zstring& MainDialog::lastRunConfigName()
{
- static wxString instance = toWx(zen::getConfigDir()) + L"LastRun.ffs_gui";
+ static Zstring instance = zen::getConfigDir() + Zstr("LastRun.ffs_gui");
return instance;
}
@@ -3021,7 +3077,7 @@ void MainDialog::OnConfigureFilter(wxCommandEvent& event)
true, //is main filter dialog
currentCfg.mainCfg.globalFilter) == ReturnSmallDlg::BUTTON_OKAY)
{
- updateFilterButtons(); //refresh global filter icon
+ updateGlobalFilterButton(); //refresh global filter icon
applyFilterConfig(); //re-apply filter
}
@@ -3031,17 +3087,28 @@ void MainDialog::OnConfigureFilter(wxCommandEvent& event)
void MainDialog::OnGlobalFilterContext(wxMouseEvent& event)
{
- ContextMenu menu;
-
auto clearFilter = [&]
{
currentCfg.mainCfg.globalFilter = FilterConfig();
-
- updateFilterButtons(); //refresh global filter icon
+ updateGlobalFilterButton(); //refresh global filter icon
applyFilterConfig(); //re-apply filter
};
- menu.addItem( _("Clear filter settings"), clearFilter, nullptr, !isNullFilter(currentCfg.mainCfg.globalFilter));
+ auto copyFilter = [&] { filterCfgOnClipboard = make_unique<FilterConfig>(currentCfg.mainCfg.globalFilter); };
+ auto pasteFilter = [&]
+ {
+ if (filterCfgOnClipboard)
+ {
+ currentCfg.mainCfg.globalFilter = *filterCfgOnClipboard;
+ updateGlobalFilterButton(); //refresh global filter icon
+ applyFilterConfig(); //re-apply filter
+ }
+ };
+ ContextMenu menu;
+ menu.addItem( _("Clear filter settings"), clearFilter, nullptr, !isNullFilter(currentCfg.mainCfg.globalFilter));
+ menu.addSeparator();
+ menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(currentCfg.mainCfg.globalFilter));
+ menu.addItem( _("Paste"), pasteFilter, nullptr, filterCfgOnClipboard.get() != nullptr);
menu.popup(*this);
}
@@ -3063,8 +3130,7 @@ wxBitmap buttonPressed(const std::string& name)
{
wxBitmap background = getResourceImage(L"buttonPressed");
return mirrorIfRtl(
- layOver(
- getResourceImage(utfCvrtTo<wxString>(name)), background));
+ layOver(getResourceImage(utfCvrtTo<wxString>(name)), background));
}
@@ -3084,73 +3150,59 @@ void MainDialog::initViewFilterButtons()
//compare result buttons
m_bpButtonShowLeftOnly->init(buttonPressed("leftOnly"),
buttonReleased("leftOnly"),
- _("Hide files that exist on left side only"),
_("Show files that exist on left side only"));
m_bpButtonShowRightOnly->init(buttonPressed("rightOnly"),
buttonReleased("rightOnly"),
- _("Hide files that exist on right side only"),
_("Show files that exist on right side only"));
m_bpButtonShowLeftNewer->init(buttonPressed("leftNewer"),
buttonReleased("leftNewer"),
- _("Hide files that are newer on left"),
_("Show files that are newer on left"));
m_bpButtonShowRightNewer->init(buttonPressed("rightNewer"),
buttonReleased("rightNewer"),
- _("Hide files that are newer on right"),
_("Show files that are newer on right"));
m_bpButtonShowEqual->init(buttonPressed("equal"),
buttonReleased("equal"),
- _("Hide files that are equal"),
_("Show files that are equal"));
m_bpButtonShowDifferent->init(buttonPressed("different"),
buttonReleased("different"),
- _("Hide files that are different"),
_("Show files that are different"));
m_bpButtonShowConflict->init(buttonPressed("conflict"),
buttonReleased("conflict"),
- _("Hide conflicts"),
_("Show conflicts"));
//sync preview buttons
m_bpButtonShowCreateLeft->init(buttonPressed("createLeft"),
buttonReleased("createLeft"),
- _("Hide files that will be created on the left side"),
_("Show files that will be created on the left side"));
m_bpButtonShowCreateRight->init(buttonPressed("createRight"),
buttonReleased("createRight"),
- _("Hide files that will be created on the right side"),
_("Show files that will be created on the right side"));
m_bpButtonShowDeleteLeft->init(buttonPressed("deleteLeft"),
buttonReleased("deleteLeft"),
- _("Hide files that will be deleted on the left side"),
_("Show files that will be deleted on the left side"));
m_bpButtonShowDeleteRight->init(buttonPressed("deleteRight"),
buttonReleased("deleteRight"),
- _("Hide files that will be deleted on the right side"),
_("Show files that will be deleted on the right side"));
m_bpButtonShowUpdateLeft->init(buttonPressed("updateLeft"),
buttonReleased("updateLeft"),
- _("Hide files that will be overwritten on left side"),
_("Show files that will be overwritten on left side"));
m_bpButtonShowUpdateRight->init(buttonPressed("updateRight"),
buttonReleased("updateRight"),
- _("Hide files that will be overwritten on right side"),
_("Show files that will be overwritten on right side"));
m_bpButtonShowDoNothing->init(buttonPressed("none"),
buttonReleased("none"),
- _("Hide files that won't be copied"),
_("Show files that won't be copied"));
}
@@ -3212,7 +3264,7 @@ void MainDialog::OnViewButtonRightClick(wxMouseEvent& event)
}
-void MainDialog::updateFilterButtons()
+void MainDialog::updateGlobalFilterButton()
{
//global filter: test for Null-filter
if (!isNullFilter(currentCfg.mainCfg.globalFilter))
@@ -3225,12 +3277,6 @@ void MainDialog::updateFilterButtons()
setImage(*m_bpButtonFilter, greyScale(getResourceImage(L"filter")));
m_bpButtonFilter->SetToolTip(_("No filter selected"));
}
-
- //update local filter buttons
- firstFolderPair->refreshButtons();
-
- std::for_each(additionalFolderPairs.begin(), additionalFolderPairs.end(),
- [&](DirectoryPair* dirPair) { dirPair->refreshButtons(); });
}
@@ -3261,24 +3307,15 @@ void MainDialog::OnCompare(wxCommandEvent& event)
const std::vector<zen::FolderPairCfg> cmpConfig = zen::extractCompareCfg(getConfig().mainCfg);
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
- std::unique_ptr<LockHolder> dummy2;
- if (globalCfg.createLockFile)
- {
- std::vector<Zstring> dirnames;
- std::for_each(cmpConfig.begin(), cmpConfig.end(),
- [&](const FolderPairCfg& fpCfg)
- {
- dirnames.push_back(fpCfg.leftDirectoryFmt);
- dirnames.push_back(fpCfg.rightDirectoryFmt);
- });
- dummy2 = make_unique<LockHolder>(dirnames, statusHandler, true); //allow pw prompt
- }
+ std::unique_ptr<LockHolder> dirLocks;
//COMPARE DIRECTORIES
compare(globalCfg.fileTimeTolerance,
globalCfg.optDialogs,
true, //allow pw prompt
globalCfg.runWithBackgroundPriority,
+ globalCfg.createLockFile,
+ dirLocks,
cmpConfig,
folderCmp,
statusHandler); //throw GuiAbortProcess
@@ -3300,12 +3337,12 @@ void MainDialog::OnCompare(wxCommandEvent& event)
m_gridNavi->clearSelection();
//play (optional) sound notification after sync has completed (GUI and batch mode)
- const wxString soundFile = toWx(zen::getResourceDir()) + L"Compare_Complete.wav";
- if (fileExists(toZ(soundFile)))
- wxSound::Play(soundFile, wxSOUND_ASYNC);
+ const Zstring soundFile = zen::getResourceDir() + Zstr("Compare_Complete.wav");
+ if (fileExists(soundFile))
+ wxSound::Play(toWx(soundFile), wxSOUND_ASYNC);
//add to folder history after successful comparison only
- folderHistoryLeft ->addItem(toZ(m_directoryLeft->GetValue()));
+ folderHistoryLeft ->addItem(toZ(m_directoryLeft ->GetValue()));
folderHistoryRight->addItem(toZ(m_directoryRight->GetValue()));
//prepare status information
@@ -3474,7 +3511,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
try
{
//PERF_START;
- const wxString activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString();
+ const Zstring activeCfgFilename = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstring();
const auto& guiCfg = getConfig();
@@ -3482,21 +3519,23 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
SyncStatusHandler statusHandler(this, //throw GuiAbortProcess
globalCfg.lastSyncsLogFileSizeMax,
currentCfg.handleError,
- xmlAccess::extractJobName(utfCvrtTo<Zstring>(activeCfgFilename)),
+ xmlAccess::extractJobName(activeCfgFilename),
guiCfg.mainCfg.onCompletion,
globalCfg.gui.onCompletionHistory);
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
- std::unique_ptr<LockHolder> dummy2;
+ std::unique_ptr<LockHolder> dirLocks;
if (globalCfg.createLockFile)
{
- std::vector<Zstring> dirnames;
+ std::set<Zstring, LessFilename> dirnamesExisting;
for (auto it = begin(folderCmp); it != end(folderCmp); ++it)
{
- dirnames.push_back(it->getBaseDirPf<LEFT_SIDE >());
- dirnames.push_back(it->getBaseDirPf<RIGHT_SIDE>());
+ if (it->isExisting<LEFT_SIDE>()) //do NOT check directory existence again!
+ dirnamesExisting.insert(it->getBaseDirPf<LEFT_SIDE >());
+ if (it->isExisting<RIGHT_SIDE>())
+ dirnamesExisting.insert(it->getBaseDirPf<RIGHT_SIDE>());
}
- dummy2 = make_unique<LockHolder>(dirnames, statusHandler, true); //allow pw prompt
+ dirLocks = make_unique<LockHolder>(dirnamesExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler);
}
//START SYNCHRONIZATION
@@ -3818,8 +3857,8 @@ void MainDialog::OnAddFolderPair(wxCommandEvent& event)
//clear first pair
const FolderPairEnh cfgEmpty;
- firstFolderPair->setValues(toWx(cfgEmpty.leftDirectory),
- toWx(cfgEmpty.rightDirectory),
+ firstFolderPair->setValues(cfgEmpty.leftDirectory,
+ cfgEmpty.rightDirectory,
cfgEmpty.altCmpConfig,
cfgEmpty.altSyncConfig,
cfgEmpty.localFilter);
@@ -3839,8 +3878,8 @@ void MainDialog::OnRemoveTopFolderPair(wxCommandEvent& event)
const FolderPairEnh cfgSecond = getEnhancedPair(additionalFolderPairs[0]);
//reset first pair
- firstFolderPair->setValues(toWx(cfgSecond.leftDirectory),
- toWx(cfgSecond.rightDirectory),
+ firstFolderPair->setValues(cfgSecond.leftDirectory,
+ cfgSecond.rightDirectory,
cfgSecond.altCmpConfig,
cfgSecond.altSyncConfig,
cfgSecond.localFilter);
@@ -3897,7 +3936,8 @@ void MainDialog::updateGuiForFolderPair()
addPairOptimalHeight = std::max(addPairOptimalHeight, addPairMinimalHeight); //implicitly handle corrupted values for "maxFolderPairsVisible"
}
- const int firstPairHeight = m_panelDirectoryPairs->ClientToWindowSize(m_panelTopLeft->GetSize()).GetHeight(); //include m_panelDirectoryPairs window borders!
+ const int firstPairHeight = std::max(m_panelDirectoryPairs->ClientToWindowSize(m_panelTopLeft ->GetSize()).GetHeight(), //include m_panelDirectoryPairs window borders!
+ m_panelDirectoryPairs->ClientToWindowSize(m_panelTopMiddle->GetSize()).GetHeight()); //
//########################################################################################################################
//wxAUI hack: set minimum height to desired value, then call wxAuiPaneInfo::Fixed() to apply it
@@ -3963,8 +4003,8 @@ void MainDialog::addFolderPair(const std::vector<FolderPairEnh>& newPairs, bool
updateGuiForFolderPair();
for (auto it = newPairs.begin(); it != newPairs.end(); ++it)//set alternate configuration
- newEntries[it - newPairs.begin()]->setValues(toWx(it->leftDirectory),
- toWx(it->rightDirectory),
+ newEntries[it - newPairs.begin()]->setValues(it->leftDirectory,
+ it->rightDirectory,
it->altCmpConfig,
it->altSyncConfig,
it->localFilter);
@@ -4198,20 +4238,26 @@ void MainDialog::OnMenuCheckVersion(wxCommandEvent& event)
}
+void MainDialog::OnMenuCheckVersionAutomatically(wxCommandEvent& event)
+{
+ globalCfg.gui.lastUpdateCheck = globalCfg.gui.lastUpdateCheck == -1 ? 0 : -1;
+ m_menuItemCheckVersionAuto->Check(globalCfg.gui.lastUpdateCheck != -1);
+ zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck, [&] { flashStatusInformation(_("Searching for program updates...")); });
+}
+
+
void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event)
{
//execute just once per startup!
Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this);
if (manualProgramUpdateRequired())
- zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck);
+ zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck, [&] { flashStatusInformation(_("Searching for program updates...")); });
}
void MainDialog::OnLayoutWindowAsync(wxIdleEvent& event)
{
- warn_static("harmonize with async version check?")
-
//execute just once per startup!
Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnLayoutWindowAsync), nullptr, this);
diff --git a/ui/main_dlg.h b/ui/main_dlg.h
index e1b2d44f..70f78e93 100644
--- a/ui/main_dlg.h
+++ b/ui/main_dlg.h
@@ -7,18 +7,19 @@
#ifndef MAINDIALOG_H
#define MAINDIALOG_H
-#include <memory>
#include <map>
#include <set>
-#include <wx/aui/aui.h>
-#include <zen/int64.h>
#include <stack>
+#include <memory>
+#include <zen/int64.h>
+#include <zen/async_task.h>
+#include <wx+/file_drop.h>
+#include <wx/aui/aui.h>
#include "gui_generated.h"
-#include "../lib/process_xml.h"
#include "custom_grid.h"
#include "tree_view.h"
-#include <wx+/file_drop.h>
#include "folder_history_box.h"
+#include "../lib/process_xml.h"
//class FolderHistory;
class DirectoryPair;
@@ -30,14 +31,14 @@ class MainDialog : public MainDialogGenerated
{
public:
//default behavior, application start
- static void create(const std::vector<wxString>& cfgFileNames); //cfgFileNames empty: restore last config; non-empty load/merge given set of config files
+ static void create(const std::vector<Zstring>& cfgFileNames); //cfgFileNames empty: restore last config; non-empty load/merge given set of config files
//load dynamically assembled config
static void create(const xmlAccess::XmlGuiConfig& guiCfg, bool startComparison);
//when switching language or switching from batch run to GUI on warnings
static void create(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<wxString>& referenceFiles,
+ const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit
bool startComparison);
@@ -48,12 +49,12 @@ public:
private:
static void create_impl(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<wxString>& referenceFiles,
+ const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison);
MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<wxString>& referenceFiles,
+ const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit
bool startComparison);
~MainDialog();
@@ -69,33 +70,35 @@ private:
friend class PanelMoveWindow;
//configuration load/save
- void setLastUsedConfig(const wxString& filename, const xmlAccess::XmlGuiConfig& guiConfig);
- void setLastUsedConfig(const std::vector<wxString>& filenames, const xmlAccess::XmlGuiConfig& guiConfig);
+ void setLastUsedConfig(const Zstring& filename, const xmlAccess::XmlGuiConfig& guiConfig);
+ void setLastUsedConfig(const std::vector<Zstring>& filenames, const xmlAccess::XmlGuiConfig& guiConfig);
xmlAccess::XmlGuiConfig getConfig() const;
- void setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector<wxString>& referenceFiles);
+ void setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector<Zstring>& referenceFiles);
void setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings); //messes with Maximize(), window sizes, so call just once!
xmlAccess::XmlGlobalSettings getGlobalCfgBeforeExit(); //destructive "get" thanks to "Iconize(false), Maximize(false)"
- bool loadConfiguration(const std::vector<wxString>& filenames); //return true if loaded successfully
+ bool loadConfiguration(const std::vector<Zstring>& filenames); //return true if loaded successfully
- bool trySaveConfig (const wxString* fileNameGui); //return true if saved successfully
- bool trySaveBatchConfig(const wxString* fileNameBatch); //
+ bool trySaveConfig (const Zstring* fileNameGui); //return true if saved successfully
+ bool trySaveBatchConfig(const Zstring* fileNameBatch); //
bool saveOldConfig(); //return false on user abort
- static const wxString& lastRunConfigName();
+ static const Zstring& lastRunConfigName();
xmlAccess::XmlGuiConfig lastConfigurationSaved; //support for: "Save changed configuration?" dialog
//used when saving configuration
- std::vector<wxString> activeConfigFiles; //name of currently loaded config file (may be more than 1)
+ std::vector<Zstring> activeConfigFiles; //name of currently loaded config file (may be more than 1)
- void updateFilterButtons(); //file exclusion
+ void updateGlobalFilterButton();
void initViewFilterButtons();
void setViewFilterDefault();
- void addFileToCfgHistory(const std::vector<wxString>& filenames); //= update/insert + apply selection
+ void addFileToCfgHistory(const std::vector<Zstring>& filenames); //= update/insert + apply selection
+ void removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filenames);
+ void removeCfgHistoryItems(const std::vector<Zstring>& filenames);
void addFolderPair(const std::vector<zen::FolderPairEnh>& newPairs, bool addFront = false);
void removeAddFolderPair(size_t pos);
@@ -117,19 +120,22 @@ private:
void setSyncDirManually(const std::vector<zen::FileSystemObject*>& selection, zen::SyncDirection direction);
void setFilterManually(const std::vector<zen::FileSystemObject*>& selection, bool setIncluded);
- void copySelectionToClipboard();
+ void copySelectionToClipboard(const std::vector<const zen::Grid*>& gridRefs);
void deleteSelectedFiles(const std::vector<zen::FileSystemObject*>& selectionLeft,
const std::vector<zen::FileSystemObject*>& selectionRight);
void openExternalApplication(const wxString& commandline, const std::vector<zen::FileSystemObject*>& selection, bool leftSide); //selection may be empty
- //work to be done in idle time
- void OnIdleEvent(wxEvent& event);
+ //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu!
+ void startProcessingAsyncTasks() { timerForAsyncTasks.Start(50); } //timer interval in [ms]
+ void onProcessAsyncTasks(wxEvent& event);
//status bar supports one of the following two states at a time:
void setStatusBarFullText(const wxString& msg);
void setStatusBarFileStatistics(size_t filesOnLeftView, size_t foldersOnLeftView, size_t filesOnRightView, size_t foldersOnRightView, zen::UInt64 filesizeLeftView, zen::UInt64 filesizeRightView);
+
void flashStatusInformation(const wxString& msg); //temporarily show different status (only valid for setStatusBarFullText)
+ void restoreStatusInformation(); //called automatically after a few seconds
//events
void onGridButtonEventL(wxKeyEvent& event);
@@ -225,6 +231,7 @@ private:
virtual void OnMenuGlobalSettings(wxCommandEvent& event);
virtual void OnMenuExportFileList(wxCommandEvent& event);
virtual void OnMenuCheckVersion (wxCommandEvent& event);
+ virtual void OnMenuCheckVersionAutomatically(wxCommandEvent& event);
virtual void OnMenuAbout (wxCommandEvent& event);
virtual void OnShowHelp (wxCommandEvent& event);
virtual void OnMenuQuit (wxCommandEvent& event) { Close(); }
@@ -263,8 +270,7 @@ private:
//***********************************************
//status information
- wxLongLong lastStatusChange;
- std::unique_ptr<wxString> oldStatusMsg;
+ std::list<wxString> oldStatusMsgs; //the first one is the original/non-flash status message
//compare status panel (hidden on start, shown when comparing)
std::unique_ptr<CompareProgressDialog> compareStatus; //always bound
@@ -285,6 +291,12 @@ private:
std::shared_ptr<FolderHistory> folderHistoryLeft; //shared by all wxComboBox dropdown controls
std::shared_ptr<FolderHistory> folderHistoryRight; //always bound!
+
+ //schedule and run long-running tasks asynchronously, but process results on GUI queue
+ zen::AsyncTasks asyncTasks;
+ wxTimer timerForAsyncTasks; //don't use wxWidgets idle handling => repeated idle requests/consumption hogs 100% cpu!
+
+ std::unique_ptr<zen::FilterConfig> filterCfgOnClipboard; //copy/paste of filter config
};
#endif // MAINDIALOG_H
diff --git a/ui/msg_popup.cpp b/ui/msg_popup.cpp
index fb33dfb0..940163af 100644
--- a/ui/msg_popup.cpp
+++ b/ui/msg_popup.cpp
@@ -156,7 +156,7 @@ WarningDlg::WarningDlg(wxWindow* parent, int activeButtons, const wxString& mes
SetTitle(_("Warning"));
m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_warning"));
m_textCtrlMessage->SetValue(messageText);
- checkBoxDontShowAgain.SetLabel(_("Don't show this dialog again"));
+ checkBoxDontShowAgain.SetLabel(_("Don't show this warning again"));
buttonIgnore.SetLabel(_("&Ignore"));
buttonSwitch.SetLabel(_("&Switch"));
//buttonIgnore.SetId(wxID_IGNORE); -> see comment in ErrorDlg
diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp
index b36ccb0f..d23de61a 100644
--- a/ui/progress_indicator.cpp
+++ b/ui/progress_indicator.cpp
@@ -196,17 +196,17 @@ void CompareProgressDialog::Pimpl::updateStatusPanelNow()
//remaining time and speed: only visible during binary comparison
if (perf)
{
- if (numeric::dist(lastStatCallSpeed, timeElapsed.Time()) >= 500)
+ const long timeNow = timeElapsed.Time();
+ if (numeric::dist(lastStatCallSpeed, timeNow) >= 500)
{
- lastStatCallSpeed = timeElapsed.Time();
+ lastStatCallSpeed = timeNow;
- perf->addSample(objectsCurrent, to<double>(dataCurrent), timeElapsed.Time());
+ perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow);
//current speed -> Win 7 copy uses 1 sec update interval
setText(*m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged);
}
- warn_static("more often: eveyr 100 ms?")
//remaining time
setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged);
}
@@ -1257,11 +1257,12 @@ void SyncProgressDialog::Pimpl::updateGui(bool allowYield)
assert(perf);
if (perf)
{
- if (numeric::dist(lastStatCallSpeed, timeElapsed.Time()) >= 500)
+ const long timeNow = timeElapsed.Time();
+ if (numeric::dist(lastStatCallSpeed, timeNow) >= 500)
{
- lastStatCallSpeed = timeElapsed.Time();
+ lastStatCallSpeed = timeNow;
- perf->addSample(objectsCurrent, to<double>(dataCurrent), timeElapsed.Time());
+ perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow);
//current speed -> Win 7 copy uses 1 sec update interval
setText(*m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged);
@@ -1617,6 +1618,7 @@ void SyncProgressDialog::Pimpl::processHasFinished(SyncResult resultId, const Er
void SyncProgressDialog::Pimpl::OnOkay(wxCommandEvent& event)
{
+ isZombie = true; //on Fedora the iconize event is executed *before* entering SyncProgressDialog::Pimpl::OnClose()!!!
Close(); //generate close event: do NOT destroy window unconditionally!
}
@@ -1726,7 +1728,9 @@ SyncProgressDialog::SyncProgressDialog(AbortCallback& abortCb,
if (showProgress)
{
pimpl->Show();
- pimpl->updateGui(false); //clear gui flicker, remove dummy texts: window must be visible to make this work!
+ warn_static("problem??")
+ //clear gui flicker, remove dummy texts: window must be visible to make this work!
+ pimpl->updateGui(true); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough
}
else
pimpl->minimizeToTray();
diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp
index fd62b0cb..3827c837 100644
--- a/ui/small_dlgs.cpp
+++ b/ui/small_dlgs.cpp
@@ -143,7 +143,7 @@ private:
virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
virtual void OnCancel (wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
virtual void OnHelp (wxCommandEvent& event) { displayHelpEntry(L"html/Exclude Items.html", this); }
- virtual void OnDefault (wxCommandEvent& event);
+ virtual void OnClear (wxCommandEvent& event);
virtual void OnApply (wxCommandEvent& event);
virtual void OnUpdateChoice(wxCommandEvent& event) { updateGui(); }
virtual void OnUpdateNameFilter(wxCommandEvent& event) { updateGui(); }
@@ -153,7 +153,6 @@ private:
FilterConfig getFilter() const;
void onKeyEvent(wxKeyEvent& event);
- const bool isGlobalFilter_;
FilterConfig& outputRef;
EnumDescrList<UnitTime> enumTimeDescr;
@@ -165,7 +164,6 @@ FilterDlg::FilterDlg(wxWindow* parent,
bool isGlobalFilter, //global or local filter dialog?
FilterConfig& filter) :
FilterDlgGenerated(parent),
- isGlobalFilter_(isGlobalFilter),
outputRef(filter) //just hold reference
{
#ifdef FFS_WIN
@@ -207,6 +205,7 @@ FilterDlg::FilterDlg(wxWindow* parent,
// else
// m_staticTexHeader->SetLabel("Filter single folder pair"));
//
+
m_staticTextHeader->SetLabel(_("Filter"));
Fit(); //child-element widths have changed: image was set
@@ -234,30 +233,23 @@ void FilterDlg::updateGui()
{
FilterConfig activeCfg = getFilter();
- m_bitmapInclude->SetBitmap(
- !NameFilter::isNull(activeCfg.includeFilter, FilterConfig().excludeFilter) ?
- getResourceImage(L"filter_include") :
- greyScale(getResourceImage(L"filter_include")));
-
- m_bitmapExclude->SetBitmap(
- !NameFilter::isNull(FilterConfig().includeFilter, activeCfg.excludeFilter) ?
- getResourceImage(L"filter_exclude") :
- greyScale(getResourceImage(L"filter_exclude")));
-
- m_bitmapFilterDate->SetBitmap(
- activeCfg.unitTimeSpan != UTIME_NONE ?
- getResourceImage(L"clock") :
- greyScale(getResourceImage(L"clock")));
-
- m_bitmapFilterSize->SetBitmap(
- activeCfg.unitSizeMin != USIZE_NONE ||
- activeCfg.unitSizeMax != USIZE_NONE ?
- getResourceImage(L"size") :
- greyScale(getResourceImage(L"size")));
+ auto setStatusBitmap = [&](wxStaticBitmap& staticBmp, const wxString& bmpName, bool active)
+ {
+ if (active)
+ staticBmp.SetBitmap(getResourceImage(bmpName));
+ else
+ staticBmp.SetBitmap(greyScale(getResourceImage(bmpName)));
+ };
+ setStatusBitmap(*m_bitmapInclude, L"filter_include", !NameFilter::isNull(activeCfg.includeFilter, FilterConfig().excludeFilter));
+ setStatusBitmap(*m_bitmapExclude, L"filter_exclude", !NameFilter::isNull(FilterConfig().includeFilter, activeCfg.excludeFilter));
+ setStatusBitmap(*m_bitmapFilterDate, L"clock", activeCfg.unitTimeSpan != UTIME_NONE);
+ setStatusBitmap(*m_bitmapFilterSize, L"size", activeCfg.unitSizeMin != USIZE_NONE || activeCfg.unitSizeMax != USIZE_NONE);
m_spinCtrlTimespan->Enable(activeCfg.unitTimeSpan == UTIME_LAST_X_DAYS);
m_spinCtrlMinSize ->Enable(activeCfg.unitSizeMin != USIZE_NONE);
m_spinCtrlMaxSize ->Enable(activeCfg.unitSizeMax != USIZE_NONE);
+
+ m_buttonClear ->Enable(!(activeCfg == FilterConfig()));
}
@@ -291,21 +283,15 @@ FilterConfig FilterDlg::getFilter() const
}
-void FilterDlg::OnDefault(wxCommandEvent& event)
+void FilterDlg::OnClear(wxCommandEvent& event)
{
- if (isGlobalFilter_)
- setFilter(MainConfiguration().globalFilter);
- else
- setFilter(FilterConfig());
-
- //changes to mainDialog are only committed when the OK button is pressed
- Fit();
+ setFilter(FilterConfig());
}
void FilterDlg::OnApply(wxCommandEvent& event)
{
- //only if user presses ApplyFilter, he wants the changes to be committed
+ //changes to mainDialog are only committed when the OK button is pressed
outputRef = getFilter();
//when leaving dialog: filter and redraw grid, if filter is active
@@ -395,14 +381,14 @@ void DeleteDialog::updateGui()
wxString header;
if (m_checkBoxUseRecycler->GetValue())
{
- header = _P("Do you really want to move the following object to the Recycle Bin?",
- "Do you really want to move the following %x objects to the Recycle Bin?", delInfo.second);
+ header = _P("Do you really want to move the following item to the Recycle Bin?",
+ "Do you really want to move the following %x items to the Recycle Bin?", delInfo.second);
m_bitmapDeleteType->SetBitmap(getResourceImage(L"recycler"));
}
else
{
- header = _P("Do you really want to delete the following object?",
- "Do you really want to delete the following %x objects?", delInfo.second);
+ header = _P("Do you really want to delete the following item?",
+ "Do you really want to delete the following %x items?", delInfo.second);
m_bitmapDeleteType->SetBitmap(getResourceImage(L"deleteFile"));
}
replace(header, L"%x", toGuiString(delInfo.second));
diff --git a/ui/switch_to_gui.h b/ui/switch_to_gui.h
index a12c7b03..a3cf6827 100644
--- a/ui/switch_to_gui.h
+++ b/ui/switch_to_gui.h
@@ -16,7 +16,7 @@ namespace zen
class SwitchToGui
{
public:
- SwitchToGui(const wxString& referenceFile,
+ SwitchToGui(const Zstring& referenceFile,
const xmlAccess::XmlBatchConfig& batchCfg,
xmlAccess::XmlGlobalSettings& globalSettings) :
guiCfg(xmlAccess::convertBatchToGui(batchCfg)),
@@ -31,7 +31,7 @@ public:
}
private:
- std::vector<wxString> referenceFiles;
+ std::vector<Zstring> referenceFiles;
const xmlAccess::XmlGuiConfig guiCfg;
xmlAccess::XmlGlobalSettings& globalSettings_;
};
diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp
index 2d92e231..dbef3619 100644
--- a/ui/sync_cfg.cpp
+++ b/ui/sync_cfg.cpp
@@ -233,12 +233,10 @@ SyncCfgDialog::SyncCfgDialog(wxWindow* parent,
setRelativeFontSize(*m_toggleBtnMirror, 1.25);
setRelativeFontSize(*m_toggleBtnUpdate, 1.25);
setRelativeFontSize(*m_toggleBtnCustom, 1.25);
- setRelativeFontSize(*m_staticTextHeaderCategory, 0.90);
- setRelativeFontSize(*m_staticTextHeaderAction, 0.90);
enumVersioningStyle.
- add(VER_STYLE_ADD_TIMESTAMP, _("Versioning"), _("Append a timestamp to each file name")).
- add(VER_STYLE_REPLACE, _("Replace"), _("Move files and replace if existing"));
+ add(VER_STYLE_ADD_TIMESTAMP, _("Time stamp"), _("Append a timestamp to each file name")).
+ add(VER_STYLE_REPLACE, _("Replace"), _("Move files and replace if existing"));
//hide controls for optional parameters
if (!handleError && !execWhenFinished) //currently either both or neither are bound!
diff --git a/ui/tree_view.cpp b/ui/tree_view.cpp
index eff840bc..07f7a942 100644
--- a/ui/tree_view.cpp
+++ b/ui/tree_view.cpp
@@ -24,13 +24,13 @@ inline
void TreeView::compressNode(Container& cont) //remove single-element sub-trees -> gain clarity + usability (call *after* inclusion check!!!)
{
if (cont.subDirs.empty() || //single files node or...
- (cont.firstFile == nullptr && //single dir node...
+ (cont.firstFileId == nullptr && //single dir node...
cont.subDirs.size() == 1 && //
- cont.subDirs[0].firstFile == nullptr && //...that is empty
+ cont.subDirs[0].firstFileId == nullptr && //...that is empty
cont.subDirs[0].subDirs.empty())) //
{
cont.subDirs.clear();
- cont.firstFile = nullptr;
+ cont.firstFileId = nullptr;
}
}
@@ -56,50 +56,57 @@ void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in
return std::max(fileObj.getFileSize<LEFT_SIDE>(), fileObj.getFileSize<RIGHT_SIDE>());
};
-
- cont.firstFile = nullptr;
+ cont.firstFileId = nullptr;
std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(),
[&](FileMapping& fileObj)
{
if (pred(fileObj))
{
cont.bytesNet += getBytes(fileObj);
+ ++cont.itemCountNet;
- if (!cont.firstFile)
- cont.firstFile = fileObj.getId();
+ if (!cont.firstFileId)
+ cont.firstFileId = fileObj.getId();
}
});
- cont.bytesGross += cont.bytesNet;
- if (!cont.firstFile)
- std::find_if(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(),
- [&](SymLinkMapping& linkObj) -> bool
+ std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(),
+ [&](SymLinkMapping& linkObj)
{
if (pred(linkObj))
{
- cont.firstFile = linkObj.getId();
- return true;
+ ++cont.itemCountNet;
+
+ if (!cont.firstFileId)
+ cont.firstFileId = linkObj.getId();
}
- return false;
});
+ cont.bytesGross += cont.bytesNet;
+ cont.itemCountGross += cont.itemCountNet;
cont.subDirs.reserve(hierObj.refSubDirs().size()); //avoid expensive reallocations!
std::for_each(hierObj.refSubDirs().begin(), hierObj.refSubDirs().end(),
[&cont, pred](DirMapping& subDirObj)
{
+ const bool included = pred(subDirObj);
+
cont.subDirs.push_back(TreeView::DirNodeImpl()); //
auto& subDirView = cont.subDirs.back();
TreeView::extractVisibleSubtree(subDirObj, subDirView, pred);
- cont.bytesGross += subDirView.bytesGross;
+ if (included)
+ ++subDirView.itemCountGross;
+
+ cont.bytesGross += subDirView.bytesGross;
+ cont.itemCountGross += subDirView.itemCountGross;
- if (pred(subDirObj) || subDirView.firstFile || !subDirView.subDirs.empty())
+ if (!included && !subDirView.firstFileId && subDirView.subDirs.empty())
+ cont.subDirs.pop_back();
+ else
{
subDirView.objId = subDirObj.getId();
compressNode(subDirView);
}
- else
- cont.subDirs.pop_back();
});
}
@@ -202,7 +209,23 @@ void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi colu
return 0U;
};
+ auto getCount = [](const TreeLine& line) -> int
+ {
+ switch (line.type_)
+ {
+ case TreeView::TYPE_ROOT:
+ case TreeView::TYPE_DIRECTORY:
+ return line.node_->itemCountGross;
+
+ case TreeView::TYPE_FILES:
+ return line.node_->itemCountNet;
+ }
+ assert(false);
+ return 0;
+ };
+
const auto lessBytes = [&](const TreeLine& lhs, const TreeLine& rhs) { return getBytes(lhs) < getBytes(rhs); };
+ const auto lessCount = [&](const TreeLine& lhs, const TreeLine& rhs) { return getCount(lhs) < getCount(rhs); };
switch (columnType)
{
@@ -213,6 +236,10 @@ void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi colu
case COL_TYPE_NAVI_DIRECTORY:
std::sort(items.begin(), items.end(), LessShortName<ascending>());
break;
+
+ case COL_TYPE_NAVI_ITEM_COUNT:
+ std::sort(items.begin(), items.end(), makeSortDirection(lessCount, Int2Type<ascending>()));
+ break;
}
}
@@ -230,7 +257,7 @@ void TreeView::getChildren(const Container& cont, size_t level, std::vector<Tree
workList.push_back(std::make_pair(subDir.bytesGross, &output.back().percent_));
});
- if (cont.firstFile)
+ if (cont.firstFileId)
{
output.push_back(TreeLine(level, 0, &cont, TreeView::TYPE_FILES));
workList.push_back(std::make_pair(cont.bytesNet, &output.back().percent_));
@@ -302,7 +329,7 @@ void TreeView::applySubView(std::vector<RootNodeImpl>&& newView)
}
//restore node expansion status
- for (size_t row = 0; row < flatTree.size(); ++row) //flatTree size changes within loop!
+ for (size_t row = 0; row < flatTree.size(); ++row) //flatTree size changes during loop!
{
const TreeLine& line = flatTree[row];
@@ -334,15 +361,16 @@ void TreeView::updateView(Predicate pred)
//warning: the following lines are almost 1:1 copy from extractVisibleSubtree:
//however we *cannot* reuse code here; this were only possible if we could replace "std::vector<RootNodeImpl>" by "Container"!
- if (root.firstFile || !root.subDirs.empty())
+ if (!root.firstFileId && root.subDirs.empty())
+ newView.pop_back();
+ else
{
root.baseMap = baseObj;
this->compressNode(root); //"this->" required by two-pass lookup as enforced by GCC 4.7
}
- else
- newView.pop_back();
});
+ lastViewFilterPred = pred;
applySubView(std::move(newView));
}
@@ -365,6 +393,8 @@ bool TreeView::getDefaultSortDirection(ColumnTypeNavi colType)
return false;
case COL_TYPE_NAVI_DIRECTORY:
return true;
+ case COL_TYPE_NAVI_ITEM_COUNT:
+ return false;
}
assert(false);
return true;
@@ -383,7 +413,7 @@ TreeView::NodeStatus TreeView::getStatus(size_t row) const
{
case TreeView::TYPE_DIRECTORY:
case TreeView::TYPE_ROOT:
- return flatTree[row].node_->firstFile || !flatTree[row].node_->subDirs.empty() ? TreeView::STATUS_REDUCED : TreeView::STATUS_EMPTY;
+ return flatTree[row].node_->firstFileId || !flatTree[row].node_->subDirs.empty() ? TreeView::STATUS_REDUCED : TreeView::STATUS_EMPTY;
case TreeView::TYPE_FILES:
return TreeView::STATUS_EMPTY;
@@ -466,7 +496,14 @@ void TreeView::updateCmpResult(bool hideFiltered,
bool equalFilesActive,
bool conflictFilesActive)
{
- updateView([&](const FileSystemObject& fsObj) -> bool
+ updateView([hideFiltered, //make sure the predicate can be stored safely!
+ leftOnlyFilesActive,
+ rightOnlyFilesActive,
+ leftNewerFilesActive,
+ rightNewerFilesActive,
+ differentFilesActive,
+ equalFilesActive,
+ conflictFilesActive](const FileSystemObject& fsObj) -> bool
{
if (hideFiltered && !fsObj.isActive())
return false;
@@ -506,7 +543,16 @@ void TreeView::updateSyncPreview(bool hideFiltered,
bool syncEqualActive,
bool conflictFilesActive)
{
- updateView([&](const FileSystemObject& fsObj) -> bool
+ updateView([hideFiltered, //make sure the predicate can be stored safely!
+ syncCreateLeftActive,
+ syncCreateRightActive,
+ syncDeleteLeftActive,
+ syncDeleteRightActive,
+ syncDirOverwLeftActive,
+ syncDirOverwRightActive,
+ syncDirNoneActive,
+ syncEqualActive,
+ conflictFilesActive](const FileSystemObject& fsObj) -> bool
{
if (hideFiltered && !fsObj.isActive())
return false;
@@ -564,14 +610,14 @@ std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const
if (row < flatTree.size())
{
const auto level = flatTree[row].level_;
-
const int percent = flatTree[row].percent_;
+
switch (flatTree[row].type_)
{
case TreeView::TYPE_ROOT:
{
const auto* root = static_cast<const TreeView::RootNodeImpl*>(flatTree[row].node_);
- return make_unique<TreeView::RootNode>(percent, getStatus(row), root->bytesGross, *(root->baseMap));
+ return make_unique<TreeView::RootNode>(percent, root->bytesGross, root->itemCountGross, getStatus(row), *(root->baseMap));
}
break;
@@ -579,15 +625,34 @@ std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const
{
const auto* dir = static_cast<const TreeView::DirNodeImpl*>(flatTree[row].node_);
if (auto dirObj = dynamic_cast<DirMapping*>(FileSystemObject::retrieve(dir->objId)))
- return make_unique<TreeView::DirNode>(percent, level, getStatus(row), dir->bytesGross, *dirObj);
+ return make_unique<TreeView::DirNode>(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *dirObj);
}
break;
case TreeView::TYPE_FILES:
{
const auto* parentDir = flatTree[row].node_;
- if (auto firstFile = FileSystemObject::retrieve(parentDir->firstFile))
- return make_unique<TreeView::FilesNode>(percent, level, parentDir->bytesNet, *firstFile);
+ if (auto firstFile = FileSystemObject::retrieve(parentDir->firstFileId))
+ {
+ std::vector<FileSystemObject*> filesAndLinks;
+ HierarchyObject& parent = firstFile->parent();
+
+ //lazy evaluation: recheck "lastViewFilterPred" again rather than buffer and bloat "lastViewFilterPred"
+ std::for_each(parent.refSubFiles().begin(), parent.refSubFiles().end(),
+ [&](FileSystemObject& fsObj)
+ {
+ if (lastViewFilterPred(fsObj))
+ filesAndLinks.push_back(&fsObj);
+ });
+ std::for_each(parent.refSubLinks().begin(), parent.refSubLinks().end(),
+ [&](FileSystemObject& fsObj)
+ {
+ if (lastViewFilterPred(fsObj))
+ filesAndLinks.push_back(&fsObj);
+ });
+
+ return make_unique<TreeView::FilesNode>(percent, parentDir->bytesNet, parentDir->itemCountNet, level, filesAndLinks);
+ }
}
break;
}
@@ -672,13 +737,16 @@ private:
else if (dirRight.empty())
return dirLeft;
else
- return utfCvrtTo<wxString>(dirLeft + L" \x2212 " + dirRight); //\x2212 = unicode minus
+ return dirLeft + L" \x2212 " + dirRight; //\x2212 = unicode minus
}
else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
return utfCvrtTo<wxString>(dir->dirObj_.getObjShortName());
else if (dynamic_cast<const TreeView::FilesNode*>(node.get()))
return _("Files");
break;
+
+ case COL_TYPE_NAVI_ITEM_COUNT:
+ return toGuiString(node->itemCount_);
}
}
return wxEmptyString;
@@ -876,8 +944,9 @@ private:
{
int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL;
- //have file size right-justified (but don't change for RTL languages)
- if (static_cast<ColumnTypeNavi>(colType) == COL_TYPE_NAVI_BYTES && grid.GetLayoutDirection() != wxLayout_RightToLeft)
+ //have file size and item count right-justified (but don't change for RTL languages)
+ if ((static_cast<ColumnTypeNavi>(colType) == COL_TYPE_NAVI_BYTES ||
+ static_cast<ColumnTypeNavi>(colType) == COL_TYPE_NAVI_ITEM_COUNT) && grid.GetLayoutDirection() != wxLayout_RightToLeft)
{
rectTmp.width -= 2 * GAP_SIZE;
alignment = wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL;
@@ -918,6 +987,8 @@ private:
return _("Size");
case COL_TYPE_NAVI_DIRECTORY:
return _("Name");
+ case COL_TYPE_NAVI_ITEM_COUNT:
+ return _("Items");
}
return wxEmptyString;
}
@@ -1043,6 +1114,8 @@ private:
ContextMenu menu;
//--------------------------------------------------------------------------------------------------------
+ menu.addCheckBox(_("Percentage"), [this] { setShowPercentage(!getShowPercentage()); }, getShowPercentage());
+ //--------------------------------------------------------------------------------------------------------
auto toggleColumn = [&](const Grid::ColumnAttribute& ca)
{
auto colAttr = grid_.getColumnConfig();
@@ -1065,8 +1138,6 @@ private:
ca.visible_, ca.type_ != static_cast<ColumnType>(COL_TYPE_NAVI_DIRECTORY)); //do not allow user to hide file name column!
}
//--------------------------------------------------------------------------------------------------------
- menu.addCheckBox(_("Percentage"), [this] { setShowPercentage(!getShowPercentage()); }, getShowPercentage());
- //--------------------------------------------------------------------------------------------------------
menu.addSeparator();
auto setDefaultColumns = [&]
diff --git a/ui/tree_view.h b/ui/tree_view.h
index af269d53..9ea7d0e4 100644
--- a/ui/tree_view.h
+++ b/ui/tree_view.h
@@ -7,10 +7,11 @@
#ifndef TREE_H_INCLUDED_841703190201835280256673425
#define TREE_H_INCLUDED_841703190201835280256673425
+#include <functional>
+#include <zen/optional.h>
#include <wx+/grid.h>
-#include "../file_hierarchy.h"
#include "column_attr.h"
-#include <zen/optional.h>
+#include "../file_hierarchy.h"
namespace zen
{
@@ -56,31 +57,32 @@ public:
//---------------------------------------------------------------------
struct Node
{
- Node(int percent, size_t level, NodeStatus status, UInt64 bytes) :
- percent_(percent), level_(level), status_(status), bytes_(bytes) {}
+ Node(int percent, UInt64 bytes, int itemCount, size_t level, NodeStatus status) :
+ percent_(percent), level_(level), status_(status), bytes_(bytes), itemCount_(itemCount) {}
virtual ~Node() {}
const int percent_; //[0, 100]
const size_t level_;
const NodeStatus status_;
const UInt64 bytes_;
+ const int itemCount_;
};
struct FilesNode : public Node
{
- FilesNode(int percent, size_t level, UInt64 bytes, FileSystemObject& firstFile) : Node(percent, level, STATUS_EMPTY, bytes), firstFile_(firstFile) {}
- FileSystemObject& firstFile_; //or symlink
+ FilesNode(int percent, UInt64 bytes, int itemCount, size_t level, const std::vector<FileSystemObject*>& filesAndLinks) : Node(percent, bytes, itemCount, level, STATUS_EMPTY), filesAndLinks_(filesAndLinks) {}
+ std::vector<FileSystemObject*> filesAndLinks_; //files or symlinks; pointers are bound!
};
struct DirNode : public Node
{
- DirNode(int percent, size_t level, NodeStatus status, UInt64 bytes, DirMapping& dirObj) : Node(percent, level, status, bytes), dirObj_(dirObj) {}
+ DirNode(int percent, UInt64 bytes, int itemCount, size_t level, NodeStatus status, DirMapping& dirObj) : Node(percent, bytes, itemCount, level, status), dirObj_(dirObj) {}
DirMapping& dirObj_;
};
struct RootNode : public Node
{
- RootNode(int percent, NodeStatus status, UInt64 bytes, BaseDirMapping& baseMap) : Node(percent, 0, status, bytes), baseMap_(baseMap) {}
+ RootNode(int percent, UInt64 bytes, int itemCount, NodeStatus status, BaseDirMapping& baseMap) : Node(percent, bytes, itemCount, 0, status), baseMap_(baseMap) {}
BaseDirMapping& baseMap_;
};
@@ -101,11 +103,17 @@ private:
struct Container
{
- Container() : firstFile(nullptr) {}
+ Container() : itemCountGross(), itemCountNet(), firstFileId(nullptr) {}
UInt64 bytesGross;
- UInt64 bytesNet; //files in this directory only
+ UInt64 bytesNet; //bytes for files on view in this directory only
+ int itemCountGross;
+ int itemCountNet; //number of files on view for in this directory only
+
std::vector<DirNodeImpl> subDirs;
- FileSystemObject::ObjectId firstFile; //weak pointer to first FileMapping or SymLinkMapping
+ FileSystemObject::ObjectId firstFileId; //weak pointer to first FileMapping or SymLinkMapping
+ //- "compress" algorithm may hide file nodes for directories with a single included file, i.e. itemCountGross == itemCountNet == 1
+ //- a HierarchyObject* would a better fit, but we need weak pointer semantics!
+ //- a std::vector<FileSystemObject::ObjectId> would be a better design, but we don't want a second memory structure as large as custom grid!
};
struct DirNodeImpl : public Container
@@ -134,13 +142,13 @@ private:
size_t level_;
int percent_; //[0, 100]
const Container* node_; //
- NodeType type_; //we choose to increase size of "flatTree" rather than "folderCmpView" by not using dynamic polymorphism!
+ NodeType type_; //we choose to increase size of "flatTree" rather than "folderCmpView" by not using dynamic polymorphism!
};
static void compressNode(Container& cont);
template <class Function>
static void extractVisibleSubtree(HierarchyObject& hierObj, Container& cont, Function includeObject);
- void getChildren(const Container& cont, size_t level, std::vector<TreeLine>& output);
+ void getChildren(const Container& cont, size_t level, std::vector<TreeLine>& output);
template <class Predicate> void updateView(Predicate pred);
void applySubView(std::vector<RootNodeImpl>&& newView);
@@ -152,6 +160,7 @@ private:
| (update...)
| */
std::vector<RootNodeImpl> folderCmpView; //partial view on folderCmp -> unsorted (cannot be, because files are not a separate entity)
+ std::function<bool(const FileSystemObject& fsObj)> lastViewFilterPred; //buffer view filter predicate for lazy evaluation of files/symlinks corresponding to a TYPE_FILES node
/* /|\
| (update...)
| */
bgstack15