diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:55 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:55 +0200 |
commit | c4182431ac7d9d306fdd2267e082fa4cec6fec2c (patch) | |
tree | 865cca543c062b7af2586f85cee19f9af4e7614d /ui/main_dlg.cpp | |
parent | 5.11 (diff) | |
download | FreeFileSync-c4182431ac7d9d306fdd2267e082fa4cec6fec2c.tar.gz FreeFileSync-c4182431ac7d9d306fdd2267e082fa4cec6fec2c.tar.bz2 FreeFileSync-c4182431ac7d9d306fdd2267e082fa4cec6fec2c.zip |
5.12
Diffstat (limited to 'ui/main_dlg.cpp')
-rw-r--r-- | ui/main_dlg.cpp | 1088 |
1 files changed, 510 insertions, 578 deletions
diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp index c3f33db5..cae45cd1 100644 --- a/ui/main_dlg.cpp +++ b/ui/main_dlg.cpp @@ -5,57 +5,44 @@ // ************************************************************************** #include "main_dlg.h" -#include <iterator> -#include <stdexcept> #include <wx/clipbrd.h> -#include <wx/dataobj.h> -#include <wx/imaglist.h> #include <wx/wupdlock.h> #include <wx/msgdlg.h> #include <wx/sound.h> -#include <wx/display.h> -#include <wx/app.h> -#include <wx/dcmemory.h> #include <wx/filedlg.h> #include <zen/format_unit.h> +#include <zen/file_handling.h> +#include <zen/serialize.h> +#include <zen/file_id.h> +#include <zen/thread.h> #include <wx+/context_menu.h> -#include "folder_history_box.h" #include <wx+/button.h> -#include "../comparison.h" -#include "../synchronization.h" -#include "../algorithm.h" +#include <wx+/shell_execute.h> #include <wx+/app_main.h> +#include <wx+/toggle_button.h> +#include <wx+/mouse_move_dlg.h> +#include <wx+/no_flicker.h> +#include <wx+/rtl.h> #include "check_version.h" #include "gui_status_handler.h" #include "sync_cfg.h" #include "small_dlgs.h" -#include <wx+/mouse_move_dlg.h> #include "progress_indicator.h" #include "msg_popup.h" -#include "../structures.h" -#include "grid_view.h" +#include "folder_pair.h" +#include "search.h" +#include "batch_config.h" +#include "triple_splitter.h" +#include "../comparison.h" +#include "../synchronization.h" +#include "../algorithm.h" #include "../lib/resources.h" -#include <zen/file_handling.h> -#include <zen/serialize.h> -#include <zen/file_id.h> -#include <zen/recycler.h> #include "../lib/resolve_path.h" #include "../lib/ffs_paths.h" -#include <wx+/toggle_button.h> -#include "folder_pair.h" -#include <wx+/rtl.h> -#include "search.h" #include "../lib/help_provider.h" -#include "batch_config.h" -#include <zen/thread.h> #include "../lib/lock_holder.h" -#include <wx+/shell_execute.h> #include "../lib/localization.h" -#include <wx+/image_tools.h> -#include <wx+/no_flicker.h> -#include <wx+/grid.h> -#include "../lib/error_log.h" -#include "triple_splitter.h" +#include <zen/perf.h> using namespace zen; using namespace std::rel_ops; @@ -281,12 +268,13 @@ public: virtual bool allowMove(const wxMouseEvent& event) { - wxPanel* panel = dynamic_cast<wxPanel*>(event.GetEventObject()); - - const wxAuiPaneInfo& paneInfo = mainDlg_.auiMgr.GetPane(panel); - if (paneInfo.IsOk() && - paneInfo.IsFloating()) - return false; //prevent main dialog move + if (wxPanel* panel = dynamic_cast<wxPanel*>(event.GetEventObject())) + { + const wxAuiPaneInfo& paneInfo = mainDlg_.auiMgr.GetPane(panel); + if (paneInfo.IsOk() && + paneInfo.IsFloating()) + return false; //prevent main dialog move + } return true; //allow dialog move } @@ -507,7 +495,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, wxAuiPaneInfo().Name(wxT("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_bpButtonSyncDirNone->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight())); + wxAuiPaneInfo().Name(wxT("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())); @@ -536,19 +524,14 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, m_panelStatusBar ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this); //---------------------------------------------------------------------------------- - //register context: quick variant selection - m_bpButtonCmpConfig ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler (MainDialog::OnCompSettingsContext), nullptr, this); - m_bpButtonSyncConfig->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler (MainDialog::OnSyncSettingsContext), nullptr, this); - m_bpButtonFilter ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(MainDialog::OnGlobalFilterContext), nullptr, this); - //sort grids - m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickL ), nullptr, this ); - m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickC ), nullptr, this ); - m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickR ), nullptr, this ); + m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickL ), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickC ), nullptr, this); + m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickR ), nullptr, this); - m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this ); - m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this ); - m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this ); + m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this); + m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this); + m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this); //grid context menu m_gridMainL->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextL), nullptr, this); @@ -561,8 +544,14 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, m_gridNavi->Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(MainDialog::onNaviSelection), nullptr, this); - gridDataView.reset(new zen::GridView); - treeDataView.reset(new zen::TreeView); + //set tool tips with (non-translated!) short cut hint + m_bpButtonOpen ->SetToolTip(_("Open...") + L" (Ctrl+O)"); + m_bpButtonSave ->SetToolTip(_("Save") + L" (Ctrl+S)"); + m_buttonCompare->SetToolTip(_("Compare both sides") + L" (F5)"); + m_buttonSync ->SetToolTip(_("Start synchronization") + L" (F6)"); + + gridDataView = std::make_shared<GridView>(); + treeDataView = std::make_shared<TreeView>(); cleanedUp = false; processingGlobalKeyEvent = false; @@ -579,7 +568,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, //init handling of first folder pair firstFolderPair.reset(new DirectoryPairFirst(*this)); - //initViewFilterButtons(); + initViewFilterButtons(); //init grid settings gridview::init(*m_gridMainL, *m_gridMainC, *m_gridMainR, gridDataView); @@ -593,7 +582,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg, m_buttonCompare ->setBitmapFront(GlobalResources::getImage(L"compare"), 5); m_bpButtonSyncConfig->SetBitmapLabel(GlobalResources::getImage(L"syncConfig")); m_bpButtonCmpConfig ->SetBitmapLabel(GlobalResources::getImage(L"cmpConfig")); - m_bpButtonLoad ->SetBitmapLabel(GlobalResources::getImage(L"load")); + m_bpButtonOpen ->SetBitmapLabel(GlobalResources::getImage(L"load")); m_bpButtonBatchJob ->SetBitmapLabel(GlobalResources::getImage(L"batch")); m_bpButtonAddPair ->SetBitmapLabel(GlobalResources::getImage(L"item_add")); @@ -1060,29 +1049,28 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const //Exception class used to abort the "compare" and "sync" process class AbortDeleteProcess {}; -class ManualDeletionHandler : private wxEvtHandler, public DeleteFilesHandler +class ManualDeletionHandler : private wxEvtHandler, public DeleteFilesHandler //throw AbortDeleteProcess { public: - ManualDeletionHandler(MainDialog* main) : + ManualDeletionHandler(MainDialog& main) : mainDlg(main), abortRequested(false), - ignoreErrors(false), - deletionCount(0) + ignoreErrors(false) { - mainDlg->disableAllElements(true); //disable everything except abort button + mainDlg.disableAllElements(true); //disable everything except abort button //register abort button - mainDlg->m_buttonAbort->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion), nullptr, this ); - mainDlg->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonAbort->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion), nullptr, this ); + mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); } ~ManualDeletionHandler() { //de-register abort button - mainDlg->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); - mainDlg->m_buttonAbort->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion ), nullptr, this ); + mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(ManualDeletionHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonAbort->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ManualDeletionHandler::OnAbortDeletion ), nullptr, this ); - mainDlg->enableAllElements(); + mainDlg.enableAllElements(); } virtual Response reportError(const std::wstring& msg) @@ -1090,9 +1078,9 @@ public: if (ignoreErrors) return DeleteFilesHandler::IGNORE_ERROR; - updateGUI(); + forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(mainDlg, + switch (showErrorDlg(&mainDlg, ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL, msg, &ignoreNextErrors)) { @@ -1114,9 +1102,9 @@ public: if (!warningActive || ignoreErrors) return; - updateGUI(); + forceUiRefresh(); bool dontWarnAgain = false; - switch (showWarningDlg(mainDlg, ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_CANCEL, msg, dontWarnAgain)) + switch (showWarningDlg(&mainDlg, ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_CANCEL, msg, dontWarnAgain)) { case ReturnWarningDlg::BUTTON_SWITCH: assert(false); @@ -1129,26 +1117,29 @@ public: } } - virtual void notifyDeletion(const Zstring& currentObject) //called for each file/folder that has been deleted + virtual void reportStatus (const std::wstring& msg) { - ++deletionCount; - updateGUI(); + statusMsg = msg; + requestUiRefresh(); } private: - void updateGUI() + virtual void requestUiRefresh() { if (updateUiIsAllowed()) //test if specific time span between ui updates is over - { - mainDlg->setStatusInformation(replaceCpy(_P("Object deleted successfully!", "%x objects deleted successfully!", deletionCount), - L"%x", zen::toGuiString(deletionCount), false)); - updateUiNow(); - } + forceUiRefresh(); if (abortRequested) //test after (implicit) call to wxApp::Yield() throw AbortDeleteProcess(); } + void forceUiRefresh() + { + //std::wstring msg = toGuiString(deletionCount) + + mainDlg.setStatusBarFullText(statusMsg); + updateUiNow(); + } + //context: C callstack message loop => throw()! void OnAbortDeletion(wxCommandEvent& event) //handle abort button click { @@ -1167,11 +1158,12 @@ private: event.Skip(); } - MainDialog* const mainDlg; + MainDialog& mainDlg; bool abortRequested; bool ignoreErrors; - size_t deletionCount; + //size_t deletionCount; // + std::wstring statusMsg; //status reporting }; @@ -1180,184 +1172,223 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec { if (!selectionLeft.empty() || !selectionRight.empty()) { + wxBusyCursor dummy; //show hourglass cursor + wxWindow* oldFocus = wxWindow::FindFocus(); ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();) - if (zen::showDeleteDialog(this, - selectionLeft, - selectionRight, - globalCfg.gui.deleteOnBothSides, - globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY) + bool deleteOnBothSides = false; //let's keep this disabled by default -> don't save + + if (zen::showDeleteDialog(this, + selectionLeft, + selectionRight, + deleteOnBothSides, + globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY) + { + try { - try - { - //handle errors when deleting files/folders - ManualDeletionHandler statusHandler(this); - - zen::deleteFromGridAndHD(selectionLeft, - selectionRight, - folderCmp, - extractDirectionCfg(getConfig().mainCfg), - globalCfg.gui.deleteOnBothSides, - globalCfg.gui.useRecyclerForManualDeletion, - statusHandler, - globalCfg.optDialogs.warningRecyclerMissing); - - gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); //do not clear, if aborted! - } - catch (AbortDeleteProcess&) {} + //handle errors when deleting files/folders + ManualDeletionHandler statusHandler(*this); + + zen::deleteFromGridAndHD(selectionLeft, + selectionRight, + folderCmp, + extractDirectionCfg(getConfig().mainCfg), + deleteOnBothSides, + globalCfg.gui.useRecyclerForManualDeletion, + statusHandler, + globalCfg.optDialogs.warningRecyclerMissing); + + gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); //do not clear, if aborted! + } + catch (AbortDeleteProcess&) {} - //remove rows that are empty: just a beautification, invalid rows shouldn't cause issues - gridDataView->removeInvalidRows(); + //remove rows that are empty: just a beautification, invalid rows shouldn't cause issues + gridDataView->removeInvalidRows(); - //redraw grid neccessary to update new dimensions and for UI-Backend data linkage - updateGui(); //call immediately after deleteFromGridAndHD!!! - } + //redraw grid neccessary to update new dimensions and for UI-Backend data linkage + updateGui(); //call immediately after deleteFromGridAndHD!!! + } } } - +namespace +{ template <SelectedSide side> -wxString extractLastValidDir(const FileSystemObject& fsObj) +Zstring getExistingParentFolder(const FileSystemObject& fsObj) { - Zstring fullname = fsObj.getBaseDirPf<side>() + fsObj.getObjRelativeName(); //full name even if FileSystemObject::isEmpty<side>() == true + const DirMapping* dirObj = dynamic_cast<const DirMapping*>(&fsObj); + if (!dirObj) + dirObj = dynamic_cast<const DirMapping*>(&fsObj.parent()); - while (!fullname.empty() && !dirExists(fullname)) //bad algorithm: this one should better retrieve the status from fsObj - fullname = beforeLast(fullname, FILE_NAME_SEPARATOR); + while (dirObj) + { + if (!dirObj->isEmpty<side>()) + return dirObj->getFullName<side>(); - return toWx(fullname); + dirObj = dynamic_cast<const DirMapping*>(&dirObj->parent()); + } + return fsObj.getBaseDirPf<side>(); +} } - -void MainDialog::openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, bool leftSide) //fsObj may be nullptr +void MainDialog::openExternalApplication(const wxString& commandline, const std::vector<FileSystemObject*>& selection, bool leftSide) { if (commandline.empty()) return; - wxString name; - wxString nameCo; - wxString dir; - wxString dirCo; + auto selectionTmp = selection; - if (fsObj) + const bool openFileBrowserRequested = [&]() -> bool { - name = toWx(fsObj->getFullName<LEFT_SIDE>()); //empty if obj not existing - dir = toWx(beforeLast(fsObj->getFullName<LEFT_SIDE>(), FILE_NAME_SEPARATOR)); //small issue: if obj does not exist but parent exists, this one erronously returns empty + xmlAccess::XmlGlobalSettings::Gui dummy; + return !dummy.externelApplications.empty() && dummy.externelApplications[0].second == commandline; + }(); - nameCo = toWx(fsObj->getFullName<RIGHT_SIDE>()); - dirCo = toWx(beforeLast(fsObj->getFullName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR)); + //support fallback instead of an error in this special case + if (openFileBrowserRequested) + { + if (selectionTmp.size() > 1) //do not open more than one explorer instance! + selectionTmp.resize(1); // + + if (selectionTmp.empty() || + (leftSide && selectionTmp[0]->isEmpty<LEFT_SIDE >()) || + (!leftSide && selectionTmp[0]->isEmpty<RIGHT_SIDE>())) + { + Zstring fallbackDir; + if (selectionTmp.empty()) + fallbackDir = leftSide ? + getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir())) : + getFormattedDirectoryName(toZ(firstFolderPair->getRightDir())); + + else + fallbackDir = leftSide ? + getExistingParentFolder<LEFT_SIDE >(*selectionTmp[0]) : + getExistingParentFolder<RIGHT_SIDE>(*selectionTmp[0]); +#ifdef FFS_WIN + zen::shellExecute(L"\"" + fallbackDir + L"\""); +#elif defined FFS_LINUX + zen::shellExecute("xdg-open \"" + fallbackDir + "\""); +#endif + return; + } } - if (!leftSide) + //regular command evaluation + for (auto it = selectionTmp.begin(); it != selectionTmp.end(); ++it) //context menu calls this function only if selection is not empty! { - std::swap(name, nameCo); - std::swap(dir, dirCo); - } + const FileSystemObject* fsObj = *it; - wxString command = commandline; + Zstring path1 = fsObj->getBaseDirPf<LEFT_SIDE>() + fsObj->getObjRelativeName(); //full path, even if item is not existing! + Zstring dir1 = beforeLast(path1, FILE_NAME_SEPARATOR); //Win: wrong for root paths like "C:\file.txt" - auto tryReplace = [&](wxString phrase, const wxString& replacement) -> bool - { - wxString cmdTmp = command.Upper(); //case insensitive search - phrase.MakeUpper(); // + Zstring path2 = fsObj->getBaseDirPf<RIGHT_SIDE>() + fsObj->getObjRelativeName(); + Zstring dir2 = beforeLast(path2, FILE_NAME_SEPARATOR); - size_t pos = cmdTmp.find(phrase); - if (pos != wxString::npos) + if (!leftSide) { - command.replace(pos, phrase.size(), replacement); - if (replacement.empty()) - return false; + std::swap(path1, path2); + std::swap(dir1, dir2); } - return true; - }; - bool expandSuccess = - /**/ tryReplace(L"%item_path%" , name ); //prevent short-cut behavior! - expandSuccess = tryReplace(L"%item_folder%" , dir ) && expandSuccess; // - expandSuccess = tryReplace(L"%item2_path%" , nameCo) && expandSuccess; // - expandSuccess = tryReplace(L"%item2_folder%", dirCo ) && expandSuccess; // + Zstring command = utfCvrtTo<Zstring>(commandline); + replace(command, Zstr("%item_path%"), path1); + replace(command, Zstr("%item2_path%"), path2); + replace(command, Zstr("%item_folder%"), dir1 ); + replace(command, Zstr("%item2_folder%"), dir2 ); - const bool openFileBrowser = [&]() -> bool - { - xmlAccess::XmlGlobalSettings::Gui dummy; - return !dummy.externelApplications.empty() && dummy.externelApplications[0].second == commandline; - }(); + auto cmdExp = expandMacros(command); + zen::shellExecute(cmdExp); //shows error message if command is malformed + } +} - if (!openFileBrowser || expandSuccess) + +void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView, + size_t foldersOnLeftView, + size_t filesOnRightView, + size_t foldersOnRightView, + UInt64 filesizeLeftView, + UInt64 filesizeRightView) +{ + wxWindowUpdateLocker dummy(m_panelStatusBar); //avoid display distortion + + //select state + bSizerFileStatus->Show(true); + m_staticTextFullStatus->Hide(); + + //fill statistics + //update status information + bSizerStatusLeftDirectories->Show(foldersOnLeftView > 0); + bSizerStatusLeftFiles ->Show(filesOnLeftView > 0); + + setText(*m_staticTextStatusLeftDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnLeftView), L"%x", toGuiString(foldersOnLeftView), false)); + setText(*m_staticTextStatusLeftFiles, replaceCpy(_P("1 file", "%x files", filesOnLeftView), L"%x", toGuiString(filesOnLeftView), false)); + setText(*m_staticTextStatusLeftBytes, filesizeToShortString(to<Int64>(filesizeLeftView))); + + wxString statusMiddleNew; + if (gridDataView->rowsTotal() > 0) { - auto cmdExp = utfCvrtTo<wxString>(expandMacros(utfCvrtTo<Zstring>(command))); - zen::shellExecute(cmdExp); //just execute, show error message if command is malformed + statusMiddleNew = _P("%x of 1 row in view", "%x of %y rows in view", gridDataView->rowsTotal()); + replace(statusMiddleNew, L"%x", toGuiString(gridDataView->rowsOnView()), false); + replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsTotal ()), false); } - else //failed to expand file browser command: support built-in fallback instead of an error! - { - wxString fallbackDir; - if (fsObj) - fallbackDir = leftSide ? - extractLastValidDir<LEFT_SIDE >(*fsObj) : - extractLastValidDir<RIGHT_SIDE>(*fsObj); - if (fallbackDir.empty()) - fallbackDir = leftSide ? - toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir()))) : - toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getRightDir()))); + bSizerStatusRightDirectories->Show(foldersOnRightView > 0); + bSizerStatusRightFiles ->Show(filesOnRightView > 0); -#ifdef FFS_WIN - zen::shellExecute(wxString(L"\"") + fallbackDir + L"\""); -#elif defined FFS_LINUX - zen::shellExecute(wxString(L"xdg-open \"") + fallbackDir + L"\""); -#endif - } + setText(*m_staticTextStatusRightDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnRightView), L"%x", toGuiString(foldersOnRightView), false)); + 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) + setText(*m_staticTextStatusMiddle, statusMiddleNew); + else + *oldStatusMsg = statusMiddleNew; + + m_panelStatusBar->Layout(); } -void MainDialog::setStatusInformation(const wxString& msg) +void MainDialog::setStatusBarFullText(const wxString& msg) { - if (statusMsgStack.empty()) - { - if (m_staticTextStatusMiddle->GetLabel() != msg) - { - m_staticTextStatusMiddle->SetLabel(msg); - m_panelStatusBar->Layout(); - } - } - else - statusMsgStack[0] = msg; //statusMsgStack, index 0 is main status, while 1, 2, ... are temporary status texts in reverse order of screen appearance + //select state + bSizerFileStatus->Show(false); + m_staticTextFullStatus->Show(); + + //update status information + setText(*m_staticTextFullStatus, msg); + m_panelStatusBar->Layout(); } void MainDialog::flashStatusInformation(const wxString& text) { - if (statusMsgStack.empty()) - { - statusMsgStack.push_back(m_staticTextStatusMiddle->GetLabel()); + if (!oldStatusMsg) + oldStatusMsg = make_unique<wxString>(m_staticTextStatusMiddle->GetLabel()); - lastStatusChange = wxGetLocalTimeMillis(); - m_staticTextStatusMiddle->SetLabel(text); - m_staticTextStatusMiddle->SetForegroundColour(wxColour(31, 57, 226)); //highlight color: blue - m_panelStatusBar->Layout(); - } - else - statusMsgStack.insert(statusMsgStack.begin() + 1, text); + lastStatusChange = wxGetLocalTimeMillis(); + m_staticTextStatusMiddle->SetLabel(text); + m_staticTextStatusMiddle->SetForegroundColour(wxColour(31, 57, 226)); //highlight color: blue + m_panelStatusBar->Layout(); } void MainDialog::OnIdleEvent(wxEvent& event) { //small routine to restore status information after some time - if (!statusMsgStack.empty()) //check if there is some work to do + if (oldStatusMsg) //check if there is some work to do { wxMilliClock_t currentTime = wxGetLocalTimeMillis(); - if (currentTime - lastStatusChange > 2500) //restore stackObject after two seconds + if (numeric::dist(currentTime, lastStatusChange) > 2500) //restore stackObject after two seconds { lastStatusChange = currentTime; - m_staticTextStatusMiddle->SetLabel(statusMsgStack.back()); - statusMsgStack.pop_back(); - - if (statusMsgStack.empty()) - m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color - + m_staticTextStatusMiddle->SetLabel(*oldStatusMsg); + m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color m_panelStatusBar->Layout(); + oldStatusMsg.reset(); } } @@ -1367,7 +1398,7 @@ void MainDialog::OnIdleEvent(wxEvent& event) void MainDialog::disableAllElements(bool enableAbort) { - //when changing consider: comparison, synchronization, manual deletion + //when changing consider: comparison, synchronization, manual deletion EnableCloseButton(false); //not allowed for synchronization! progress indicator is top window! @@ -1377,13 +1408,14 @@ void MainDialog::disableAllElements(bool enableAbort) m_panelFilter ->Disable(); m_panelConfig ->Disable(); m_bpButtonSyncConfig ->Disable(); - m_buttonStartSync ->Disable(); + m_buttonSync ->Disable(); m_gridMainL ->Disable(); m_gridMainC ->Disable(); m_gridMainR ->Disable(); m_panelStatistics ->Disable(); m_gridNavi ->Disable(); m_panelDirectoryPairs->Disable(); + m_splitterMain ->Disable(); m_menubar1->EnableTop(0, false); m_menubar1->EnableTop(1, false); m_menubar1->EnableTop(2, false); @@ -1415,13 +1447,14 @@ void MainDialog::enableAllElements() m_panelFilter ->Enable(); m_panelConfig ->Enable(); m_bpButtonSyncConfig ->Enable(); - m_buttonStartSync ->Enable(); + m_buttonSync ->Enable(); m_gridMainL ->Enable(); m_gridMainC ->Enable(); m_gridMainR ->Enable(); m_panelStatistics ->Enable(); m_gridNavi ->Enable(); m_panelDirectoryPairs->Enable(); + m_splitterMain ->Enable(); m_menubar1->EnableTop(0, true); m_menubar1->EnableTop(1, true); m_menubar1->EnableTop(2, true); @@ -1643,12 +1676,8 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide) case WXK_RETURN: case WXK_NUMPAD_ENTER: if (!globalCfg.gui.externelApplications.empty()) - { - const wxString commandline = globalCfg.gui.externelApplications[0].second; //open with first external application - auto cursorPos = grid.getGridCursor(); - const size_t row = cursorPos.first; - openExternalApplication(commandline, gridDataView->getObject(row), leftSide); - } + openExternalApplication(globalCfg.gui.externelApplications[0].second, //open with first external application + getGridSelection(), leftSide); return; } @@ -1832,7 +1861,7 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) std::swap(shortCutLeft, shortCutRight); menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight); - menu.addItem(_("Set direction:") + L" -" L"\tAlt+Up", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); + menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft); //Gtk needs a direction, "<-", because it has no context menu icons! //Gtk requires "no spaces" for shortcut identifiers! @@ -1867,7 +1896,7 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) //---------------------------------------------------------------------------------------------------- //CONTEXT_DELETE_FILES menu.addSeparator(); - menu.addItem(_("Delete") + L"\tDel", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty()); + menu.addItem(_("Delete") + L"\tDelete", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty()); menu.popup(*this); } @@ -1924,7 +1953,7 @@ void MainDialog::onMainGridContextRim(bool leftSide) std::swap(shortCutLeft, shortCutRight); menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight); - menu.addItem(_("Set direction:") + L" -" L"\tAlt+Up", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); + menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone); menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft); //Gtk needs a direction, "<-", because it has no context menu icons! //Gtk requires "no spaces" for shortcut identifiers! @@ -1987,26 +2016,26 @@ void MainDialog::onMainGridContextRim(bool leftSide) it != globalCfg.gui.externelApplications.end(); ++it) { - //some trick to translate default external apps on the fly: 1. "open in explorer" 2. "start directly" + //translate default external apps on the fly: 1. "open in explorer" 2. "start directly" wxString description = zen::implementation::translate(it->first); if (description.empty()) description = L" "; //wxWidgets doesn't like empty items - const wxString command = it->second; + const wxString command = it->second; //COPY into lambda - auto openApp = [this, &selection, leftSide, command] { openExternalApplication(command, selection.empty() ? nullptr : selection[0], leftSide); }; + auto openApp = [this, &selection, leftSide, command] { openExternalApplication(command, selection, leftSide); }; if (it == globalCfg.gui.externelApplications.begin()) - menu.addItem(description + L"\tEnter", openApp); - else - menu.addItem(description, openApp, nullptr, !selection.empty()); + description += L"\tEnter"; + + menu.addItem(description, openApp, nullptr, !selection.empty()); } } //---------------------------------------------------------------------------------------------------- //CONTEXT_DELETE_FILES menu.addSeparator(); - menu.addItem(_("Delete") + L"\tDel", [this] + menu.addItem(_("Delete") + L"\tDelete", [this] { deleteSelectedFiles( getGridSelection(true, false), @@ -2724,38 +2753,48 @@ bool MainDialog::loadConfiguration(const std::vector<wxString>& filenames) } } - -void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event) +void MainDialog::deleteSelectedCfgHistoryItems() { - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) + wxArrayInt tmp; + 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; + }); + + //set active selection on next element to allow "batch-deletion" by holding down DEL key + if (!selections.empty() && m_listBoxHistory->GetCount() > 0) { - //delete currently selected config history items - wxArrayInt tmp; - m_listBoxHistory->GetSelections(tmp); + int newSelection = *selections.begin(); + if (newSelection >= static_cast<int>(m_listBoxHistory->GetCount())) + newSelection = m_listBoxHistory->GetCount() - 1; + m_listBoxHistory->SetSelection(newSelection); + } +} - 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; - }); +void MainDialog::OnCfgHistoryRightClick(wxMouseEvent& event) +{ + ContextMenu menu; + menu.addItem(_("Delete"), [this] { deleteSelectedCfgHistoryItems(); }); + menu.popup(*this); +} - //set active selection on next element to allow "batch-deletion" by holding down DEL key - if (!selections.empty() && m_listBoxHistory->GetCount() > 0) - { - int newSelection = *selections.begin(); - if (newSelection >= static_cast<int>(m_listBoxHistory->GetCount())) - newSelection = m_listBoxHistory->GetCount() - 1; - m_listBoxHistory->SetSelection(newSelection); - } +void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event) +{ + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) + { + deleteSelectedCfgHistoryItems(); return; //"swallow" event } - event.Skip(); } @@ -2840,7 +2879,7 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std:: //evaluate new settings... //(re-)set view filter buttons - initViewFilterButtons(newGuiCfg.mainCfg); + setViewFilterDefault(); updateFilterButtons(); @@ -2966,7 +3005,7 @@ void MainDialog::OnConfigureFilter(wxCommandEvent& event) } -void MainDialog::OnGlobalFilterContext(wxCommandEvent& event) +void MainDialog::OnGlobalFilterContext(wxMouseEvent& event) { ContextMenu menu; @@ -2983,101 +3022,15 @@ void MainDialog::OnGlobalFilterContext(wxCommandEvent& event) } -void MainDialog::OnLeftOnlyFiles(wxCommandEvent& event) -{ - m_bpButtonLeftOnly->toggle(); - updateGui(); -} - - -void MainDialog::OnLeftNewerFiles(wxCommandEvent& event) -{ - m_bpButtonLeftNewer->toggle(); - updateGui(); -} - - -void MainDialog::OnDifferentFiles(wxCommandEvent& event) -{ - m_bpButtonDifferent->toggle(); - updateGui(); -} - - -void MainDialog::OnRightNewerFiles(wxCommandEvent& event) -{ - m_bpButtonRightNewer->toggle(); - updateGui(); -} - - -void MainDialog::OnRightOnlyFiles(wxCommandEvent& event) -{ - m_bpButtonRightOnly->toggle(); - updateGui(); -} - - -void MainDialog::OnEqualFiles(wxCommandEvent& event) -{ - m_bpButtonEqual->toggle(); - updateGui(); -} - - -void MainDialog::OnConflictFiles(wxCommandEvent& event) +void MainDialog::OnToggleViewButton(wxCommandEvent& event) { - m_bpButtonConflict->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncCreateLeft(wxCommandEvent& event) -{ - m_bpButtonSyncCreateLeft->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncCreateRight(wxCommandEvent& event) -{ - m_bpButtonSyncCreateRight->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDeleteLeft(wxCommandEvent& event) -{ - m_bpButtonSyncDeleteLeft->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDeleteRight(wxCommandEvent& event) -{ - m_bpButtonSyncDeleteRight->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDirLeft(wxCommandEvent& event) -{ - m_bpButtonSyncDirOverwLeft->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDirRight(wxCommandEvent& event) -{ - m_bpButtonSyncDirOverwRight->toggle(); - updateGui(); -} - - -void MainDialog::OnSyncDirNone(wxCommandEvent& event) -{ - m_bpButtonSyncDirNone->toggle(); - updateGui(); + if (auto button = dynamic_cast<ToggleButton*>(event.GetEventObject())) + { + button->toggle(); + updateGui(); + } + else + assert(false); } @@ -3102,135 +3055,136 @@ wxBitmap buttonReleased(const std::string& name) } -void MainDialog::initViewFilterButtons(const MainConfiguration& mainCfg) +void MainDialog::initViewFilterButtons() { //compare result buttons - m_bpButtonLeftOnly->init(buttonPressed("leftOnly"), - buttonReleased("leftOnly"), - _("Hide files that exist on left side only"), - _("Show files that exist on left side only")); - - m_bpButtonRightOnly->init(buttonPressed("rightOnly"), - buttonReleased("rightOnly"), - _("Hide files that exist on right side only"), - _("Show files that exist on right side only")); - - m_bpButtonLeftNewer->init(buttonPressed("leftNewer"), - buttonReleased("leftNewer"), - _("Hide files that are newer on left"), - _("Show files that are newer on left")); - - m_bpButtonRightNewer->init(buttonPressed("rightNewer"), - buttonReleased("rightNewer"), - _("Hide files that are newer on right"), - _("Show files that are newer on right")); - - m_bpButtonEqual->init(buttonPressed("equal"), - buttonReleased("equal"), - _("Hide files that are equal"), - _("Show files that are equal")); - - m_bpButtonDifferent->init(buttonPressed("different"), - buttonReleased("different"), - _("Hide files that are different"), - _("Show files that are different")); - - m_bpButtonConflict->init(buttonPressed("conflict"), - buttonReleased("conflict"), - _("Hide conflicts"), - _("Show conflicts")); + 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_bpButtonSyncCreateLeft->init(buttonPressed("createLeft"), + 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_bpButtonSyncCreateRight->init(buttonPressed("createRight"), + 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_bpButtonSyncDeleteLeft->init(buttonPressed("deleteLeft"), + 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_bpButtonSyncDeleteRight->init(buttonPressed("deleteRight"), + 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_bpButtonSyncDirOverwLeft->init(buttonPressed("updateLeft"), - buttonReleased("updateLeft"), - _("Hide files that will be overwritten on left side"), - _("Show files that will be overwritten on left 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_bpButtonSyncDirOverwRight->init(buttonPressed("updateRight"), - buttonReleased("updateRight"), - _("Hide files that will be overwritten on right side"), - _("Show files that will be overwritten on right 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_bpButtonSyncDirNone->init(buttonPressed("none"), - buttonReleased("none"), - _("Hide files that won't be copied"), - _("Show files that won't be copied")); + m_bpButtonShowDoNothing->init(buttonPressed("none"), + buttonReleased("none"), + _("Hide files that won't be copied"), + _("Show files that won't be copied")); +} - //compare result buttons - m_bpButtonLeftOnly-> setActive(true); - m_bpButtonRightOnly-> setActive(true); - m_bpButtonLeftNewer-> setActive(true); - m_bpButtonRightNewer->setActive(true); - m_bpButtonDifferent-> setActive(true); - m_bpButtonConflict-> setActive(true); - //sync preview buttons - m_bpButtonSyncCreateLeft-> setActive(true); - m_bpButtonSyncCreateRight-> setActive(true); - m_bpButtonSyncDeleteLeft-> setActive(true); - m_bpButtonSyncDeleteRight-> setActive(true); - m_bpButtonSyncDirOverwLeft-> setActive(true); - m_bpButtonSyncDirOverwRight->setActive(true); +void MainDialog::setViewFilterDefault() +{ + auto setButton = [](ToggleButton* tb, bool value) { tb->setActive(value); }; - m_bpButtonEqual->setActive(false); + const auto& def = globalCfg.gui.viewFilterDefault; + setButton(m_bpButtonShowLeftOnly, def.leftOnly); + setButton(m_bpButtonShowRightOnly, def.rightOnly); + setButton(m_bpButtonShowLeftNewer, def.leftNewer); + setButton(m_bpButtonShowRightNewer, def.rightNewer); + setButton(m_bpButtonShowEqual, def.equal); + setButton(m_bpButtonShowDifferent, def.different); + setButton(m_bpButtonShowConflict, def.conflict); - //special case "m_bpButtonSyncDirNone": set always active, unless sync direction "none" is part of the rule set: - //e.g. for a "custom" config or "update" variant. Otherwise rows with sync direction "none" can only occur on grid if the user manually - //sets them, in which case these rows should not be hidden immediately, so m_bpButtonSyncDirNone must be active - const std::vector<FolderPairCfg>& cmpCfg = extractCompareCfg(mainCfg); - const bool syncDirNonePartOfConfig = std::any_of(cmpCfg.begin(), cmpCfg.end(), - [&](const FolderPairCfg& fpCfg) -> bool - { - //attention: following is quite an amount of implicit/redundant knowledge here... let's hope our model is fundamental enough to not change any time soon! - - if (fpCfg.directionCfg.var == DirectionConfig::AUTOMATIC) - return false; + setButton(m_bpButtonShowCreateLeft, def.createLeft); + setButton(m_bpButtonShowCreateRight,def.createRight); + setButton(m_bpButtonShowUpdateLeft, def.updateLeft); + setButton(m_bpButtonShowUpdateRight,def.updateRight); + setButton(m_bpButtonShowDeleteLeft, def.deleteLeft); + setButton(m_bpButtonShowDeleteRight,def.deleteRight); + setButton(m_bpButtonShowDoNothing, def.doNothing); +} - const DirectionSet dirSet = extractDirections(fpCfg.directionCfg); - switch (fpCfg.compareVar) - { - case CMP_BY_TIME_SIZE: - return dirSet.exLeftSideOnly == SYNC_DIR_NONE || - dirSet.exRightSideOnly == SYNC_DIR_NONE || - dirSet.leftNewer == SYNC_DIR_NONE || - dirSet.rightNewer == SYNC_DIR_NONE; - //dirSet.different == SYNC_DIR_NONE || - //dirSet.conflict == SYNC_DIR_NONE; +void MainDialog::OnViewButtonRightClick(wxMouseEvent& event) +{ + auto setButtonDefault = [](const ToggleButton* tb, bool& defaultValue) + { + if (tb->IsShown()) + defaultValue = tb->isActive(); + }; - case CMP_BY_CONTENT: - return dirSet.exLeftSideOnly == SYNC_DIR_NONE || - dirSet.exRightSideOnly == SYNC_DIR_NONE || - //dirSet.leftNewer == SYNC_DIR_NONE || - //dirSet.rightNewer == SYNC_DIR_NONE || - dirSet.different == SYNC_DIR_NONE; - //dirSet.conflict == SYNC_DIR_NONE; - } - assert(false); - return false; - }); + auto setDefault = [&] + { + auto& def = globalCfg.gui.viewFilterDefault; + setButtonDefault(m_bpButtonShowLeftOnly, def.leftOnly); + setButtonDefault(m_bpButtonShowRightOnly, def.rightOnly); + setButtonDefault(m_bpButtonShowLeftNewer, def.leftNewer); + setButtonDefault(m_bpButtonShowRightNewer, def.rightNewer); + setButtonDefault(m_bpButtonShowEqual, def.equal); + setButtonDefault(m_bpButtonShowDifferent, def.different); + setButtonDefault(m_bpButtonShowConflict, def.conflict); + + setButtonDefault(m_bpButtonShowCreateLeft, def.createLeft); + setButtonDefault(m_bpButtonShowCreateRight, def.createRight); + setButtonDefault(m_bpButtonShowUpdateLeft, def.updateLeft); + setButtonDefault(m_bpButtonShowUpdateRight, def.updateRight); + setButtonDefault(m_bpButtonShowDeleteLeft, def.deleteLeft); + setButtonDefault(m_bpButtonShowDeleteRight, def.deleteRight); + setButtonDefault(m_bpButtonShowDoNothing, def.doNothing); + }; - m_bpButtonSyncDirNone->setActive(!syncDirNonePartOfConfig); + ContextMenu menu; + menu.addItem( _("Set as default"), setDefault); + menu.popup(*this); } @@ -3316,7 +3270,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) treeDataView->setData(folderCmp); // updateGui(); - // if (m_buttonStartSync->IsShownOnScreen()) m_buttonStartSync->SetFocus(); + // if (m_buttonSync->IsShownOnScreen()) m_buttonSync->SetFocus(); gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); m_gridNavi->clearSelection(); @@ -3353,13 +3307,13 @@ void MainDialog::updateGui() //update sync button enabled/disabled status if (!folderCmp.empty()) { - m_buttonStartSync->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT)); - m_buttonStartSync->setBitmapFront(GlobalResources::getImage(L"sync"), 5); + m_buttonSync->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT)); + m_buttonSync->setBitmapFront(GlobalResources::getImage(L"sync"), 5); } else { - m_buttonStartSync->SetForegroundColour(wxColor(128, 128, 128)); //Some colors seem to have problems with 16-bit desktop color, well this one hasn't! - m_buttonStartSync->setBitmapFront(greyScale(GlobalResources::getImage(L"sync")), 5); + m_buttonSync->SetForegroundColour(wxColor(128, 128, 128)); //Some colors seem to have problems with 16-bit desktop color, well this one hasn't! + m_buttonSync->setBitmapFront(greyScale(GlobalResources::getImage(L"sync")), 5); } auiMgr.Update(); //fix small display distortion, if view filter panel is empty @@ -3562,9 +3516,13 @@ void MainDialog::onGridDoubleClickR(GridClickEvent& event) void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide) { if (!globalCfg.gui.externelApplications.empty()) - openExternalApplication(globalCfg.gui.externelApplications[0].second, - gridDataView->getObject(row), //optional - leftSide); + { + std::vector<FileSystemObject*> selection; + if (FileSystemObject* fsObj = gridDataView->getObject(row)) //selection must be a list of BOUND pointers! + selection.push_back(fsObj); + + openExternalApplication(globalCfg.gui.externelApplications[0].second, selection, leftSide); + } } @@ -3620,26 +3578,26 @@ void MainDialog::OnSwapSides(wxCommandEvent& event) } //swap view filter - bool tmp = m_bpButtonLeftOnly->isActive(); - m_bpButtonLeftOnly->setActive(m_bpButtonRightOnly->isActive()); - m_bpButtonRightOnly->setActive(tmp); + bool tmp = m_bpButtonShowLeftOnly->isActive(); + m_bpButtonShowLeftOnly->setActive(m_bpButtonShowRightOnly->isActive()); + m_bpButtonShowRightOnly->setActive(tmp); - tmp = m_bpButtonLeftNewer->isActive(); - m_bpButtonLeftNewer->setActive(m_bpButtonRightNewer->isActive()); - m_bpButtonRightNewer->setActive(tmp); + tmp = m_bpButtonShowLeftNewer->isActive(); + m_bpButtonShowLeftNewer->setActive(m_bpButtonShowRightNewer->isActive()); + m_bpButtonShowRightNewer->setActive(tmp); /* for sync preview and "mirror" variant swapping may create strange effect: - tmp = m_bpButtonSyncCreateLeft->isActive(); - m_bpButtonSyncCreateLeft->setActive(m_bpButtonSyncCreateRight->isActive()); - m_bpButtonSyncCreateRight->setActive(tmp); + tmp = m_bpButtonShowCreateLeft->isActive(); + m_bpButtonShowCreateLeft->setActive(m_bpButtonShowCreateRight->isActive()); + m_bpButtonShowCreateRight->setActive(tmp); - tmp = m_bpButtonSyncDeleteLeft->isActive(); - m_bpButtonSyncDeleteLeft->setActive(m_bpButtonSyncDeleteRight->isActive()); - m_bpButtonSyncDeleteRight->setActive(tmp); + tmp = m_bpButtonShowDeleteLeft->isActive(); + m_bpButtonShowDeleteLeft->setActive(m_bpButtonShowDeleteRight->isActive()); + m_bpButtonShowDeleteRight->setActive(tmp); - tmp = m_bpButtonSyncDirOverwLeft->isActive(); - m_bpButtonSyncDirOverwLeft->setActive(m_bpButtonSyncDirOverwRight->isActive()); - m_bpButtonSyncDirOverwRight->setActive(tmp); + tmp = m_bpButtonShowUpdateLeft->isActive(); + m_bpButtonShowUpdateLeft->setActive(m_bpButtonShowUpdateRight->isActive()); + m_bpButtonShowUpdateRight->setActive(tmp); */ //swap grid information @@ -3659,34 +3617,34 @@ void MainDialog::updateGridViewData() zen::UInt64 filesizeRightView; //disable all buttons per default - m_bpButtonLeftOnly-> Show(false); - m_bpButtonRightOnly-> Show(false); - m_bpButtonLeftNewer-> Show(false); - m_bpButtonRightNewer->Show(false); - m_bpButtonDifferent-> Show(false); - m_bpButtonEqual-> Show(false); - m_bpButtonConflict-> Show(false); - - m_bpButtonSyncCreateLeft-> Show(false); - m_bpButtonSyncCreateRight-> Show(false); - m_bpButtonSyncDeleteLeft-> Show(false); - m_bpButtonSyncDeleteRight-> Show(false); - m_bpButtonSyncDirOverwLeft-> Show(false); - m_bpButtonSyncDirOverwRight->Show(false); - m_bpButtonSyncDirNone-> Show(false); + m_bpButtonShowLeftOnly ->Show(false); + m_bpButtonShowRightOnly ->Show(false); + m_bpButtonShowLeftNewer ->Show(false); + m_bpButtonShowRightNewer->Show(false); + m_bpButtonShowDifferent ->Show(false); + m_bpButtonShowEqual ->Show(false); + m_bpButtonShowConflict ->Show(false); + + m_bpButtonShowCreateLeft ->Show(false); + m_bpButtonShowCreateRight->Show(false); + m_bpButtonShowDeleteLeft ->Show(false); + m_bpButtonShowDeleteRight->Show(false); + m_bpButtonShowUpdateLeft ->Show(false); + m_bpButtonShowUpdateRight->Show(false); + m_bpButtonShowDoNothing ->Show(false); if (showSyncAction_) { const GridView::StatusSyncPreview result = gridDataView->updateSyncPreview(currentCfg.hideExcludedItems, - m_bpButtonSyncCreateLeft-> isActive(), - m_bpButtonSyncCreateRight-> isActive(), - m_bpButtonSyncDeleteLeft-> isActive(), - m_bpButtonSyncDeleteRight-> isActive(), - m_bpButtonSyncDirOverwLeft-> isActive(), - m_bpButtonSyncDirOverwRight->isActive(), - m_bpButtonSyncDirNone-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowCreateLeft ->isActive(), + m_bpButtonShowCreateRight->isActive(), + m_bpButtonShowDeleteLeft ->isActive(), + m_bpButtonShowDeleteRight->isActive(), + m_bpButtonShowUpdateLeft ->isActive(), + m_bpButtonShowUpdateRight->isActive(), + m_bpButtonShowDoNothing ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); filesOnLeftView = result.filesOnLeftView; foldersOnLeftView = result.foldersOnLeftView; filesOnRightView = result.filesOnRightView; @@ -3696,25 +3654,25 @@ void MainDialog::updateGridViewData() //sync preview buttons - m_bpButtonSyncCreateLeft-> Show(result.existsSyncCreateLeft); - m_bpButtonSyncCreateRight-> Show(result.existsSyncCreateRight); - m_bpButtonSyncDeleteLeft-> Show(result.existsSyncDeleteLeft); - m_bpButtonSyncDeleteRight-> Show(result.existsSyncDeleteRight); - m_bpButtonSyncDirOverwLeft-> Show(result.existsSyncDirLeft); - m_bpButtonSyncDirOverwRight->Show(result.existsSyncDirRight); - m_bpButtonSyncDirNone-> Show(result.existsSyncDirNone); - m_bpButtonEqual-> Show(result.existsSyncEqual); - m_bpButtonConflict-> Show(result.existsConflict); - - if (m_bpButtonSyncCreateLeft-> IsShown() || - m_bpButtonSyncCreateRight-> IsShown() || - m_bpButtonSyncDeleteLeft-> IsShown() || - m_bpButtonSyncDeleteRight-> IsShown() || - m_bpButtonSyncDirOverwLeft-> IsShown() || - m_bpButtonSyncDirOverwRight->IsShown() || - m_bpButtonSyncDirNone-> IsShown() || - m_bpButtonEqual-> IsShown() || - m_bpButtonConflict-> IsShown()) + m_bpButtonShowCreateLeft ->Show(result.existsSyncCreateLeft); + m_bpButtonShowCreateRight ->Show(result.existsSyncCreateRight); + m_bpButtonShowDeleteLeft ->Show(result.existsSyncDeleteLeft); + m_bpButtonShowDeleteRight ->Show(result.existsSyncDeleteRight); + m_bpButtonShowUpdateLeft ->Show(result.existsSyncDirLeft); + m_bpButtonShowUpdateRight ->Show(result.existsSyncDirRight); + m_bpButtonShowDoNothing ->Show(result.existsSyncDirNone); + m_bpButtonShowEqual ->Show(result.existsSyncEqual); + m_bpButtonShowConflict ->Show(result.existsConflict); + + if (m_bpButtonShowCreateLeft ->IsShown() || + m_bpButtonShowCreateRight->IsShown() || + m_bpButtonShowDeleteLeft ->IsShown() || + m_bpButtonShowDeleteRight->IsShown() || + m_bpButtonShowUpdateLeft ->IsShown() || + m_bpButtonShowUpdateRight->IsShown() || + m_bpButtonShowDoNothing ->IsShown() || + m_bpButtonShowEqual ->IsShown() || + m_bpButtonShowConflict ->IsShown()) { m_panelViewFilter->Show(); m_panelViewFilter->Layout(); @@ -3726,13 +3684,13 @@ void MainDialog::updateGridViewData() else { const GridView::StatusCmpResult result = gridDataView->updateCmpResult(currentCfg.hideExcludedItems, - m_bpButtonLeftOnly-> isActive(), - m_bpButtonRightOnly-> isActive(), - m_bpButtonLeftNewer-> isActive(), - m_bpButtonRightNewer->isActive(), - m_bpButtonDifferent-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowLeftOnly ->isActive(), + m_bpButtonShowRightOnly ->isActive(), + m_bpButtonShowLeftNewer ->isActive(), + m_bpButtonShowRightNewer->isActive(), + m_bpButtonShowDifferent ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); filesOnLeftView = result.filesOnLeftView; foldersOnLeftView = result.foldersOnLeftView; filesOnRightView = result.filesOnRightView; @@ -3741,21 +3699,21 @@ void MainDialog::updateGridViewData() filesizeRightView = result.filesizeRightView; //comparison result view buttons - m_bpButtonLeftOnly-> Show(result.existsLeftOnly); - m_bpButtonRightOnly-> Show(result.existsRightOnly); - m_bpButtonLeftNewer-> Show(result.existsLeftNewer); - m_bpButtonRightNewer->Show(result.existsRightNewer); - m_bpButtonDifferent-> Show(result.existsDifferent); - m_bpButtonEqual-> Show(result.existsEqual); - m_bpButtonConflict-> Show(result.existsConflict); - - if (m_bpButtonLeftOnly-> IsShown() || - m_bpButtonRightOnly-> IsShown() || - m_bpButtonLeftNewer-> IsShown() || - m_bpButtonRightNewer->IsShown() || - m_bpButtonDifferent-> IsShown() || - m_bpButtonEqual-> IsShown() || - m_bpButtonConflict-> IsShown()) + m_bpButtonShowLeftOnly ->Show(result.existsLeftOnly); + m_bpButtonShowRightOnly ->Show(result.existsRightOnly); + m_bpButtonShowLeftNewer ->Show(result.existsLeftNewer); + m_bpButtonShowRightNewer->Show(result.existsRightNewer); + m_bpButtonShowDifferent ->Show(result.existsDifferent); + m_bpButtonShowEqual ->Show(result.existsEqual); + m_bpButtonShowConflict ->Show(result.existsConflict); + + if (m_bpButtonShowLeftOnly ->IsShown() || + m_bpButtonShowRightOnly ->IsShown() || + m_bpButtonShowLeftNewer ->IsShown() || + m_bpButtonShowRightNewer->IsShown() || + m_bpButtonShowDifferent ->IsShown() || + m_bpButtonShowEqual ->IsShown() || + m_bpButtonShowConflict ->IsShown()) { m_panelViewFilter->Show(); m_panelViewFilter->Layout(); @@ -3769,58 +3727,33 @@ void MainDialog::updateGridViewData() //navigation tree if (showSyncAction_) treeDataView->updateSyncPreview(currentCfg.hideExcludedItems, - m_bpButtonSyncCreateLeft-> isActive(), - m_bpButtonSyncCreateRight-> isActive(), - m_bpButtonSyncDeleteLeft-> isActive(), - m_bpButtonSyncDeleteRight-> isActive(), - m_bpButtonSyncDirOverwLeft-> isActive(), - m_bpButtonSyncDirOverwRight->isActive(), - m_bpButtonSyncDirNone-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowCreateLeft ->isActive(), + m_bpButtonShowCreateRight->isActive(), + m_bpButtonShowDeleteLeft ->isActive(), + m_bpButtonShowDeleteRight->isActive(), + m_bpButtonShowUpdateLeft ->isActive(), + m_bpButtonShowUpdateRight->isActive(), + m_bpButtonShowDoNothing ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); else treeDataView->updateCmpResult(currentCfg.hideExcludedItems, - m_bpButtonLeftOnly-> isActive(), - m_bpButtonRightOnly-> isActive(), - m_bpButtonLeftNewer-> isActive(), - m_bpButtonRightNewer->isActive(), - m_bpButtonDifferent-> isActive(), - m_bpButtonEqual-> isActive(), - m_bpButtonConflict-> isActive()); + m_bpButtonShowLeftOnly ->isActive(), + m_bpButtonShowRightOnly ->isActive(), + m_bpButtonShowLeftNewer ->isActive(), + m_bpButtonShowRightNewer->isActive(), + m_bpButtonShowDifferent ->isActive(), + m_bpButtonShowEqual ->isActive(), + m_bpButtonShowConflict ->isActive()); m_gridNavi->Refresh(); - - //status bar - wxWindowUpdateLocker dummy(m_panelStatusBar); //avoid display distortion - - //################################################# - //update status information - bSizerStatusLeftDirectories->Show(foldersOnLeftView > 0); - bSizerStatusLeftFiles ->Show(filesOnLeftView > 0); - - setText(*m_staticTextStatusLeftDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnLeftView), L"%x", toGuiString(foldersOnLeftView), false)); - setText(*m_staticTextStatusLeftFiles, replaceCpy(_P("1 file", "%x files", filesOnLeftView), L"%x", toGuiString(filesOnLeftView), false)); - setText(*m_staticTextStatusLeftBytes, filesizeToShortString(to<Int64>(filesizeLeftView))); - - { - wxString statusMiddleNew; - if (gridDataView->rowsTotal() > 0) - { - statusMiddleNew = _P("%x of 1 row in view", "%x of %y rows in view", gridDataView->rowsTotal()); - replace(statusMiddleNew, L"%x", toGuiString(gridDataView->rowsOnView()), false); - replace(statusMiddleNew, L"%y", toGuiString(gridDataView->rowsTotal ()), false); - } - setStatusInformation(statusMiddleNew); - } - - bSizerStatusRightDirectories->Show(foldersOnRightView > 0); - bSizerStatusRightFiles ->Show(filesOnRightView > 0); - - setText(*m_staticTextStatusRightDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnRightView), L"%x", toGuiString(foldersOnRightView), false)); - setText(*m_staticTextStatusRightFiles, replaceCpy(_P("1 file", "%x files", filesOnRightView), L"%x", toGuiString(filesOnRightView), false)); - setText(*m_staticTextStatusRightBytes, filesizeToShortString(to<Int64>(filesizeRightView))); - - m_panelStatusBar->Layout(); + //update status bar information + setStatusBarFileStatistics(filesOnLeftView, + foldersOnLeftView, + filesOnRightView, + foldersOnRightView, + filesizeLeftView, + filesizeRightView); } @@ -3898,10 +3831,10 @@ void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) wxWindowUpdateLocker dummy(this); //avoid display distortion const wxObject* const eventObj = event.GetEventObject(); //find folder pair originating the event - for (std::vector<DirectoryPair*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - if (eventObj == (*i)->m_bpButtonRemovePair) + for (std::vector<DirectoryPair*>::const_iterator it = additionalFolderPairs.begin(); it != additionalFolderPairs.end(); ++it) + if (eventObj == (*it)->m_bpButtonRemovePair) { - removeAddFolderPair(i - additionalFolderPairs.begin()); + removeAddFolderPair(it - additionalFolderPairs.begin()); return; } } @@ -4007,10 +3940,10 @@ void MainDialog::addFolderPair(const std::vector<FolderPairEnh>& newPairs, bool for (auto it = newPairs.begin(); it != newPairs.end(); ++it)//set alternate configuration newEntries[it - newPairs.begin()]->setValues(toWx(it->leftDirectory), - toWx(it->rightDirectory), - it->altCmpConfig, - it->altSyncConfig, - it->localFilter); + toWx(it->rightDirectory), + it->altCmpConfig, + it->altSyncConfig, + it->localFilter); clearGrid(); //+ GUI update } @@ -4302,7 +4235,6 @@ void MainDialog::switchProgramLanguage(int langID) void MainDialog::OnMenuLanguageSwitch(wxCommandEvent& event) { std::map<MenuItemID, LanguageID>::const_iterator it = languageMenuItemMap.find(event.GetId()); - if (it != languageMenuItemMap.end()) switchProgramLanguage(it->second); } |