diff options
Diffstat (limited to 'ui')
35 files changed, 1316 insertions, 1588 deletions
diff --git a/ui/batch_config.cpp b/ui/batch_config.cpp index 0ed13399..cbb667ad 100644 --- a/ui/batch_config.cpp +++ b/ui/batch_config.cpp @@ -33,7 +33,7 @@ class BatchDialog: public BatchDlgGenerated friend class FolderPairCallback; public: - BatchDialog(wxWindow* window, + BatchDialog(wxWindow* parent, const wxString& referenceFile, const xmlAccess::XmlBatchConfig& batchCfg, const std::shared_ptr<FolderHistory>& folderHistLeft, @@ -42,23 +42,23 @@ public: size_t onCompletionHistoryMax); private: - virtual void OnCmpSettings( wxCommandEvent& event); - virtual void OnSyncSettings( wxCommandEvent& event); - virtual void OnConfigureFilter( wxCommandEvent& event); - virtual void OnHelp( wxCommandEvent& event); + virtual void OnCmpSettings (wxCommandEvent& event); + virtual void OnSyncSettings (wxCommandEvent& event); + virtual void OnConfigureFilter(wxCommandEvent& event); + virtual void OnHelp (wxCommandEvent& event); void OnCompSettingsContext(wxCommandEvent& event); void OnSyncSettingsContext(wxCommandEvent& event); void OnGlobalFilterContext(wxCommandEvent& event); - virtual void OnCheckSaveLog( wxCommandEvent& event); + virtual void OnCheckSaveLog (wxCommandEvent& event); virtual void OnChangeMaxLogCountTxt(wxCommandEvent& event); - virtual void OnClose( wxCloseEvent& event) { EndModal(0); } - virtual void OnCancel( wxCommandEvent& event) { EndModal(0); } - virtual void OnSaveBatchJob( wxCommandEvent& event); - virtual void OnLoadBatchJob( wxCommandEvent& event); - virtual void OnAddFolderPair( wxCommandEvent& event); - virtual void OnRemoveFolderPair( wxCommandEvent& event); - virtual void OnRemoveTopFolderPair(wxCommandEvent& event); + virtual void OnClose (wxCloseEvent& event) { EndModal(0); } + virtual void OnCancel (wxCommandEvent& event) { EndModal(0); } + virtual void OnSaveBatchJob (wxCommandEvent& event); + virtual void OnLoadBatchJob (wxCommandEvent& event); + virtual void OnAddFolderPair (wxCommandEvent& event); + virtual void OnRemoveFolderPair (wxCommandEvent& event); + virtual void OnRemoveTopFolderPair (wxCommandEvent& event); void OnFilesDropped(FileDropEvent& event); void addFolderPair(const std::vector<zen::FolderPairEnh>& newPairs, bool addFront = false); @@ -229,14 +229,14 @@ private: //################################################################################################################################### -BatchDialog::BatchDialog(wxWindow* window, +BatchDialog::BatchDialog(wxWindow* parent, const wxString& referenceFile, const xmlAccess::XmlBatchConfig& batchCfg, const std::shared_ptr<FolderHistory>& folderHistLeft, const std::shared_ptr<FolderHistory>& folderHistRight, std::vector<std::wstring>& onCompletionHistory, size_t onCompletionHistoryMax) : - BatchDlgGenerated(window), + BatchDlgGenerated(parent), folderHistLeft_(folderHistLeft), folderHistRight_(folderHistRight), onCompletionHistory_(onCompletionHistory), @@ -317,7 +317,7 @@ void BatchDialog::OnCmpSettings(wxCommandEvent& event) //wxPoint windowPos = m_bpButtonCmpConfig->GetScreenPosition(); //windowPos.x += m_bpButtonCmpConfig->GetSize().GetWidth() + 5; - if (zen::showCompareCfgDialog(localBatchCfg.mainCfg.cmpConfig) == ReturnSmallDlg::BUTTON_OKAY) + if (zen::showCompareCfgDialog(this,localBatchCfg.mainCfg.cmpConfig) == ReturnSmallDlg::BUTTON_OKAY) { updateGui(); } @@ -331,7 +331,8 @@ void BatchDialog::OnSyncSettings(wxCommandEvent& event) onCompletionHistoryMax_ }; - if (showSyncConfigDlg(localBatchCfg.mainCfg.cmpConfig.compareVar, + if (showSyncConfigDlg(this, + localBatchCfg.mainCfg.cmpConfig.compareVar, localBatchCfg.mainCfg.syncCfg, nullptr, &ewfCfg) == ReturnSyncConfig::BUTTON_OKAY) //optional input parameter @@ -343,7 +344,8 @@ void BatchDialog::OnSyncSettings(wxCommandEvent& event) void BatchDialog::OnConfigureFilter(wxCommandEvent& event) { - if (showFilterDialog(true, //is main filter dialog + if (showFilterDialog(this, + true, //is main filter dialog localBatchCfg.mainCfg.globalFilter) == ReturnSmallDlg::BUTTON_OKAY) { updateGui(); @@ -455,23 +457,7 @@ void BatchDialog::OnChangeMaxLogCountTxt(wxCommandEvent& event) void BatchDialog::OnFilesDropped(FileDropEvent& event) { - if (event.getFiles().empty()) - return; - - const std::vector<wxString>& fileList = event.getFiles(); - - switch (xmlAccess::getMergeType(fileList)) //throw () - { - case xmlAccess::MERGE_BATCH: - case xmlAccess::MERGE_GUI: - case xmlAccess::MERGE_GUI_BATCH: - loadBatchFile(fileList); - break; - - case xmlAccess::MERGE_OTHER: - wxMessageBox(_("Invalid FreeFileSync config file!"), _("Error"), wxOK | wxICON_ERROR); - break; - } + loadBatchFile(event.getFiles()); } @@ -578,11 +564,11 @@ bool BatchDialog::saveBatchFile(const wxString& filename) //write config to XML try { - xmlAccess::writeConfig(batchCfg, filename); + xmlAccess::writeConfig(batchCfg, toZ(filename)); } catch (const xmlAccess::FfsXmlError& error) { - wxMessageBox(error.toString().c_str(), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(error.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this); return false; } @@ -611,17 +597,17 @@ void BatchDialog::loadBatchFile(const std::vector<wxString>& filenames) try { //open a *.ffs_gui or *.ffs_batch file! - xmlAccess::convertConfig(filenames, batchCfg); //throw (xmlAccess::FfsXmlError) + xmlAccess::convertConfig(toZ(filenames), batchCfg); //throw FfsXmlError //xmlAccess::readConfig(filename, batchCfg); } catch (const xmlAccess::FfsXmlError& error) { if (error.getSeverity() == xmlAccess::FfsXmlError::WARNING) - wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING); + wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING, this); else { - wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR, this); return; } } @@ -888,70 +874,14 @@ void BatchDialog::clearAddFolderPairs() } -/* -#ifdef FFS_WIN -#include <zen/win.h> //includes "windows.h" -#include <shlobj.h> -#endif // FFS_WIN - - -bool BatchDialog::createBatchFile(const wxString& filename) -{ - //create shell link (instead of batch file) for full Unicode support - HRESULT hResult = E_FAIL; - IShellLink* pShellLink = nullptr; - - if (FAILED(CoCreateInstance(CLSID_ShellLink, //class identifier - nullptr, //object isn't part of an aggregate - CLSCTX_INPROC_SERVER, //context for running executable code - IID_IShellLink, //interface identifier - (void**)&pShellLink))) //pointer to storage of interface pointer - return false; - CleanUp<IShellLink> cleanOnExit(pShellLink); - - wxString freeFileSyncExe = wxStandardPaths::Get().GetExecutablePath(); - if (FAILED(pShellLink->SetPath(freeFileSyncExe.c_str()))) - return false; - - if (FAILED(pShellLink->SetArguments(getCommandlineArguments().c_str()))) - return false; - - if (FAILED(pShellLink->SetIconLocation(freeFileSyncExe.c_str(), 1))) //second icon from executable file is used - return false; - - if (FAILED(pShellLink->SetDescription(_("FreeFileSync Batch Job")))) - return false; - - IPersistFile* pPersistFile = nullptr; - if (FAILED(pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile))) - return false; - CleanUp<IPersistFile> cleanOnExit2(pPersistFile); - - //pPersistFile->Save accepts unicode input only -#ifdef _UNICODE - hResult = pPersistFile->Save(filename.c_str(), TRUE); -#else - WCHAR wszTemp [MAX_PATH]; - if (MultiByteToWideChar(CP_ACP, 0, filename.c_str(), -1, wszTemp, MAX_PATH) == 0) - return false; - - hResult = pPersistFile->Save(wszTemp, TRUE); -#endif - if (FAILED(hResult)) - return false; - - return true; -} -*/ - - -void zen::showSyncBatchDlg(const wxString& referenceFile, +void zen::showSyncBatchDlg(wxWindow* parent, + const wxString& referenceFile, const xmlAccess::XmlBatchConfig& batchCfg, const std::shared_ptr<FolderHistory>& folderHistLeft, const std::shared_ptr<FolderHistory>& folderHistRight, std::vector<std::wstring>& execFinishedhistory, size_t execFinishedhistoryMax) { - BatchDialog batchDlg(nullptr, referenceFile, batchCfg, folderHistLeft, folderHistRight, execFinishedhistory, execFinishedhistoryMax); + BatchDialog batchDlg(parent, referenceFile, batchCfg, folderHistLeft, folderHistRight, execFinishedhistory, execFinishedhistoryMax); batchDlg.ShowModal(); } diff --git a/ui/batch_config.h b/ui/batch_config.h index 472f21bf..04f0bf04 100644 --- a/ui/batch_config.h +++ b/ui/batch_config.h @@ -10,9 +10,11 @@ #include "../lib/process_xml.h" #include "folder_history_box.h" + namespace zen { -void showSyncBatchDlg(const wxString& referenceFile, +void showSyncBatchDlg(wxWindow* parent, + const wxString& referenceFile, const xmlAccess::XmlBatchConfig& batchCfg, const std::shared_ptr<FolderHistory>& folderHistLeft, const std::shared_ptr<FolderHistory>& folderHistRight, @@ -20,5 +22,4 @@ void showSyncBatchDlg(const wxString& referenceFile, size_t execFinishedhistoryMax); } - #endif // BATCHCONFIG_H_INCLUDED diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp index 0c45db3f..7254f570 100644 --- a/ui/batch_status_handler.cpp +++ b/ui/batch_status_handler.cpp @@ -5,18 +5,17 @@ // ************************************************************************** #include "batch_status_handler.h" -#include "msg_popup.h" #include <wx/ffile.h> -#include <wx/msgdlg.h> -#include "../lib/ffs_paths.h" #include <zen/file_handling.h> -#include "../lib/resolve_path.h" +#include <zen/file_traverser.h> #include <wx+/string_conv.h> #include <wx+/app_main.h> -#include <zen/file_traverser.h> -#include <zen/time.h> -#include "exec_finished_box.h" +#include <wx+/format_unit.h> #include <wx+/shell_execute.h> +#include "msg_popup.h" +#include "exec_finished_box.h" +#include "../lib/ffs_paths.h" +#include "../lib/resolve_path.h" #include "../lib/status_handler_impl.h" using namespace zen; @@ -24,7 +23,7 @@ using namespace zen; namespace { -class FindLogfiles : public zen::TraverseCallback +class FindLogfiles : public TraverseCallback { public: FindLogfiles(const Zstring& prefix, std::vector<Zstring>& logfiles) : prefix_(prefix), logfiles_(logfiles) {} @@ -57,31 +56,47 @@ public: { logFile.Open(toWx(logfileName), L"w"); if (!logFile.IsOpened()) - throw FileError(_("Unable to create log file!") + L"\"" + logfileName + L"\""); + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(logfileName))); //write header - wxString headerLine = wxString(L"FreeFileSync - ") + _("Batch execution") + L" - " + formatTime<wxString>(FORMAT_DATE); - - logFile.Write(headerLine + wxChar('\n')); + const wxString& headerLine = wxString(L"FreeFileSync - ") + _("Batch execution") + L" - " + formatTime<wxString>(FORMAT_DATE); + logFile.Write(headerLine + L'\n'); logFile.Write(wxString().Pad(headerLine.Len(), L'=') + L'\n'); - logItemStart = formatTime<wxString>(L"[%X] ") + _("Start"); + //logItemStart = formatTime<wxString>(L"[%X] ") + _("Start"); totalTime.Start(); //measure total time } - void writeLog(const ErrorLog& log, const std::wstring& finalStatus) + void writeLog(const ErrorLog& log, const std::wstring& finalStatus, + int itemsSynced, Int64 dataSynced, + int itemsTotal, Int64 dataTotal) { - const size_t sepLineLen = finalStatus.size(); + //assemble results box + std::vector<wxString> results; + results.push_back(finalStatus); + results.push_back(L""); + if (itemsTotal != 0 || dataTotal != 0) //=: sync phase was reached and there were actual items to sync + { + results.push_back(L" " + _("Items processed:") + L" " + toStringSep(itemsSynced) + L" (" + filesizeToShortString(dataSynced) + L")"); + + if (itemsSynced != itemsTotal || + dataSynced != dataTotal) + results.push_back(L" " + _("Items remaining:") + L" " + toStringSep(itemsTotal - itemsSynced) + L" (" + filesizeToShortString(dataTotal - dataSynced) + L")"); + } + results.push_back(L" " + _("Total time:") + L" " + wxTimeSpan::Milliseconds(totalTime.Time()).Format()); + + //write results box + size_t sepLineLen = 0; + std::for_each(results.begin(), results.end(), [&](const wxString& str) { sepLineLen = std::max(sepLineLen, str.size()); }); - //result + statistics - logFile.Write(wxString().Pad(sepLineLen, L'_') + L'\n'); - logFile.Write(L"\n" + finalStatus + L"\n"); + logFile.Write(wxString().Pad(sepLineLen, L'_') + L"\n\n"); + std::for_each(results.begin(), results.end(), [&](const wxString& str) { logFile.Write(str + L'\n'); }); logFile.Write(wxString().Pad(sepLineLen, L'_') + L"\n\n"); - logFile.Write(logItemStart + L"\n\n"); + //logFile.Write(logItemStart + L"\n\n"); - //write actual logfile + //write log items const auto& entries = log.getEntries(); for (auto iter = entries.begin(); iter != entries.end(); ++iter) { @@ -90,9 +105,9 @@ public: logFile.Write(L'\n'); } - //write footer - logFile.Write(L'\n'); - logFile.Write(formatTime<wxString>(L"[%X] ") + _("Stop") + L" (" + _("Total time:") + L" " + wxTimeSpan::Milliseconds(totalTime.Time()).Format() + L")\n"); + ////write footer + //logFile.Write(L'\n'); + //logFile.Write(formatTime<wxString>(L"[%X] ") + _("Stop") + L" (" + _("Total time:") + L" " + wxTimeSpan::Milliseconds(totalTime.Time()).Format() + L")\n"); } void limitLogfileCount(size_t maxCount) const //throw() @@ -111,7 +126,7 @@ public: std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end()); //take advantage of logfile naming convention to find oldest files std::for_each(logFiles.begin(), logFiles.end() - maxCount, - [](const Zstring& filename) { try { zen::removeFile(filename); } catch (FileError&) {} }); + [](const Zstring& filename) { try { removeFile(filename); } catch (FileError&) {} }); } //Zstring getLogfileName() const { return logfileName; } @@ -121,23 +136,20 @@ private: { //create logfile directory Zstring logfileDir = logfileDirectory.empty() ? - zen::getConfigDir() + Zstr("Logs") : - zen::getFormattedDirectoryName(logfileDirectory); + getConfigDir() + Zstr("Logs") : + getFormattedDirectoryName(logfileDirectory); - if (!zen::dirExists(logfileDir)) - zen::createDirectory(logfileDir); //throw FileError; create recursively if necessary + if (!dirExists(logfileDir)) + createDirectory(logfileDir); //throw FileError; create recursively if necessary //assemble logfile name - if (!endsWith(logfileDir, FILE_NAME_SEPARATOR)) - logfileDir += FILE_NAME_SEPARATOR; - - const Zstring logfileName = logfileDir + toZ(jobName) + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S")); + const Zstring logfileName = appendSeparator(logfileDir) + toZ(jobName) + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S")); //ensure uniqueness Zstring output = logfileName + Zstr(".log"); - for (int i = 1; zen::somethingExists(output); ++i) - output = logfileName + Zstr('_') + zen::numberTo<Zstring>(i) + Zstr(".log"); + for (int i = 1; somethingExists(output); ++i) + output = logfileName + Zstr('_') + numberTo<Zstring>(i) + Zstr(".log"); return output; } @@ -145,7 +157,6 @@ private: const Zstring logfileName; wxFFile logFile; wxStopWatch totalTime; - wxString logItemStart; }; @@ -163,21 +174,18 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, showFinalResults(showProgress), //=> exit immediately or wait when finished switchToGuiRequested(false), handleError_(handleError), - currentProcess(StatusHandler::PROCESS_NONE), returnValue(returnVal), - syncStatusFrame(*this, nullptr, SyncStatus::SCANNING, showProgress, jobName, execWhenFinished, execFinishedHistory) + syncStatusFrame(*this, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory) { - if (logFileCountMax > 0) - { + if (logFileCountMax > 0) //init log file: starts internal timer! if (!tryReportingError([&] { - logFile = std::make_shared<LogFile>(toZ(logfileDirectory), jobName); //throw FileError + logFile.reset(new LogFile(toZ(logfileDirectory), jobName)); //throw FileError logFile->limitLogfileCount(logFileCountMax); //throw() }, *this)) - { - returnValue = -7; - throw BatchAbortProcess(); - } + { + returnValue = -7; + throw BatchAbortProcess(); } //::wxSetEnv(L"logfile", logFile->getLogfileName()); @@ -194,7 +202,7 @@ BatchStatusHandler::~BatchStatusHandler() { returnValue = -4; finalStatus = _("Synchronization aborted!"); - errorLog.logMsg(finalStatus, TYPE_FATAL_ERROR); + errorLog.logMsg(finalStatus, TYPE_ERROR); } else if (totalErrors > 0) { @@ -204,14 +212,20 @@ BatchStatusHandler::~BatchStatusHandler() } else { - finalStatus = _("Synchronization completed successfully!"); + if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! + getDataTotal (PHASE_SYNCHRONIZING) == 0) + finalStatus = _("Nothing to synchronize!"); //even if "ignored conflicts" occurred! + else + finalStatus = _("Synchronization completed successfully!"); errorLog.logMsg(finalStatus, TYPE_INFO); } //print the results list: logfile if (logFile.get()) { - logFile->writeLog(errorLog, finalStatus); + logFile->writeLog(errorLog, finalStatus, + getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING), + getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING)); logFile.reset(); //close file now: user may do something with it in "on completion" } @@ -240,19 +254,18 @@ BatchStatusHandler::~BatchStatusHandler() shellExecute(finalCommand); } - if (showFinalResults) //warning: wxWindow::Show() is called within processHasFinished()! { //notify about (logical) application main window => program won't quit, but stay on this dialog - zen::setMainWindow(syncStatusFrame.getAsWindow()); + setMainWindow(syncStatusFrame.getAsWindow()); //notify to syncStatusFrame that current process has ended if (abortIsRequested()) - syncStatusFrame.processHasFinished(SyncStatus::ABORTED, errorLog); //enable okay and close events + syncStatusFrame.processHasFinished(SyncStatus::RESULT_ABORTED, errorLog); //enable okay and close events else if (totalErrors > 0) - syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_ERROR, errorLog); + syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_ERROR, errorLog); else - syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS, errorLog); + syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_SUCCESS, errorLog); } else syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is main window => program will quit directly @@ -260,68 +273,36 @@ BatchStatusHandler::~BatchStatusHandler() } -void BatchStatusHandler::initNewProcess(int objectsTotal, zen::Int64 dataTotal, StatusHandler::Process processID) +void BatchStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, ProcessCallback::Phase phaseID) { - currentProcess = processID; - - switch (currentProcess) - { - case StatusHandler::PROCESS_SCANNING: - syncStatusFrame.initNewProcess(SyncStatus::SCANNING, 0, 0); //initialize some gui elements (remaining time, speed) - break; - case StatusHandler::PROCESS_COMPARING_CONTENT: - syncStatusFrame.initNewProcess(SyncStatus::COMPARING_CONTENT, objectsTotal, dataTotal); - break; - case StatusHandler::PROCESS_SYNCHRONIZING: - syncStatusFrame.initNewProcess(SyncStatus::SYNCHRONIZING, objectsTotal, dataTotal); - break; - case StatusHandler::PROCESS_NONE: - assert(false); - break; - } + StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); + syncStatusFrame.initNewPhase(); //call after "StatusHandler::initNewPhase" } void BatchStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) { - switch (currentProcess) + StatusHandler::updateProcessedData(objectsDelta, dataDelta); + + switch (currentPhase()) { - case StatusHandler::PROCESS_SCANNING: - syncStatusFrame.incScannedObjects_NoUpdate(objectsDelta); //throw () - break; - case StatusHandler::PROCESS_COMPARING_CONTENT: - case StatusHandler::PROCESS_SYNCHRONIZING: - syncStatusFrame.incProcessedData_NoUpdate(objectsDelta, dataDelta); - break; - case StatusHandler::PROCESS_NONE: + case ProcessCallback::PHASE_NONE: assert(false); + case ProcessCallback::PHASE_SCANNING: + break; + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + syncStatusFrame.reportCurrentBytes(getDataCurrent(currentPhase())); break; } - //note: this method should NOT throw in order to properly allow undoing setting of statistics! } -void BatchStatusHandler::updateTotalData(int objectsDelta, Int64 dataDelta) -{ - assert(currentProcess != PROCESS_SCANNING); - syncStatusFrame.incTotalData_NoUpdate(objectsDelta, dataDelta); -} - - -void BatchStatusHandler::reportStatus(const std::wstring& text) -{ - syncStatusFrame.setStatusText_NoUpdate(text); - requestUiRefresh(); //throw AbortThisProcess -} - - void BatchStatusHandler::reportInfo(const std::wstring& text) { + StatusHandler::reportInfo(text); errorLog.logMsg(text, TYPE_INFO); - - syncStatusFrame.setStatusText_NoUpdate(text); - requestUiRefresh(); //throw AbortThisProcess } @@ -340,8 +321,9 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& forceUiRefresh(); bool dontWarnAgain = false; - switch (showWarningDlg(ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_SWITCH | ReturnWarningDlg::BUTTON_ABORT, - warningMessage + wxT("\n\n") + _("Press \"Switch\" to open FreeFileSync GUI mode."), + switch (showWarningDlg(syncStatusFrame.getAsWindow(), + ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_SWITCH | ReturnWarningDlg::BUTTON_ABORT, + warningMessage + L"\n\n" + _("Press \"Switch\" to resolve issues in FreeFileSync main dialog."), dontWarnAgain)) { case ReturnWarningDlg::BUTTON_ABORT: @@ -349,7 +331,7 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& break; case ReturnWarningDlg::BUTTON_SWITCH: - errorLog.logMsg(_("Switching to FreeFileSync GUI mode..."), TYPE_INFO); + errorLog.logMsg(_("Switching to FreeFileSync main dialog..."), TYPE_INFO); switchToGuiRequested = true; abortThisProcess(); break; @@ -365,7 +347,7 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& abortThisProcess(); break; - case xmlAccess::ON_ERROR_IGNORE: //no unhandled error situation! + case xmlAccess::ON_ERROR_IGNORE: break; } } @@ -381,9 +363,9 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, - errorMessage, - &ignoreNextErrors)) + switch (showErrorDlg(syncStatusFrame.getAsWindow(), + ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, + errorMessage, &ignoreNextErrors)) { case ReturnErrorDlg::BUTTON_IGNORE: if (ignoreNextErrors) //falsify only @@ -428,9 +410,9 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_ABORT, - errorMessage, - &ignoreNextErrors)) + switch (showErrorDlg(syncStatusFrame.getAsWindow(), + ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_ABORT, + errorMessage, &ignoreNextErrors)) { case ReturnErrorDlg::BUTTON_IGNORE: if (ignoreNextErrors) //falsify only @@ -459,12 +441,12 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) void BatchStatusHandler::forceUiRefresh() { - syncStatusFrame.updateStatusDialogNow(); + syncStatusFrame.updateProgress(); } void BatchStatusHandler::abortThisProcess() { - requestAbortion(); + requestAbortion(); //just make sure... throw BatchAbortProcess(); //abort can be triggered by syncStatusFrame } diff --git a/ui/batch_status_handler.h b/ui/batch_status_handler.h index 0657e881..cc23019e 100644 --- a/ui/batch_status_handler.h +++ b/ui/batch_status_handler.h @@ -14,13 +14,12 @@ #include "switch_to_gui.h" class LogFile; -class SyncStatus; //Exception class used to abort the "compare" and "sync" process class BatchAbortProcess {}; -class BatchStatusHandler : public StatusHandler +class BatchStatusHandler : public zen::StatusHandler { public: BatchStatusHandler(bool showProgress, //defines: -start minimized and -quit immediately when finished @@ -34,10 +33,8 @@ public: std::vector<std::wstring>& execFinishedHistory); ~BatchStatusHandler(); - virtual void initNewProcess (int objectsTotal, zen::Int64 dataTotal, Process processID); + virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta); - virtual void updateTotalData (int objectsDelta, zen::Int64 dataDelta); - virtual void reportStatus(const std::wstring& text); virtual void reportInfo(const std::wstring& text); virtual void forceUiRefresh(); @@ -46,18 +43,17 @@ public: virtual void reportFatalError(const std::wstring& errorMessage); private: - virtual void abortThisProcess(); + virtual void abortThisProcess(); //throw BatchAbortProcess const zen::SwitchToGui& switchBatchToGui_; //functionality to change from batch mode to GUI mode bool showFinalResults; bool switchToGuiRequested; xmlAccess::OnError handleError_; zen::ErrorLog errorLog; //list of non-resolved errors and warnings - Process currentProcess; int& returnValue; SyncStatus syncStatusFrame; //the window managed by SyncStatus has longer lifetime than this handler! - std::shared_ptr<LogFile> logFile; //optional! + std::unique_ptr<LogFile> logFile; //optional! }; diff --git a/ui/check_version.cpp b/ui/check_version.cpp index ba922f97..37fa16ff 100644 --- a/ui/check_version.cpp +++ b/ui/check_version.cpp @@ -82,27 +82,30 @@ bool newerVersionExists(const wxString& onlineVersion) } -void zen::checkForUpdateNow() +void zen::checkForUpdateNow(wxWindow* parent) { wxString onlineVersion; if (!getOnlineVersion(onlineVersion)) { - wxMessageBox(_("Unable to connect to sourceforge.net!"), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(_("Unable to connect to sourceforge.net!"), _("Error"), wxOK | wxICON_ERROR, parent); return; } if (newerVersionExists(onlineVersion)) { - const int rv = wxMessageBox(wxString(_("A newer version of FreeFileSync is available:")) + wxT(" v") + onlineVersion + wxT(". ") + _("Download now?"), _("Information"), wxYES_NO | wxICON_QUESTION); + const int rv = wxMessageBox(_("A newer version of FreeFileSync is available:") + L" v" + onlineVersion + L". " + _("Download now?"), + _("Information"), + wxYES_NO | wxICON_QUESTION, + parent); if (rv == wxYES) wxLaunchDefaultBrowser(wxString(L"http://sourceforge.net/projects/freefilesync/files/freefilesync/v") + onlineVersion + L"/"); } else - wxMessageBox(_("FreeFileSync is up to date!"), _("Information"), wxICON_INFORMATION); + wxMessageBox(_("FreeFileSync is up to date!"), _("Information"), wxICON_INFORMATION, parent); } -void zen::checkForUpdatePeriodically(long& lastUpdateCheck) +void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck) { #ifdef FFS_LINUX if (!zen::isPortableVersion()) //don't check for updates in installer version -> else: handled by .deb @@ -113,14 +116,14 @@ void zen::checkForUpdatePeriodically(long& lastUpdateCheck) { if (lastUpdateCheck == 0) { - const bool checkRegularly = showQuestionDlg(ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_NO, + const bool checkRegularly = showQuestionDlg(parent, ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_NO, _("Do you want FreeFileSync to automatically check for updates every week?") + L"\n" + _("(Requires an Internet connection!)")) == ReturnQuestionDlg::BUTTON_YES; if (checkRegularly) { lastUpdateCheck = 123; //some old date (few seconds after 1970) - checkForUpdatePeriodically(lastUpdateCheck); //check for updates now + checkForUpdatePeriodically(parent, lastUpdateCheck); //check for updates now } else lastUpdateCheck = -1; //don't check for updates anymore @@ -135,7 +138,10 @@ void zen::checkForUpdatePeriodically(long& lastUpdateCheck) if (newerVersionExists(onlineVersion)) { - const int rv = wxMessageBox(wxString(_("A newer version of FreeFileSync is available:")) + wxT(" v") + onlineVersion + wxT(". ") + _("Download now?"), _("Information"), wxYES_NO | wxICON_QUESTION); + const int rv = wxMessageBox(_("A newer version of FreeFileSync is available:") + L" v" + onlineVersion + L". " + _("Download now?"), + _("Information"), + wxYES_NO | wxICON_QUESTION, + parent); if (rv == wxYES) wxLaunchDefaultBrowser(wxString(L"http://sourceforge.net/projects/freefilesync/files/freefilesync/v") + onlineVersion + L"/"); } diff --git a/ui/check_version.h b/ui/check_version.h index bbf8d7cb..5a0bbd73 100644 --- a/ui/check_version.h +++ b/ui/check_version.h @@ -7,12 +7,14 @@ #ifndef UPDATEVERSION_H_INCLUDED #define UPDATEVERSION_H_INCLUDED +#include <wx/window.h> + namespace zen { -void checkForUpdateNow(); +void checkForUpdateNow(wxWindow* parent); -void checkForUpdatePeriodically(long& lastUpdateCheck); +void checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck); } #endif // UPDATEVERSION_H_INCLUDED diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp index 465a1b6f..fdb72161 100644 --- a/ui/custom_grid.cpp +++ b/ui/custom_grid.cpp @@ -91,6 +91,12 @@ std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(Grid& grid) //returns range [from } +Zstring getExtension(const Zstring& shortName) +{ + return contains(shortName, Zchar('.')) ? afterLast(shortName, Zchar('.')) : Zstring(); +}; + + class IconUpdater; class GridEventManager; @@ -121,7 +127,6 @@ private: Grid& grid_; }; - //######################################################################################################## template <SelectedSide side> @@ -254,6 +259,7 @@ private: struct GetTextValue : public FSObjectVisitor { GetTextValue(ColumnTypeRim colType, const FileSystemObject& fso) : colType_(colType), fsObj_(fso) {} + virtual void visit(const FileMapping& fileObj) { switch (colType_) @@ -279,7 +285,7 @@ private: value = zen::utcToLocalTimeString(fileObj.getLastWriteTime<side>()); break; case COL_TYPE_EXTENSION: //file extension - value = toWx(fileObj.getExtension<side>()); + value = toWx(getExtension(fileObj.getShortName<side>())); break; } } @@ -309,7 +315,7 @@ private: value = zen::utcToLocalTimeString(linkObj.getLastWriteTime<side>()); break; case COL_TYPE_EXTENSION: //file extension - value = wxEmptyString; + value = toWx(getExtension(linkObj.getShortName<side>())); break; } } @@ -543,30 +549,32 @@ private: virtual wxString getToolTip(size_t row, ColumnType colType) const { wxString toolTip; + const FileSystemObject* fsObj = getRawData(row); if (fsObj && !fsObj->isEmpty<side>()) { + toolTip = toWx(gridDataView_->getFolderPairCount() > 1 ? //gridDataView_ bound in this path + fsObj->getFullName<side>() : + fsObj->getRelativeName<side>()); + struct AssembleTooltip : public FSObjectVisitor { AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {} virtual void visit(const FileMapping& fileObj) { - tipMsg_ = copyStringTo<wxString>(std::wstring() + fileObj.getRelativeName<side>() + L"\n" + - _("Size") + L": " + zen::filesizeToShortString(to<Int64>(fileObj.getFileSize<side>())) + L"\n" + - _("Date") + L": " + zen::utcToLocalTimeString(fileObj.getLastWriteTime<side>())); + tipMsg_ += L"\n" + + _("Size") + L": " + zen::filesizeToShortString(to<Int64>(fileObj.getFileSize<side>())) + L"\n" + + _("Date") + L": " + zen::utcToLocalTimeString(fileObj.getLastWriteTime<side>()); } virtual void visit(const SymLinkMapping& linkObj) { - tipMsg_ = copyStringTo<wxString>(std::wstring() + linkObj.getRelativeName<side>() + L"\n" + - _("Date") + L": " + zen::utcToLocalTimeString(linkObj.getLastWriteTime<side>())); + tipMsg_ += L"\n" + + _("Date") + L": " + zen::utcToLocalTimeString(linkObj.getLastWriteTime<side>()); } - virtual void visit(const DirMapping& dirObj) - { - tipMsg_ = toWx(dirObj.getRelativeName<side>()); - } + virtual void visit(const DirMapping& dirObj) {} wxString& tipMsg_; } assembler(toolTip); @@ -700,7 +708,7 @@ public: { case BLOCKPOS_CHECK_BOX: { - const FileSystemObject* fsObj = getRawData(rowFrom); + const FileSystemObject* fsObj = getRawData(dragSelection->first); const bool setIncluded = fsObj ? !fsObj->isActive() : true; CheckRowsEvent evt(rowFrom, rowTo, setIncluded); diff --git a/ui/exec_finished_box.cpp b/ui/exec_finished_box.cpp index a8ee9f4a..edc57d58 100644 --- a/ui/exec_finished_box.cpp +++ b/ui/exec_finished_box.cpp @@ -192,7 +192,8 @@ void ExecFinishedBox::setValueAndUpdateList(const std::wstring& value) void ExecFinishedBox::OnSelection(wxCommandEvent& event) { wxCommandEvent dummy2(wxEVT_REPLACE_BUILT_IN_COMMANDS); //we cannot replace built-in commands at this position in call stack, so defer to a later time! - GetEventHandler()->AddPendingEvent(dummy2); // + if (auto handler = GetEventHandler()) + handler->AddPendingEvent(dummy2); event.Skip(); } diff --git a/ui/folder_history_box.cpp b/ui/folder_history_box.cpp index 76ed785e..72204c3a 100644 --- a/ui/folder_history_box.cpp +++ b/ui/folder_history_box.cpp @@ -146,18 +146,12 @@ void FolderHistoryBox::OnMouseWheel(wxMouseEvent& event) //redirect to parent scrolled window! wxWindow* wnd = this; - for (;;) - { - wnd = wnd->GetParent(); - if (!wnd) - break; - + while ((wnd = wnd->GetParent()) != nullptr) //silence MSVC warning if (dynamic_cast<wxScrolledWindow*>(wnd) != nullptr) { wnd->GetEventHandler()->AddPendingEvent(event); break; } - } // event.Skip(); } diff --git a/ui/folder_pair.h b/ui/folder_pair.h index 7b1e7643..d80d8a05 100644 --- a/ui/folder_pair.h +++ b/ui/folder_pair.h @@ -151,7 +151,7 @@ private: CompConfig cmpCfg = altCompConfig.get() ? *altCompConfig : mainCfg.cmpConfig; - if (showCompareCfgDialog(cmpCfg) == ReturnSmallDlg::BUTTON_OKAY) + if (showCompareCfgDialog(getParentWindow(), cmpCfg) == ReturnSmallDlg::BUTTON_OKAY) { altCompConfig = std::make_shared<CompConfig>(cmpCfg); refreshButtons(); @@ -167,7 +167,8 @@ private: CompConfig cmpCfg = altCompConfig.get() ? *altCompConfig : mainCfg.cmpConfig; SyncConfig syncCfg = altSyncConfig.get() ? *altSyncConfig : mainCfg.syncCfg; - if (showSyncConfigDlg(cmpCfg.compareVar, + if (showSyncConfigDlg(getParentWindow(), + cmpCfg.compareVar, syncCfg, nullptr, nullptr) == ReturnSyncConfig::BUTTON_OKAY) //optional input parameter @@ -183,7 +184,8 @@ private: { FilterConfig localFiltTmp = localFilter; - if (showFilterDialog(false, //is local filter dialog + if (showFilterDialog(getParentWindow(), + false, //is local filter dialog localFiltTmp) == ReturnSmallDlg::BUTTON_OKAY) { localFilter = localFiltTmp; diff --git a/ui/grid_view.cpp b/ui/grid_view.cpp index 2a3e84e3..80ff895c 100644 --- a/ui/grid_view.cpp +++ b/ui/grid_view.cpp @@ -267,10 +267,9 @@ GridView::StatusSyncPreview GridView::updateSyncPreview(bool hideFiltered, //map } -void GridView::getAllFileRef(const std::set<size_t>& rows, std::vector<FileSystemObject*>& output) +std::vector<FileSystemObject*> GridView::getAllFileRef(const std::set<size_t>& rows) { - output.clear(); - output.reserve(rows.size()); + std::vector<FileSystemObject*> output; auto iterLast = rows.lower_bound(rowsOnView()); //loop over valid rows only! std::for_each(rows.begin(), iterLast, @@ -279,6 +278,7 @@ void GridView::getAllFileRef(const std::set<size_t>& rows, std::vector<FileSyste if (FileSystemObject* fsObj = FileSystemObject::retrieve(viewRef[pos])) output.push_back(fsObj); }); + return output; } @@ -336,6 +336,13 @@ void GridView::setData(FolderComparison& folderCmp) std::vector<RefIndex>().swap(sortedRef); // currentSort.reset(); + folderPairCount = std::count_if(begin(folderCmp), end(folderCmp), + [](const BaseDirMapping& baseObj) //count non-empty pairs to distinguish single/multiple folder pair cases + { + return !baseObj.getBaseDirPf<LEFT_SIDE >().empty() || + !baseObj.getBaseDirPf<RIGHT_SIDE>().empty(); + }); + for (auto iter = begin(folderCmp); iter != end(folderCmp); ++iter) SerializeHierarchy(sortedRef, iter - begin(folderCmp)).execute(*iter); } @@ -499,7 +506,7 @@ void GridView::sortView(ColumnTypeRim type, bool onLeft, bool ascending) viewRef.clear(); rowPositions.clear(); rowPositionsFirstChild.clear(); - currentSort.reset(new SortInfo(type, onLeft, ascending)); + currentSort = make_unique<SortInfo>(type, onLeft, ascending); switch (type) { diff --git a/ui/grid_view.h b/ui/grid_view.h index fc828f91..c830224b 100644 --- a/ui/grid_view.h +++ b/ui/grid_view.h @@ -18,6 +18,8 @@ namespace zen class GridView { public: + GridView() : folderPairCount(0) {} + //direct data access via row number const FileSystemObject* getObject(size_t row) const; //returns nullptr if object is not found; complexity: constant! /**/ @@ -26,7 +28,7 @@ public: size_t rowsTotal () const { return sortedRef.size(); } //total rows available //get references to FileSystemObject: no nullptr-check needed! Everything's bound. - void getAllFileRef(const std::set<size_t>& rows, std::vector<FileSystemObject*>& output); + std::vector<FileSystemObject*> getAllFileRef(const std::set<size_t>& rows); struct StatusCmpResult { @@ -115,6 +117,8 @@ public: ptrdiff_t findRowFirstChild(const HierarchyObject* hierObj) const; // find first child of DirMapping or BaseDirMapping *on sorted sub view* //"hierObj" may be invalid, it is NOT dereferenced, return < 0 if not found + size_t getFolderPairCount() const { return folderPairCount; } //count non-empty pairs to distinguish single/multiple folder pair cases + private: struct RefIndex { @@ -129,8 +133,8 @@ private: zen::hash_map<FileSystemObject::ObjectIdConst, size_t> rowPositions; //find row positions on sortedRef directly - zen::hash_map<const HierarchyObject*, size_t> rowPositionsFirstChild; //find first child on sortedRef of a hierarchy object - //NEVER DEREFERENCE HierarchyObject*!!! lookup only! + zen::hash_map<const void*, size_t> rowPositionsFirstChild; //find first child on sortedRef of a hierarchy object + //void* instead of HierarchyObject*: these are weak pointers and should *never be dereferenced*! std::vector<FileSystemObject::ObjectId> viewRef; //partial view on sortedRef /* /|\ @@ -141,6 +145,8 @@ private: | (setData...) | */ //std::shared_ptr<FolderComparison> folderCmp; //actual comparison data: owned by GridView! + size_t folderPairCount; //number of non-empty folder pairs + class SerializeHierarchy; diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp index 9caa466a..4c42caf1 100644 --- a/ui/gui_generated.cpp +++ b/ui/gui_generated.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Mar 17 2012) +// C++ code generated with wxFormBuilder (version Apr 10 2012) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -928,23 +928,11 @@ CompareStatusGenerated::CompareStatusGenerated( wxWindow* parent, wxWindowID id, bSizer154->Add( m_staticTextFilesRemaining, 0, wxALIGN_BOTTOM, 5 ); - m_staticText117 = new wxStaticText( this, wxID_ANY, _("("), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText117->Wrap( -1 ); - m_staticText117->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - - bSizer154->Add( m_staticText117, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - m_staticTextDataRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextDataRemaining->Wrap( -1 ); m_staticTextDataRemaining->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - bSizer154->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM, 5 ); - - m_staticText118 = new wxStaticText( this, wxID_ANY, _(")"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText118->Wrap( -1 ); - m_staticText118->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - - bSizer154->Add( m_staticText118, 0, wxALIGN_BOTTOM, 5 ); + bSizer154->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); bSizerFilesRemaining->Add( bSizer154, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); @@ -1413,7 +1401,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer68->Add( m_buttonLoad, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_button6 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button6 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button6->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer68->Add( m_button6, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1881,13 +1869,13 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer291; bSizer291 = new wxBoxSizer( wxHORIZONTAL ); - m_buttonOK = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOK = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonOK->SetDefault(); m_buttonOK->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) ); bSizer291->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_button16 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button16 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button16->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer291->Add( m_button16, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -2042,13 +2030,13 @@ CmpCfgDlgGenerated::CmpCfgDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer22; bSizer22 = new wxBoxSizer( wxHORIZONTAL ); - m_button10 = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button10 = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button10->SetDefault(); m_button10->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) ); bSizer22->Add( m_button10, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_button6 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button6 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button6->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer22->Add( m_button6, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -2064,6 +2052,8 @@ CmpCfgDlgGenerated::CmpCfgDlgGenerated( wxWindow* parent, wxWindowID id, const w this->Layout(); bSizer136->Fit( this ); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( CmpCfgDlgGenerated::OnClose ) ); m_radioBtnSizeDate->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( CmpCfgDlgGenerated::OnTimeSize ), NULL, this ); @@ -2168,23 +2158,11 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, bSizerItemsProc->Add( m_staticTextProcessedObj, 0, wxALIGN_BOTTOM, 5 ); - m_staticText98 = new wxStaticText( m_panelProgress, wxID_ANY, _("("), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText98->Wrap( -1 ); - m_staticText98->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - - bSizerItemsProc->Add( m_staticText98, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - m_staticTextDataProcessed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextDataProcessed->Wrap( -1 ); m_staticTextDataProcessed->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - bSizerItemsProc->Add( m_staticTextDataProcessed, 0, wxALIGN_BOTTOM, 5 ); - - m_staticText99 = new wxStaticText( m_panelProgress, wxID_ANY, _(")"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText99->Wrap( -1 ); - m_staticText99->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - - bSizerItemsProc->Add( m_staticText99, 0, wxALIGN_BOTTOM, 5 ); + bSizerItemsProc->Add( m_staticTextDataProcessed, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); fgSizer10->Add( bSizerItemsProc, 0, wxALIGN_BOTTOM, 5 ); @@ -2203,38 +2181,26 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, bSizerItemsRem->Add( m_staticTextRemainingObj, 0, wxALIGN_BOTTOM, 5 ); - m_staticText96 = new wxStaticText( m_panelProgress, wxID_ANY, _("("), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText96->Wrap( -1 ); - m_staticText96->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - - bSizerItemsRem->Add( m_staticText96, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - m_staticTextDataRemaining = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextDataRemaining->Wrap( -1 ); m_staticTextDataRemaining->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - bSizerItemsRem->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM, 5 ); - - m_staticText97 = new wxStaticText( m_panelProgress, wxID_ANY, _(")"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText97->Wrap( -1 ); - m_staticText97->SetFont( wxFont( 9, 70, 90, 90, false, wxEmptyString ) ); - - bSizerItemsRem->Add( m_staticText97, 0, wxALIGN_BOTTOM, 5 ); + bSizerItemsRem->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); fgSizer10->Add( bSizerItemsRem, 0, wxALIGN_BOTTOM, 5 ); - m_staticTextLabelElapsedTime = new wxStaticText( m_panelProgress, wxID_ANY, _("Elapsed time:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextLabelElapsedTime->Wrap( -1 ); - m_staticTextLabelElapsedTime->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); + m_staticText84 = new wxStaticText( m_panelProgress, wxID_ANY, _("Speed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText84->Wrap( -1 ); + m_staticText84->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); - fgSizer10->Add( m_staticTextLabelElapsedTime, 0, wxALIGN_BOTTOM, 5 ); + fgSizer10->Add( m_staticText84, 0, wxALIGN_BOTTOM, 5 ); - m_staticTextTimeElapsed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextTimeElapsed->Wrap( -1 ); - m_staticTextTimeElapsed->SetFont( wxFont( 9, 70, 90, 92, false, wxEmptyString ) ); + m_staticTextSpeed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextSpeed->Wrap( -1 ); + m_staticTextSpeed->SetFont( wxFont( 9, 70, 90, 92, false, wxEmptyString ) ); - fgSizer10->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM, 5 ); + fgSizer10->Add( m_staticTextSpeed, 0, wxALIGN_BOTTOM, 5 ); m_staticTextLabelRemTime = new wxStaticText( m_panelProgress, wxID_ANY, _("Remaining time:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextLabelRemTime->Wrap( -1 ); @@ -2248,17 +2214,17 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, fgSizer10->Add( m_staticTextRemTime, 0, wxALIGN_BOTTOM, 5 ); - m_staticText84 = new wxStaticText( m_panelProgress, wxID_ANY, _("Speed:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText84->Wrap( -1 ); - m_staticText84->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); + m_staticTextLabelElapsedTime = new wxStaticText( m_panelProgress, wxID_ANY, _("Elapsed time:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextLabelElapsedTime->Wrap( -1 ); + m_staticTextLabelElapsedTime->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); - fgSizer10->Add( m_staticText84, 0, wxALIGN_BOTTOM, 5 ); + fgSizer10->Add( m_staticTextLabelElapsedTime, 0, wxALIGN_BOTTOM, 5 ); - m_staticTextSpeed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextSpeed->Wrap( -1 ); - m_staticTextSpeed->SetFont( wxFont( 9, 70, 90, 92, false, wxEmptyString ) ); + m_staticTextTimeElapsed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeElapsed->Wrap( -1 ); + m_staticTextTimeElapsed->SetFont( wxFont( 9, 70, 90, 92, false, wxEmptyString ) ); - fgSizer10->Add( m_staticTextSpeed, 0, wxALIGN_BOTTOM, 5 ); + fgSizer10->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM, 5 ); bSizerProgressStat->Add( fgSizer10, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); @@ -2320,7 +2286,7 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, bSizer28->Add( 0, 0, 1, 0, 5 ); - m_buttonOK = new wxButton( m_panelBackground, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( 100,30 ), 0 ); + m_buttonOK = new wxButton( m_panelBackground, wxID_OK, _("OK"), wxDefaultPosition, wxSize( 100,30 ), 0 ); m_buttonOK->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); m_buttonOK->Enable( false ); @@ -2331,7 +2297,7 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, bSizer28->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_buttonAbort = new wxButton( m_panelBackground, wxID_CANCEL, _("&Abort"), wxDefaultPosition, wxSize( 100,30 ), 0 ); + m_buttonAbort = new wxButton( m_panelBackground, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( 100,30 ), 0 ); m_buttonAbort->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer28->Add( m_buttonAbort, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -2675,7 +2641,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer31->Add( sbSizer14, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 5 ); - m_buttonOkay = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( 100,30 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( 100,30 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); @@ -2744,7 +2710,7 @@ ErrorDlgGenerated::ErrorDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer25->Add( m_buttonRetry, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - m_buttonAbort = new wxButton( this, wxID_CANCEL, _("&Abort"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonAbort = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonAbort->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer25->Add( m_buttonAbort, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); @@ -2756,6 +2722,8 @@ ErrorDlgGenerated::ErrorDlgGenerated( wxWindow* parent, wxWindowID id, const wxS this->SetSizer( bSizer24 ); this->Layout(); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( ErrorDlgGenerated::OnClose ) ); m_buttonIgnore->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ErrorDlgGenerated::OnIgnore ), NULL, this ); @@ -2814,7 +2782,7 @@ WarningDlgGenerated::WarningDlgGenerated( wxWindow* parent, wxWindowID id, const bSizer25->Add( m_buttonSwitch, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_buttonAbort = new wxButton( this, wxID_CANCEL, _("&Abort"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonAbort = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonAbort->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer25->Add( m_buttonAbort, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); @@ -2826,6 +2794,8 @@ WarningDlgGenerated::WarningDlgGenerated( wxWindow* parent, wxWindowID id, const this->SetSizer( bSizer24 ); this->Layout(); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( WarningDlgGenerated::OnClose ) ); m_buttonIgnore->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WarningDlgGenerated::OnIgnore ), NULL, this ); @@ -2884,7 +2854,7 @@ QuestionDlgGenerated::QuestionDlgGenerated( wxWindow* parent, wxWindowID id, con bSizer25->Add( m_buttonNo, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonCancel->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer25->Add( m_buttonCancel, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); @@ -2896,6 +2866,8 @@ QuestionDlgGenerated::QuestionDlgGenerated( wxWindow* parent, wxWindowID id, con this->SetSizer( bSizer24 ); this->Layout(); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( QuestionDlgGenerated::OnClose ) ); m_buttonYes->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( QuestionDlgGenerated::OnYes ), NULL, this ); @@ -2963,13 +2935,13 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer25; bSizer25 = new wxBoxSizer( wxHORIZONTAL ); - m_buttonOK = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOK = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonOK->SetDefault(); m_buttonOK->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) ); bSizer25->Add( m_buttonOK, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonCancel->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer25->Add( m_buttonCancel, 0, wxALL, 5 ); @@ -2984,6 +2956,8 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w this->SetSizer( bSizer24 ); this->Layout(); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DeleteDlgGenerated::OnClose ) ); m_checkBoxUseRecycler->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DeleteDlgGenerated::OnUseRecycler ), NULL, this ); @@ -3254,13 +3228,13 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer22->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - m_button10 = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button10 = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button10->SetDefault(); m_button10->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) ); bSizer22->Add( m_button10, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_button17 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button17 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button17->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer22->Add( m_button17, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); @@ -3441,13 +3415,13 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind bSizer97->Add( 0, 0, 1, 0, 5 ); - m_buttonOkay = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) ); bSizer97->Add( m_buttonOkay, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - m_button29 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button29 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button29->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer97->Add( m_button29, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -3460,6 +3434,8 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind this->Layout(); bSizer95->Fit( this ); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GlobalSettingsDlgGenerated::OnClose ) ); m_buttonResetDialogs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnResetDialogs ), NULL, this ); @@ -3493,7 +3469,7 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i wxBoxSizer* bSizer158; bSizer158 = new wxBoxSizer( wxHORIZONTAL ); - m_buttonStartSync = new zen::BitmapButton( this, wxID_ANY, _("Start"), wxDefaultPosition, wxSize( -1,40 ), 0 ); + m_buttonStartSync = new zen::BitmapButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1,40 ), 0 ); m_buttonStartSync->SetDefault(); m_buttonStartSync->SetFont( wxFont( 14, 70, 90, 92, false, wxEmptyString ) ); m_buttonStartSync->SetToolTip( _("Start synchronization") ); @@ -3659,7 +3635,7 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i bSizer142->Add( 10, 0, 1, 0, 5 ); - m_button16 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button16 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button16->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer142->Add( m_button16, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); @@ -3672,6 +3648,8 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i this->Layout(); bSizer134->Fit( this ); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncPreviewDlgGenerated::OnClose ) ); m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncPreviewDlgGenerated::OnStartSync ), NULL, this ); @@ -3752,7 +3730,7 @@ SearchDialogGenerated::SearchDialogGenerated( wxWindow* parent, wxWindowID id, c bSizer97->Add( m_buttonFindNext, 0, wxEXPAND|wxTOP|wxRIGHT, 5 ); - m_button29 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button29 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button29->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer97->Add( m_button29, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3763,7 +3741,8 @@ SearchDialogGenerated::SearchDialogGenerated( wxWindow* parent, wxWindowID id, c this->SetSizer( bSizer161 ); this->Layout(); - bSizer161->Fit( this ); + + this->Centre( wxBOTH ); // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SearchDialogGenerated::OnClose ) ); @@ -3807,13 +3786,13 @@ SelectTimespanDlgGenerated::SelectTimespanDlgGenerated( wxWindow* parent, wxWind bSizer97->Add( 0, 0, 1, wxEXPAND, 5 ); - m_buttonOkay = new wxButton( this, wxID_ANY, _("&OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( 10, 70, 90, 92, false, wxEmptyString ) ); bSizer97->Add( m_buttonOkay, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - m_button29 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button29 = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button29->SetFont( wxFont( 10, 70, 90, 90, false, wxEmptyString ) ); bSizer97->Add( m_button29, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); @@ -3829,6 +3808,8 @@ SelectTimespanDlgGenerated::SelectTimespanDlgGenerated( wxWindow* parent, wxWind this->Layout(); bSizer96->Fit( this ); + this->Centre( wxBOTH ); + // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SelectTimespanDlgGenerated::OnClose ) ); m_calendarFrom->Connect( wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler( SelectTimespanDlgGenerated::OnChangeSelectionFrom ), NULL, this ); diff --git a/ui/gui_generated.h b/ui/gui_generated.h index 8176dbf9..bd7c6be4 100644 --- a/ui/gui_generated.h +++ b/ui/gui_generated.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Mar 17 2012) +// C++ code generated with wxFormBuilder (version Apr 10 2012) // http://www.wxformbuilder.org/ // // PLEASE DO "NOT" EDIT THIS FILE! @@ -262,9 +262,7 @@ protected: wxBoxSizer* bSizerFilesRemaining; wxStaticText* m_staticText46; wxStaticText* m_staticTextFilesRemaining; - wxStaticText* m_staticText117; wxStaticText* m_staticTextDataRemaining; - wxStaticText* m_staticText118; wxBoxSizer* sSizerSpeed; wxStaticText* m_staticText104; wxStaticText* m_staticTextSpeed; @@ -527,21 +525,17 @@ protected: wxStaticText* m_staticTextLabelItemsProc; wxBoxSizer* bSizerItemsProc; wxStaticText* m_staticTextProcessedObj; - wxStaticText* m_staticText98; wxStaticText* m_staticTextDataProcessed; - wxStaticText* m_staticText99; wxStaticText* m_staticTextLabelItemsRem; wxBoxSizer* bSizerItemsRem; wxStaticText* m_staticTextRemainingObj; - wxStaticText* m_staticText96; wxStaticText* m_staticTextDataRemaining; - wxStaticText* m_staticText97; - wxStaticText* m_staticTextLabelElapsedTime; - wxStaticText* m_staticTextTimeElapsed; - wxStaticText* m_staticTextLabelRemTime; - wxStaticText* m_staticTextRemTime; wxStaticText* m_staticText84; wxStaticText* m_staticTextSpeed; + wxStaticText* m_staticTextLabelRemTime; + wxStaticText* m_staticTextRemTime; + wxStaticText* m_staticTextLabelElapsedTime; + wxStaticText* m_staticTextTimeElapsed; zen::Graph2D* m_panelGraph; wxBoxSizer* bSizerFinalStat; wxListbook* m_listbookResult; @@ -948,7 +942,7 @@ protected: public: - SearchDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Find"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE ); + SearchDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Find"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 384,97 ), long style = wxDEFAULT_DIALOG_STYLE ); ~SearchDialogGenerated(); }; diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp index 6e3c08a0..65e40410 100644 --- a/ui/gui_status_handler.cpp +++ b/ui/gui_status_handler.cpp @@ -5,13 +5,11 @@ // ************************************************************************** #include "gui_status_handler.h" -#include "small_dlgs.h" +#include <wx/wupdlock.h> +#include <wx+/shell_execute.h> #include "msg_popup.h" #include "main_dlg.h" -#include <wx/wupdlock.h> -#include <wx+/string_conv.h> #include "exec_finished_box.h" -#include <wx+/shell_execute.h> using namespace zen; using namespace xmlAccess; @@ -19,24 +17,24 @@ using namespace xmlAccess; CompareStatusHandler::CompareStatusHandler(MainDialog& dlg) : mainDlg(dlg), - ignoreErrors(false), - currentProcess(StatusHandler::PROCESS_NONE) + ignoreErrors(false) { - wxWindowUpdateLocker dummy(&mainDlg); //avoid display distortion + { + wxWindowUpdateLocker dummy(&mainDlg); //avoid display distortion - //prevent user input during "compare", do not disable maindialog since abort-button would also be disabled - mainDlg.disableAllElements(true); - mainDlg.compareStatus->init(); //clear old values + //prevent user input during "compare", do not disable maindialog since abort-button would also be disabled + mainDlg.disableAllElements(true); - //display status panel during compare - mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Show(); - mainDlg.auiMgr.Update(); - mainDlg.compareStatus->updateStatusPanelNow(); //clear gui flicker: window must be visible to make this work! + //display status panel during compare + mainDlg.compareStatus->init(*this); //clear old values before showing panel + mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Show(); + mainDlg.auiMgr.Update(); - //register abort button - mainDlg.m_buttonAbort->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CompareStatusHandler::OnAbortCompare), nullptr, this); - //register key event - mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CompareStatusHandler::OnKeyPressed), nullptr, this); + //register keys + mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CompareStatusHandler::OnKeyPressed), nullptr, this); + mainDlg.m_buttonAbort->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CompareStatusHandler::OnAbortCompare), nullptr, this); + } + mainDlg.Update(); //don't wait until idle event! } @@ -46,17 +44,17 @@ CompareStatusHandler::~CompareStatusHandler() //reenable complete main dialog mainDlg.enableAllElements(); - mainDlg.compareStatus->finalize(); + mainDlg.compareStatus->finalize(); mainDlg.auiMgr.GetPane(mainDlg.compareStatus->getAsWindow()).Hide(); mainDlg.auiMgr.Update(); - if (abortIsRequested()) - mainDlg.pushStatusInformation(_("Operation aborted!")); - - //de-register keys + //unregister keys mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CompareStatusHandler::OnKeyPressed), nullptr, this); mainDlg.m_buttonAbort->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CompareStatusHandler::OnAbortCompare), nullptr, this); + + if (abortIsRequested()) + mainDlg.pushStatusInformation(_("Operation aborted!")); } @@ -73,71 +71,29 @@ void CompareStatusHandler::OnKeyPressed(wxKeyEvent& event) } -void CompareStatusHandler::initNewProcess(int objectsTotal, zen::Int64 dataTotal, Process processID) +void CompareStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseID) { - currentProcess = processID; + StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); - switch (currentProcess) + switch (currentPhase()) { - case StatusHandler::PROCESS_SCANNING: + case PHASE_NONE: + case PHASE_SYNCHRONIZING: + assert(false); + case PHASE_SCANNING: break; - case StatusHandler::PROCESS_COMPARING_CONTENT: + case PHASE_COMPARING_CONTENT: { wxWindowUpdateLocker dummy(&mainDlg); - mainDlg.compareStatus->switchToCompareBytewise(objectsTotal, dataTotal); + mainDlg.compareStatus->switchToCompareBytewise(); mainDlg.Layout(); //show progress bar... mainDlg.Refresh(); //remove distortion... } break; - case StatusHandler::PROCESS_SYNCHRONIZING: - case StatusHandler::PROCESS_NONE: - assert(false); - break; } } -void CompareStatusHandler::updateProcessedData(int objectsDelta, zen::Int64 dataDelta) -{ - switch (currentProcess) - { - case StatusHandler::PROCESS_SCANNING: - mainDlg.compareStatus->incScannedObjects_NoUpdate(objectsDelta); //throw () - break; - case StatusHandler::PROCESS_COMPARING_CONTENT: - mainDlg.compareStatus->incProcessedCmpData_NoUpdate(objectsDelta, dataDelta); //throw () - break; - case StatusHandler::PROCESS_SYNCHRONIZING: - case StatusHandler::PROCESS_NONE: - assert(false); - break; - } - - //note: this method must NOT throw in order to properly allow undoing setting of statistics! -} - - -void CompareStatusHandler::updateTotalData(int objectsDelta, Int64 dataDelta) -{ - assert(currentProcess != PROCESS_SCANNING); - mainDlg.compareStatus->incTotalCmpData_NoUpdate(objectsDelta, dataDelta); -} - - -void CompareStatusHandler::reportStatus(const std::wstring& text) -{ - mainDlg.compareStatus->setStatusText_NoUpdate(text); - requestUiRefresh(); //throw AbortThisProcess -} - - -void CompareStatusHandler::reportInfo(const std::wstring& text) -{ - mainDlg.compareStatus->setStatusText_NoUpdate(text); - requestUiRefresh(); //throw AbortThisProcess -} - - ProcessCallback::Response CompareStatusHandler::reportError(const std::wstring& message) { if (ignoreErrors) @@ -146,7 +102,8 @@ ProcessCallback::Response CompareStatusHandler::reportError(const std::wstring& forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, + switch (showErrorDlg(&mainDlg, + ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, message, &ignoreNextErrors)) { case ReturnErrorDlg::BUTTON_IGNORE: @@ -169,8 +126,7 @@ void CompareStatusHandler::reportFatalError(const std::wstring& errorMessage) { forceUiRefresh(); - showErrorDlg(ReturnErrorDlg::BUTTON_ABORT, - errorMessage, nullptr); + showErrorDlg(&mainDlg, ReturnErrorDlg::BUTTON_ABORT, errorMessage, nullptr); } @@ -183,9 +139,9 @@ void CompareStatusHandler::reportWarning(const std::wstring& warningMessage, boo //show pop-up and ask user how to handle warning bool dontWarnAgain = false; - switch (showWarningDlg(ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_ABORT, - warningMessage, - dontWarnAgain)) + switch (showWarningDlg(&mainDlg, + ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_ABORT, + warningMessage, dontWarnAgain)) { case ReturnWarningDlg::BUTTON_IGNORE: warningActive = !dontWarnAgain; @@ -214,7 +170,7 @@ void CompareStatusHandler::OnAbortCompare(wxCommandEvent& event) void CompareStatusHandler::abortThisProcess() { - requestAbortion(); + requestAbortion(); //just make sure... throw GuiAbortProcess(); } //######################################################################################################## @@ -226,7 +182,7 @@ SyncStatusHandler::SyncStatusHandler(MainDialog* parentDlg, const std::wstring& execWhenFinished, std::vector<std::wstring>& execFinishedHistory) : parentDlg_(parentDlg), - syncStatusFrame(*this, parentDlg, SyncStatus::SYNCHRONIZING, true, jobName, execWhenFinished, execFinishedHistory), + syncStatusFrame(*this, *this, parentDlg, true, jobName, execWhenFinished, execFinishedHistory), handleError_(handleError) { } @@ -238,11 +194,17 @@ SyncStatusHandler::~SyncStatusHandler() //finalize error log if (abortIsRequested()) - errorLog.logMsg(_("Synchronization aborted!"), TYPE_FATAL_ERROR); + errorLog.logMsg(_("Synchronization aborted!"), TYPE_ERROR); else if (totalErrors > 0) errorLog.logMsg(_("Synchronization completed with errors!"), TYPE_WARNING); else - errorLog.logMsg(_("Synchronization completed successfully!"), TYPE_INFO); + { + if (getObjectsTotal(PHASE_SYNCHRONIZING) == 0 && //we're past "initNewPhase(PHASE_SYNCHRONIZING)" at this point! + getDataTotal (PHASE_SYNCHRONIZING) == 0) + errorLog.logMsg(_("Nothing to synchronize!"), TYPE_INFO); //even if "ignored conflicts" occurred! + else + errorLog.logMsg(_("Synchronization completed successfully!"), TYPE_INFO); + } bool showFinalResults = true; @@ -260,60 +222,37 @@ SyncStatusHandler::~SyncStatusHandler() if (showFinalResults) { if (abortIsRequested()) - syncStatusFrame.processHasFinished(SyncStatus::ABORTED, errorLog); //enable okay and close events + syncStatusFrame.processHasFinished(SyncStatus::RESULT_ABORTED, errorLog); //enable okay and close events else if (totalErrors > 0) - syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_ERROR, errorLog); + syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_ERROR, errorLog); else - syncStatusFrame.processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS, errorLog); + syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_SUCCESS, errorLog); } else syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is main window => program will quit directly } -void SyncStatusHandler::initNewProcess(int objectsTotal, zen::Int64 dataTotal, Process processID) +void SyncStatusHandler::initNewPhase(int objectsTotal, Int64 dataTotal, Phase phaseID) { - switch (processID) - { - case StatusHandler::PROCESS_SYNCHRONIZING: - syncStatusFrame.initNewProcess(SyncStatus::SYNCHRONIZING, objectsTotal, dataTotal); - break; - case StatusHandler::PROCESS_SCANNING: - case StatusHandler::PROCESS_COMPARING_CONTENT: - case StatusHandler::PROCESS_NONE: - assert(false); - break; - } + assert(phaseID == PHASE_SYNCHRONIZING); + StatusHandler::initNewPhase(objectsTotal, dataTotal, phaseID); + syncStatusFrame.initNewPhase(); //call after "StatusHandler::initNewPhase" } void SyncStatusHandler::updateProcessedData(int objectsDelta, Int64 dataDelta) { - syncStatusFrame.incProcessedData_NoUpdate(objectsDelta, dataDelta); //throw () - + StatusHandler::updateProcessedData(objectsDelta, dataDelta); + syncStatusFrame.reportCurrentBytes(getDataCurrent(currentPhase())); //throw () //note: this method should NOT throw in order to properly allow undoing setting of statistics! } -void SyncStatusHandler::updateTotalData(int objectsDelta, Int64 dataDelta) -{ - syncStatusFrame.incTotalData_NoUpdate(objectsDelta, dataDelta); //throw () -} - - -void SyncStatusHandler::reportStatus(const std::wstring& text) -{ - syncStatusFrame.setStatusText_NoUpdate(text); //throw () - requestUiRefresh(); //throw AbortThisProccess -} - - void SyncStatusHandler::reportInfo(const std::wstring& text) { + StatusHandler::reportInfo(text); errorLog.logMsg(text, TYPE_INFO); - - syncStatusFrame.setStatusText_NoUpdate(text); //throw () - requestUiRefresh(); //throw AbortThisProccess } @@ -332,7 +271,8 @@ ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& err forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, + switch (showErrorDlg(parentDlg_, + ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, errorMessage, &ignoreNextErrors)) { @@ -351,9 +291,8 @@ ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& err break; } - assert (false); - errorLog.logMsg(errorMessage, TYPE_ERROR); - return ProcessCallback::IGNORE_ERROR; + assert(false); + return ProcessCallback::IGNORE_ERROR; //dummy value } @@ -369,9 +308,9 @@ void SyncStatusHandler::reportFatalError(const std::wstring& errorMessage) forceUiRefresh(); bool ignoreNextErrors = false; - switch (showErrorDlg(ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_ABORT, - errorMessage, - &ignoreNextErrors)) + switch (showErrorDlg(parentDlg_, + ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_ABORT, + errorMessage, &ignoreNextErrors)) { case ReturnErrorDlg::BUTTON_IGNORE: if (ignoreNextErrors) //falsify only @@ -398,47 +337,48 @@ void SyncStatusHandler::reportWarning(const std::wstring& warningMessage, bool& { errorLog.logMsg(warningMessage, TYPE_WARNING); + if (!warningActive) + return; + switch (handleError_) { case ON_GUIERROR_POPUP: - break; - case ON_GUIERROR_IGNORE: - return; //if errors are ignored, then warnings should also - } - if (!warningActive) - return; + { + PauseTimers dummy(syncStatusFrame); + forceUiRefresh(); - PauseTimers dummy(syncStatusFrame); - forceUiRefresh(); + bool dontWarnAgain = false; + switch (showWarningDlg(parentDlg_, + ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_ABORT, + warningMessage, dontWarnAgain)) + { + case ReturnWarningDlg::BUTTON_IGNORE: //no unhandled error situation! + warningActive = !dontWarnAgain; + break; - bool dontWarnAgain = false; - switch (showWarningDlg(ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_ABORT, - warningMessage, - dontWarnAgain)) - { - case ReturnWarningDlg::BUTTON_IGNORE: //no unhandled error situation! - warningActive = !dontWarnAgain; - return; + case ReturnWarningDlg::BUTTON_SWITCH: + assert(false); + case ReturnWarningDlg::BUTTON_ABORT: + abortThisProcess(); + break; + } + } + break; - case ReturnWarningDlg::BUTTON_SWITCH: - assert(false); - case ReturnWarningDlg::BUTTON_ABORT: - abortThisProcess(); - return; + case ON_GUIERROR_IGNORE: + break; //if errors are ignored, then warnings should be, too } - - assert(false); } void SyncStatusHandler::forceUiRefresh() { - syncStatusFrame.updateStatusDialogNow(); + syncStatusFrame.updateProgress(); } void SyncStatusHandler::abortThisProcess() { - requestAbortion(); + requestAbortion(); //just make sure... throw GuiAbortProcess(); //abort can be triggered by syncStatusFrame } diff --git a/ui/gui_status_handler.h b/ui/gui_status_handler.h index 4e289648..ebc59473 100644 --- a/ui/gui_status_handler.h +++ b/ui/gui_status_handler.h @@ -21,17 +21,13 @@ class wxCommandEvent; class GuiAbortProcess {}; //classes handling sync and compare error as well as status information -class CompareStatusHandler : private wxEvtHandler, public StatusHandler +class CompareStatusHandler : private wxEvtHandler, public zen::StatusHandler { public: CompareStatusHandler(MainDialog& dlg); ~CompareStatusHandler(); - virtual void initNewProcess (int objectsTotal, zen::Int64 dataTotal, Process processID); - virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta); - virtual void updateTotalData (int objectsDelta, zen::Int64 dataDelta); - virtual void reportStatus(const std::wstring& text); - virtual void reportInfo(const std::wstring& text); + virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); virtual void forceUiRefresh(); virtual Response reportError(const std::wstring& text); @@ -41,15 +37,14 @@ public: private: void OnKeyPressed(wxKeyEvent& event); void OnAbortCompare(wxCommandEvent& event); //handle abort button click - virtual void abortThisProcess(); + virtual void abortThisProcess(); //throw GuiAbortProcess MainDialog& mainDlg; bool ignoreErrors; - Process currentProcess; }; -class SyncStatusHandler : public StatusHandler +class SyncStatusHandler : public zen::StatusHandler { public: SyncStatusHandler(MainDialog* parentDlg, @@ -59,10 +54,8 @@ public: std::vector<std::wstring>& execFinishedHistory); ~SyncStatusHandler(); - virtual void initNewProcess (int objectsTotal, zen::Int64 dataTotal, Process processID); + virtual void initNewPhase (int objectsTotal, zen::Int64 dataTotal, Phase phaseID); virtual void updateProcessedData(int objectsDelta, zen::Int64 dataDelta); - virtual void updateTotalData (int objectsDelta, zen::Int64 dataDelta); - virtual void reportStatus(const std::wstring& text); virtual void reportInfo(const std::wstring& text); virtual void forceUiRefresh(); @@ -71,7 +64,7 @@ public: virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); private: - virtual void abortThisProcess(); + virtual void abortThisProcess(); //throw GuiAbortProcess MainDialog* parentDlg_; SyncStatus syncStatusFrame; //the window managed by SyncStatus has longer lifetime than this handler! diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp index 85f9a580..049a1176 100644 --- a/ui/main_dlg.cpp +++ b/ui/main_dlg.cpp @@ -39,8 +39,8 @@ #include "../lib/resources.h" #include <zen/file_handling.h> #include <zen/file_id.h> +#include <zen/recycler.h> #include "../lib/resolve_path.h" -#include "../lib/recycler.h" #include "../lib/ffs_paths.h" #include <wx+/toggle_button.h> #include "folder_pair.h" @@ -119,7 +119,7 @@ public: return false; } - switch (xmlAccess::getMergeType(droppedFiles)) //throw () + switch (xmlAccess::getMergeType(toZ(droppedFiles))) //throw() { case xmlAccess::MERGE_BATCH: case xmlAccess::MERGE_GUI: @@ -363,20 +363,20 @@ MainDialog::MainDialog(const std::vector<wxString>& cfgFileNames, xmlAccess::Xml //------------------------------------------------------------------------------------------ //check existence of all directories in parallel! - std::list<boost::unique_future<bool>> fileEx; + + RunUntilFirstHit<NullType> findFirstMissing; std::for_each(filenames.begin(), filenames.end(), - [&fileEx](const wxString& filename) + [&](const wxString& filename) { const Zstring filenameFmt = 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 !filenameFmt.empty() && zen::fileExists(filenameFmt); })); + findFirstMissing.addJob([=] { return filenameFmt.empty() || !fileExists(filenameFmt) ? zen::make_unique<NullType>() : nullptr; }); }); //potentially slow network access: give all checks 500ms to finish - wait_for_all_timed(fileEx.begin(), fileEx.end(), boost::posix_time::milliseconds(500)); + const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //false: time elapsed + !findFirstMissing.get(); //no missing //------------------------------------------------------------------------------------------ - //check if one of the files is not existing (this shall not be an error!) - const bool allFilesExist = std::all_of(fileEx.begin(), fileEx.end(), [](boost::unique_future<bool>& ft) { return ft.is_ready() && ft.get(); }); if (!allFilesExist) filenames.clear(); @@ -392,16 +392,16 @@ MainDialog::MainDialog(const std::vector<wxString>& cfgFileNames, xmlAccess::Xml try { //load XML - xmlAccess::convertConfig(filenames, guiCfg); //throw (xmlAccess::FfsXmlError) + xmlAccess::convertConfig(toZ(filenames), guiCfg); //throw xmlAccess::FfsXmlError loadCfgSuccess = true; } catch (const xmlAccess::FfsXmlError& error) { if (error.getSeverity() == xmlAccess::FfsXmlError::WARNING) - wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING); + wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING, this); else - wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR, this); } const bool startComparisonImmediately = !cfgFileNames.empty() && loadCfgSuccess; @@ -437,7 +437,7 @@ MainDialog::~MainDialog() const xmlAccess::XmlGuiConfig guiCfg = getConfig(); try { - xmlAccess::writeConfig(guiCfg, lastRunConfigName()); + xmlAccess::writeConfig(guiCfg, toZ(lastRunConfigName())); //setLastUsedConfig(lastRunConfigName(), guiCfg); -> may be removed!? } //don't annoy users on read-only drives: no error checking should be fine since this is not a config the user explicitly wanted to save @@ -456,7 +456,7 @@ MainDialog::~MainDialog() void MainDialog::onQueryEndSession() { writeGlobalSettings(); - try { xmlAccess::writeConfig(getConfig(), lastRunConfigName()); } + try { xmlAccess::writeConfig(getConfig(), toZ(lastRunConfigName())); } catch (const xmlAccess::FfsXmlError&) {} } @@ -703,29 +703,31 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg, //------------------------------------------------------------------------------------------ //check existence of all directories in parallel! - std::list<boost::unique_future<bool>> dirEx; + RunUntilFirstHit<NullType> findFirstMissing; - auto addDirCheck = [&dirEx](const FolderPairEnh& fp) + bool haveNonEmptyPair = false; + auto addDirCheck = [&](const FolderPairEnh& fp) { - const Zstring dirFmtLeft = zen::getFormattedDirectoryName(fp.leftDirectory); - const Zstring dirFmtRight = zen::getFormattedDirectoryName(fp.rightDirectory); + const Zstring dirFmtLeft = getFormattedDirectoryName(fp.leftDirectory ); //should not block!? + const Zstring dirFmtRight = getFormattedDirectoryName(fp.rightDirectory); // if (dirFmtLeft.empty() && dirFmtRight.empty()) //only skip check if both sides are empty! return; - - dirEx.push_back(zen::async2<bool>([=] { return !dirFmtLeft .empty() && zen::dirExists(dirFmtLeft); })); - dirEx.push_back(zen::async2<bool>([=] { return !dirFmtRight.empty() && zen::dirExists(dirFmtRight); })); + haveNonEmptyPair = true; + findFirstMissing.addJob([=] { return dirFmtLeft .empty() || !dirExists(dirFmtLeft ) ? zen::make_unique<NullType>() : nullptr; }); + findFirstMissing.addJob([=] { return dirFmtRight.empty() || !dirExists(dirFmtRight) ? zen::make_unique<NullType>() : nullptr; }); }; + addDirCheck(currMainCfg.firstPair); std::for_each(currMainCfg.additionalPairs.begin(), currMainCfg.additionalPairs.end(), addDirCheck); - if (!dirEx.empty()) + //------------------------------------------------------------------------------------------ + + if (haveNonEmptyPair) { //potentially slow network access: give all checks 500ms to finish - wait_for_all_timed(dirEx.begin(), dirEx.end(), boost::posix_time::milliseconds(500)); - //------------------------------------------------------------------------------------------ - - const bool allFoldersExist = std::all_of(dirEx.begin(), dirEx.end(), [](boost::unique_future<bool>& ft) { return ft.is_ready() && ft.get(); }); - if (allFoldersExist) + const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //true: have result + !findFirstMissing.get(); //no missing + if (allFilesExist) { wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); m_buttonCompare->GetEventHandler()->AddPendingEvent(dummy2); //simulate button click on "compare" @@ -816,9 +818,9 @@ void MainDialog::writeGlobalSettings() //write list of last used configuration files std::vector<wxString> cfgFileHistory; - for (int i = 0; i < static_cast<int>(m_listBoxHistory->GetCount()); ++i) - if (m_listBoxHistory->GetClientObject(i)) - cfgFileHistory.push_back(static_cast<wxClientDataString*>(m_listBoxHistory->GetClientObject(i))->name_); + for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) + if (auto clientString = dynamic_cast<wxClientDataString*>(m_listBoxHistory->GetClientObject(i))) + cfgFileHistory.push_back(clientString->name_); globalSettings->gui.cfgFileHistory = cfgFileHistory; globalSettings->gui.lastUsedConfigFiles = activeConfigFiles; @@ -951,9 +953,7 @@ std::vector<FileSystemObject*> MainDialog::getGridSelection(bool fromLeft, bool if (fromRight) addSelection(gridview::COMP_RIGHT); - std::vector<FileSystemObject*> selection; - gridDataView->getAllFileRef(selectedRows, selection); - return selection; + return gridDataView->getAllFileRef(selectedRows); } @@ -1022,7 +1022,8 @@ public: return DeleteFilesHandler::IGNORE_ERROR; bool ignoreNextErrors = false; - switch (showErrorDlg(ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, + switch (showErrorDlg(mainDlg, + ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_ABORT, errorMessage, &ignoreNextErrors)) { case ReturnErrorDlg::BUTTON_IGNORE: @@ -1094,7 +1095,8 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec wxWindow* oldFocus = wxWindow::FindFocus(); ZEN_ON_SCOPE_EXIT( if (oldFocus) oldFocus->SetFocus(); ) - if (zen::showDeleteDialog(selectionLeft, + if (zen::showDeleteDialog(this, + selectionLeft, selectionRight, globalSettings->gui.deleteOnBothSides, globalSettings->gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY) @@ -1535,7 +1537,7 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou switch (keyCode) { case 'F': //CTRL + F - zen::startFind(*this, *m_gridMain, gridview::COMP_LEFT, gridview::COMP_RIGHT, globalSettings->gui.textSearchRespectCase); + zen::startFind(this, *m_gridMain, gridview::COMP_LEFT, gridview::COMP_RIGHT, globalSettings->gui.textSearchRespectCase); return; //-> swallow event! } @@ -1543,7 +1545,7 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou { case WXK_F3: //F3 case WXK_NUMPAD_F3: // - zen::findNext(*this, *m_gridMain, gridview::COMP_LEFT, gridview::COMP_RIGHT, globalSettings->gui.textSearchRespectCase); + zen::findNext(this, *m_gridMain, gridview::COMP_LEFT, gridview::COMP_RIGHT, globalSettings->gui.textSearchRespectCase); return; //-> swallow event! case WXK_F8: //F8 @@ -1679,15 +1681,19 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false); //---------------------------------------------------------------------------------------------------- - //CONTEXT_EXCLUDE_OBJ + //EXCLUDE FILTER if (selection.size() == 1) - menu.addItem(_("Exclude via filter:") + L" " + afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR), - [this, &selection] { excludeItems(selection); }, - &GlobalResources::getImage(L"filterOnSmall")); + { + //by relative path + menu.addItem(_("Exclude via filter:") + L" " + (FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()), + [this, &selection] { excludeItems(selection); }, &GlobalResources::getImage(L"filterOnSmall")); + } else if (selection.size() > 1) + { + //by relative path menu.addItem(_("Exclude via filter:") + L" " + _("<multiple selection>"), - [this, &selection] { excludeItems(selection); }, - &GlobalResources::getImage(L"filterOnSmall")); + [this, &selection] { excludeItems(selection); }, &GlobalResources::getImage(L"filterOnSmall")); + } //---------------------------------------------------------------------------------------------------- //CONTEXT_DELETE_FILES @@ -1743,30 +1749,40 @@ void MainDialog::onMainGridContext(GridClickEvent& event) menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false); //---------------------------------------------------------------------------------------------------- - //CONTEXT_EXCLUDE_EXT - if (!selection.empty() && - dynamic_cast<const DirMapping*>(selection[0]) == nullptr) //non empty && no directory + //EXCLUDE FILTER + if (selection.size() == 1) { - const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR); - if (filename.find(Zchar('.')) != Zstring::npos) //be careful: AfterLast would return the whole string if '.' were not found! + ContextMenu submenu; + + //by extension + if (dynamic_cast<const DirMapping*>(selection[0]) == nullptr) //non empty && no directory { - const Zstring extension = afterLast(filename, Zchar('.')); + const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR); + if (contains(filename, Zchar('.'))) //be careful: AfterLast would return the whole string if '.' were not found! + { + const Zstring extension = afterLast(filename, Zchar('.')); - menu.addItem(_("Exclude via filter:") + L" *." + extension, - [this, extension] { excludeExtension(extension); }, - &GlobalResources::getImage(L"filterOnSmall")); + submenu.addItem(L"*." + utf8CvrtTo<wxString>(extension), + [this, extension] { excludeExtension(extension); }); + } } + + //by short name + submenu.addItem(utf8CvrtTo<wxString>(Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName()), + [this, &selection] { excludeShortname(*selection[0]); }); + + //by relative path + submenu.addItem(utf8CvrtTo<wxString>(FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()), + [this, &selection] { excludeItems(selection); }); + + menu.addSubmenu(_("Exclude via filter:"), submenu, &GlobalResources::getImage(L"filterOnSmall")); } - //---------------------------------------------------------------------------------------------------- - //CONTEXT_EXCLUDE_OBJ - if (selection.size() == 1) - menu.addItem(_("Exclude via filter:") + L" " + afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR), - [this, &selection] { excludeItems(selection); }, - &GlobalResources::getImage(L"filterOnSmall")); else if (selection.size() > 1) + { + //by relative path menu.addItem(_("Exclude via filter:") + L" " + _("<multiple selection>"), - [this, &selection] { excludeItems(selection); }, - &GlobalResources::getImage(L"filterOnSmall")); + [this, &selection] { excludeItems(selection); }, &GlobalResources::getImage(L"filterOnSmall")); + } //---------------------------------------------------------------------------------------------------- //CONTEXT_EXTERNAL_APP @@ -1836,10 +1852,28 @@ void MainDialog::excludeExtension(const Zstring& extension) updateFilterButtons(); //do not fully apply filter, just exclude new items - std::for_each(begin(folderCmp), end(folderCmp), - [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); + std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); + updateGui(); +} - //applyFiltering(getConfig().mainCfg, gridDataView->getDataTentative()); + +void MainDialog::excludeShortname(const FileSystemObject& fsObj) +{ + Zstring newExclude = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getObjShortName(); + const bool isDir = dynamic_cast<const DirMapping*>(&fsObj) != nullptr; + if (isDir) + newExclude += FILE_NAME_SEPARATOR; + + //add to filter config + Zstring& excludeFilter = currentCfg.mainCfg.globalFilter.excludeFilter; + if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) + excludeFilter += Zstr("\n"); + excludeFilter += newExclude; + + updateFilterButtons(); + + //do not fully apply filter, just exclude new items + std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); updateGui(); } @@ -1856,7 +1890,7 @@ void MainDialog::excludeItems(const std::vector<FileSystemObject*>& selection) if (iter != selection.begin()) newExclude += Zstr("\n"); - //#pragma warning(suppress: 6011) -> fsObj cannot be NULL here! + //#pragma warning(suppress: 6011) -> fsObj bound in this context! newExclude += FILE_NAME_SEPARATOR + fsObj->getObjRelativeName(); const bool isDir = dynamic_cast<const DirMapping*>(fsObj) != nullptr; @@ -1873,10 +1907,7 @@ void MainDialog::excludeItems(const std::vector<FileSystemObject*>& selection) updateFilterButtons(); //do not fully apply filter, just exclude new items - std::for_each(begin(folderCmp), end(folderCmp), - [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); - - //applyFiltering(getConfig().mainCfg, gridDataView->getDataTentative()); + std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); updateGui(); } } @@ -1953,7 +1984,7 @@ void MainDialog::onGridLabelContext(GridClickEvent& event) auto selectTimeSpan = [&] { - if (showSelectTimespanDlg(manualTimeSpanFrom, manualTimeSpanTo) == ReturnSmallDlg::BUTTON_OKAY) + if (showSelectTimespanDlg(this, manualTimeSpanFrom, manualTimeSpanTo) == ReturnSmallDlg::BUTTON_OKAY) { applyTimeSpanFilter(folderCmp, manualTimeSpanFrom, manualTimeSpanTo); //overwrite current active/inactive settings updateGuiAfterFilterChange(400); @@ -2062,22 +2093,8 @@ void MainDialog::OnDirSelected(wxFileDirPickerEvent& event) void MainDialog::onNaviPanelFilesDropped(FileDropEvent& event) { - const auto& droppedFiles = event.getFiles(); - - switch (xmlAccess::getMergeType(droppedFiles)) //throw () - { - case xmlAccess::MERGE_BATCH: - case xmlAccess::MERGE_GUI: - case xmlAccess::MERGE_GUI_BATCH: - loadConfiguration(droppedFiles); - return; - - case xmlAccess::MERGE_OTHER: - break; - } - + loadConfiguration(event.getFiles()); event.Skip(); - } @@ -2180,17 +2197,17 @@ bool MainDialog::trySaveConfig(const wxString* fileName) //return true if saved targetFilename = *fileName; else { - wxString defaultFileName = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxT("SyncSettings.ffs_gui"); + wxString defaultFileName = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? 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 (defaultFileName.EndsWith(wxT(".ffs_batch"))) - defaultFileName.Replace(wxT(".ffs_batch"), wxT(".ffs_gui"), false); + if (endsWith(defaultFileName, L".ffs_batch")) + replace(defaultFileName, L".ffs_batch", L".ffs_gui", false); - wxFileDialog filePicker(this, + wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! wxEmptyString, wxEmptyString, defaultFileName, wxString(_("FreeFileSync configuration")) + wxT(" (*.ffs_gui)|*.ffs_gui"), - wxFD_SAVE /*| wxFD_OVERWRITE_PROMPT*/); //creating this on freestore leads to memleak! + wxFD_SAVE /*| wxFD_OVERWRITE_PROMPT*/); if (filePicker.ShowModal() != wxID_OK) return false; targetFilename = filePicker.GetPath(); @@ -2200,15 +2217,15 @@ bool MainDialog::trySaveConfig(const wxString* fileName) //return true if saved try { - xmlAccess::writeConfig(guiCfg, targetFilename); //write config to XML + xmlAccess::writeConfig(guiCfg, toZ(targetFilename)); //write config to XML setLastUsedConfig(targetFilename, guiCfg); pushStatusInformation(_("Configuration saved!")); return true; } - catch (const xmlAccess::FfsXmlError& error) + catch (const xmlAccess::FfsXmlError& e) { - wxMessageBox(error.toString().c_str(), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(e.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this); return false; } } @@ -2220,7 +2237,7 @@ void MainDialog::OnLoadConfig(wxCommandEvent& event) wxEmptyString, beforeLast(activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString(), utf8CvrtTo<wxString>(FILE_NAME_SEPARATOR)), //set default dir: empty string if "activeConfigFiles" is empty or has no path separator wxEmptyString, - wxString(_("FreeFileSync configuration")) + wxT(" (*.ffs_gui;*.ffs_batch)|*.ffs_gui;*.ffs_batch"), + _("FreeFileSync configuration") + L" (*.ffs_gui;*.ffs_batch)|*.ffs_gui;*.ffs_batch", wxFD_OPEN | wxFD_MULTIPLE); if (filePicker.ShowModal() == wxID_OK) @@ -2255,8 +2272,7 @@ void MainDialog::OnLoadFromHistory(wxCommandEvent& event) std::for_each(selections.begin(), selections.end(), [&](int pos) { - wxClientDataString* cData = dynamic_cast<wxClientDataString*>(m_listBoxHistory->GetClientObject(pos)); - if (cData) + if (auto cData = dynamic_cast<wxClientDataString*>(m_listBoxHistory->GetClientObject(pos))) filenames.push_back(cData->name_); }); @@ -2286,7 +2302,8 @@ bool MainDialog::saveOldConfig() //return false on user abort bool neverSave = !globalSettings->optDialogs.popupOnConfigChange; CheckBox cb(_("Never save changes"), neverSave); - switch (showQuestionDlg(ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_NO | ReturnQuestionDlg::BUTTON_CANCEL, + switch (showQuestionDlg(this, + ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_NO | ReturnQuestionDlg::BUTTON_CANCEL, _("Save changes to current configuration?"), &cb)) { case ReturnQuestionDlg::BUTTON_YES: @@ -2330,7 +2347,7 @@ void MainDialog::loadConfiguration(const std::vector<wxString>& filenames) try { //allow reading batch configurations also - xmlAccess::convertConfig(filenames, newGuiCfg); //throw (xmlAccess::FfsXmlError) + xmlAccess::convertConfig(toZ(filenames), newGuiCfg); //throw FfsXmlError setLastUsedConfig(filenames, newGuiCfg); pushStatusInformation(_("Configuration loaded!")); @@ -2340,11 +2357,11 @@ void MainDialog::loadConfiguration(const std::vector<wxString>& filenames) if (error.getSeverity() == xmlAccess::FfsXmlError::WARNING) { setLastUsedConfig(filenames, xmlAccess::XmlGuiConfig()); //simulate changed config on parsing errors - wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING); + wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING, this); } else { - wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR, this); return; } } @@ -2421,9 +2438,7 @@ void MainDialog::onCheckRows(CheckRowsEvent& event) for (int i = rowFirst; i < rowLast; ++i) selectedRows.insert(i); - std::vector<FileSystemObject*> objects; - gridDataView->getAllFileRef(selectedRows, objects); - + std::vector<FileSystemObject*> objects = gridDataView->getAllFileRef(selectedRows); setManualFilter(objects, event.setIncluded_); } } @@ -2441,9 +2456,7 @@ void MainDialog::onSetSyncDirection(SyncDirectionEvent& event) for (int i = rowFirst; i < rowLast; ++i) selectedRows.insert(i); - std::vector<FileSystemObject*> objects; - gridDataView->getAllFileRef(selectedRows, objects); - + std::vector<FileSystemObject*> objects = gridDataView->getAllFileRef(selectedRows); setSyncDirManually(objects, event.direction_); } } @@ -2469,7 +2482,7 @@ void MainDialog::setLastUsedConfig(const std::vector<wxString>& filenames, if (activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName()) SetTitle(activeConfigFiles[0]); else - SetTitle(wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")); + SetTitle(L"FreeFileSync - " + _("Folder Comparison and Synchronization")); } @@ -2556,7 +2569,7 @@ xmlAccess::XmlGuiConfig MainDialog::getConfig() const const wxString& MainDialog::lastRunConfigName() { - static wxString instance = toWx(zen::getConfigDir()) + wxT("LastRun.ffs_gui"); + static wxString instance = toWx(zen::getConfigDir()) + L"LastRun.ffs_gui"; return instance; } @@ -2589,7 +2602,8 @@ void MainDialog::OnHideFilteredButton(wxCommandEvent& event) void MainDialog::OnConfigureFilter(wxCommandEvent& event) { - if (showFilterDialog(true, //is main filter dialog + if (showFilterDialog(this, + true, //is main filter dialog currentCfg.mainCfg.globalFilter) == ReturnSmallDlg::BUTTON_OKAY) { updateFilterButtons(); //refresh global filter icon @@ -2718,7 +2732,7 @@ void MainDialog::OnSyncDirNone(wxCommandEvent& event) inline wxBitmap buttonPressed(const std::string& name) { - wxBitmap background = GlobalResources::getImage(wxT("buttonPressed")); + wxBitmap background = GlobalResources::getImage(L"buttonPressed"); return mirrorIfRtl( layOver( GlobalResources::getImage(utf8CvrtTo<wxString>(name)), background)); @@ -2740,74 +2754,74 @@ void MainDialog::initViewFilterButtons() { //compare result buttons m_bpButtonLeftOnly->init(buttonPressed("leftOnly"), - _("Hide files that exist on left side only"), buttonReleased("leftOnly"), + _("Hide files that exist on left side only"), _("Show files that exist on left side only")); m_bpButtonRightOnly->init(buttonPressed("rightOnly"), - _("Hide files that exist on right side only"), buttonReleased("rightOnly"), + _("Hide files that exist on right side only"), _("Show files that exist on right side only")); m_bpButtonLeftNewer->init(buttonPressed("leftNewer"), - _("Hide files that are newer on left"), buttonReleased("leftNewer"), + _("Hide files that are newer on left"), _("Show files that are newer on left")); m_bpButtonRightNewer->init(buttonPressed("rightNewer"), - _("Hide files that are newer on right"), buttonReleased("rightNewer"), + _("Hide files that are newer on right"), _("Show files that are newer on right")); m_bpButtonEqual->init(buttonPressed("equal"), - _("Hide files that are equal"), buttonReleased("equal"), + _("Hide files that are equal"), _("Show files that are equal")); m_bpButtonDifferent->init(buttonPressed("different"), - _("Hide files that are different"), buttonReleased("different"), + _("Hide files that are different"), _("Show files that are different")); m_bpButtonConflict->init(buttonPressed("conflict"), - _("Hide conflicts"), buttonReleased("conflict"), + _("Hide conflicts"), _("Show conflicts")); //sync preview buttons m_bpButtonSyncCreateLeft->init(buttonPressed("createLeft"), - _("Hide files that will be created on the left side"), 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"), - _("Hide files that will be created on the right side"), 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"), - _("Hide files that will be deleted on the left side"), 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"), - _("Hide files that will be deleted on the right side"), 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"), - _("Hide files that will be overwritten on left side"), 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"), - _("Hide files that will be overwritten on right side"), 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"), - _("Hide files that won't be copied"), buttonReleased("none"), + _("Hide files that won't be copied"), _("Show files that won't be copied")); //compare result buttons @@ -2877,11 +2891,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 - LockHolder dummy2(true); //allow pw prompt - for (std::vector<FolderPairCfg>::const_iterator i = cmpConfig.begin(); i != cmpConfig.end(); ++i) + std::unique_ptr<LockHolder> dummy2; + if (globalSettings->createLockFile) { - dummy2.addDir(i->leftDirectoryFmt, statusHandler); - dummy2.addDir(i->rightDirectoryFmt, statusHandler); + dummy2.reset(new LockHolder(true)); //allow pw prompt + for (auto iter = cmpConfig.begin(); iter != cmpConfig.end(); ++iter) + { + dummy2->addDir(iter->leftDirectoryFmt, statusHandler); + dummy2->addDir(iter->rightDirectoryFmt, statusHandler); + } } //begin comparison @@ -2975,7 +2993,8 @@ void MainDialog::OnSyncSettings(wxCommandEvent& event) globalSettings->gui.onCompletionHistoryMax }; - if (showSyncConfigDlg(currentCfg.mainCfg.cmpConfig.compareVar, + if (showSyncConfigDlg(this, + currentCfg.mainCfg.cmpConfig.compareVar, currentCfg.mainCfg.syncCfg, ¤tCfg.handleError, &ewfCfg) == ReturnSyncConfig::BUTTON_OKAY) //optional input parameter @@ -3020,7 +3039,7 @@ void MainDialog::OnCmpSettings(wxCommandEvent& event) CompConfig cmpConfigNew = currentCfg.mainCfg.cmpConfig; - if (zen::showCompareCfgDialog(cmpConfigNew) == ReturnSmallDlg::BUTTON_OKAY && + if (zen::showCompareCfgDialog(this, cmpConfigNew) == ReturnSmallDlg::BUTTON_OKAY && //check if settings were changed at all cmpConfigNew != currentCfg.mainCfg.cmpConfig) { @@ -3050,10 +3069,10 @@ void MainDialog::OnStartSync(wxCommandEvent& event) { bool dontShowAgain = false; - if (zen::showSyncPreviewDlg( - getConfig().mainCfg.getSyncVariantName(), - zen::SyncStatistics(folderCmp), - dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY) + if (zen::showSyncPreviewDlg(this, + getConfig().mainCfg.getSyncVariantName(), + zen::SyncStatistics(folderCmp), + dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY) return; globalSettings->optDialogs.showSummaryBeforeSync = !dontShowAgain; @@ -3078,12 +3097,15 @@ void MainDialog::OnStartSync(wxCommandEvent& event) globalSettings->gui.onCompletionHistory); //GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization - LockHolder dummy2(true); //allow pw prompt - - for (auto iter = begin(folderCmp); iter != end(folderCmp); ++iter) + std::unique_ptr<LockHolder> dummy2; + if (globalSettings->createLockFile) { - dummy2.addDir(iter->getBaseDirPf<LEFT_SIDE >(), statusHandler); - dummy2.addDir(iter->getBaseDirPf<RIGHT_SIDE>(), statusHandler); + dummy2.reset(new LockHolder(true)); //allow pw prompt + for (auto iter = begin(folderCmp); iter != end(folderCmp); ++iter) + { + dummy2->addDir(iter->getBaseDirPf<LEFT_SIDE >(), statusHandler); + dummy2->addDir(iter->getBaseDirPf<RIGHT_SIDE>(), statusHandler); + } } //start synchronization and mark all elements processed @@ -3104,7 +3126,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) syncProc.startSynchronizationProcess(syncProcessCfg, folderCmp); //play (optional) sound notification after sync has completed (GUI and batch mode) - const wxString soundFile = toWx(zen::getResourceDir()) + wxT("Sync_Complete.wav"); + const wxString soundFile = toWx(zen::getResourceDir()) + L"Sync_Complete.wav"; if (fileExists(toZ(soundFile))) wxSound::Play(soundFile, wxSOUND_ASYNC); } @@ -3411,7 +3433,8 @@ void MainDialog::applySyncConfig() if (warningActive) { bool dontWarnAgain = false; - if (showWarningDlg(ReturnWarningDlg::BUTTON_IGNORE, warning, dontWarnAgain) == ReturnWarningDlg::BUTTON_IGNORE) + if (showWarningDlg(this, + ReturnWarningDlg::BUTTON_IGNORE, warning, dontWarnAgain) == ReturnWarningDlg::BUTTON_IGNORE) warningActive = !dontWarnAgain; } }); @@ -3652,146 +3675,151 @@ void MainDialog::clearAddFolderPairs() //menu events void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event) { - zen::showGlobalSettingsDlg(*globalSettings); + zen::showGlobalSettingsDlg(this, *globalSettings); } void MainDialog::OnMenuExportFileList(wxCommandEvent& event) { //get a filename - const wxString defaultFileName = wxT("FileList.csv"); //proposal + const wxString defaultFileName = L"FileList.csv"; //proposal wxFileDialog filePicker(this, wxEmptyString, wxEmptyString, defaultFileName, - wxString(_("Comma separated list")) + wxT(" (*.csv)|*.csv"), + _("Comma separated list") + L" (*.csv)|*.csv", wxFD_SAVE /*| wxFD_OVERWRITE_PROMPT*/); //creating this on freestore leads to memleak! - if (filePicker.ShowModal() == wxID_OK) + if (filePicker.ShowModal() != wxID_OK) + return; + + const wxString newFileName = filePicker.GetPath(); + + typedef Zbase<char> UtfString; //perf: wxString doesn't model exponential growth and so is out, std::string doesn't give performance guarantee! + UtfString exportString; + + //write legend + exportString += utf8CvrtTo<UtfString>(_("Legend")) + '\n'; + if (syncPreviewEnabled) { - const wxString newFileName = filePicker.GetPath(); + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_EQUAL)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_EQUAL)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_CREATE_NEW_LEFT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_CREATE_NEW_LEFT)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_CREATE_NEW_RIGHT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_CREATE_NEW_RIGHT)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_OVERWRITE_LEFT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_OVERWRITE_LEFT)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_OVERWRITE_RIGHT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_OVERWRITE_RIGHT)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_DELETE_LEFT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_DELETE_LEFT)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_DELETE_RIGHT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_DELETE_RIGHT)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_DO_NOTHING)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_DO_NOTHING)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getSyncOpDescription(SO_UNRESOLVED_CONFLICT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(SO_UNRESOLVED_CONFLICT)) + '\n'; + } + else + { + exportString += "\"" + utf8CvrtTo<UtfString>(getCategoryDescription(FILE_EQUAL)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(FILE_EQUAL)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getCategoryDescription(FILE_DIFFERENT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(FILE_DIFFERENT)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getCategoryDescription(FILE_LEFT_SIDE_ONLY)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(FILE_LEFT_SIDE_ONLY)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getCategoryDescription(FILE_RIGHT_SIDE_ONLY)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(FILE_RIGHT_SIDE_ONLY)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getCategoryDescription(FILE_LEFT_NEWER)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(FILE_LEFT_NEWER)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getCategoryDescription(FILE_RIGHT_NEWER)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(FILE_RIGHT_NEWER)) + '\n'; + exportString += "\"" + utf8CvrtTo<UtfString>(getCategoryDescription(FILE_CONFLICT)) + "\";" + utf8CvrtTo<UtfString>(getSymbol(FILE_CONFLICT)) + '\n'; + } + exportString += '\n'; - zxString exportString; //perf: wxString doesn't model exponential growth and so is out + //base folders + exportString += utf8CvrtTo<UtfString>(_("Folder pairs")) + '\n' ; + std::for_each(begin(folderCmp), end(folderCmp), + [&](BaseDirMapping& baseMap) + { + exportString += utf8CvrtTo<UtfString>(baseMap.getBaseDirPf<LEFT_SIDE >()) + ';'; + exportString += utf8CvrtTo<UtfString>(baseMap.getBaseDirPf<RIGHT_SIDE>()) + '\n'; + }); + exportString += '\n'; - //write legend - exportString += copyStringTo<zxString>(_("Legend")) + wxT('\n'); - if (syncPreviewEnabled) - { - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_EQUAL)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_EQUAL)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_CREATE_NEW_LEFT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_CREATE_NEW_LEFT)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_CREATE_NEW_RIGHT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_CREATE_NEW_RIGHT)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_OVERWRITE_LEFT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_OVERWRITE_LEFT)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_OVERWRITE_RIGHT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_OVERWRITE_RIGHT)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_DELETE_LEFT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_DELETE_LEFT)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_DELETE_RIGHT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_DELETE_RIGHT)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_DO_NOTHING)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_DO_NOTHING)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getSyncOpDescription(SO_UNRESOLVED_CONFLICT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(SO_UNRESOLVED_CONFLICT)) + wxT('\n'); - } - else - { - exportString += wxT("\"") + copyStringTo<zxString>(getCategoryDescription(FILE_EQUAL)) + wxT("\";") + copyStringTo<zxString>(getSymbol(FILE_EQUAL)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getCategoryDescription(FILE_DIFFERENT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(FILE_DIFFERENT)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getCategoryDescription(FILE_LEFT_SIDE_ONLY)) + wxT("\";") + copyStringTo<zxString>(getSymbol(FILE_LEFT_SIDE_ONLY)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getCategoryDescription(FILE_RIGHT_SIDE_ONLY)) + wxT("\";") + copyStringTo<zxString>(getSymbol(FILE_RIGHT_SIDE_ONLY)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getCategoryDescription(FILE_LEFT_NEWER)) + wxT("\";") + copyStringTo<zxString>(getSymbol(FILE_LEFT_NEWER)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getCategoryDescription(FILE_RIGHT_NEWER)) + wxT("\";") + copyStringTo<zxString>(getSymbol(FILE_RIGHT_NEWER)) + wxT('\n'); - exportString += wxT("\"") + copyStringTo<zxString>(getCategoryDescription(FILE_CONFLICT)) + wxT("\";") + copyStringTo<zxString>(getSymbol(FILE_CONFLICT)) + wxT('\n'); - } - exportString += wxT('\n'); + //write header + auto provLeft = m_gridMain->getDataProvider(gridview::COMP_LEFT); + auto provMiddle = m_gridMain->getDataProvider(gridview::COMP_MIDDLE); + auto provRight = m_gridMain->getDataProvider(gridview::COMP_RIGHT); - auto addCellValue = [&](const wxString& val) - { - if (val.find(wxT(';')) != wxString::npos) - exportString += wxT('\"') + copyStringTo<zxString>(val) + wxT('\"'); - else - exportString += copyStringTo<zxString>(val); - }; + auto colAttrLeft = m_gridMain->getColumnConfig(gridview::COMP_LEFT); + auto colAttrMiddle = m_gridMain->getColumnConfig(gridview::COMP_MIDDLE); + auto colAttrRight = m_gridMain->getColumnConfig(gridview::COMP_RIGHT); - //write header - auto provLeft = m_gridMain->getDataProvider(gridview::COMP_LEFT); - auto provMiddle = m_gridMain->getDataProvider(gridview::COMP_MIDDLE); - auto provRight = m_gridMain->getDataProvider(gridview::COMP_RIGHT); + vector_remove_if(colAttrLeft , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + vector_remove_if(colAttrMiddle, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + vector_remove_if(colAttrRight , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - auto colAttrLeft = m_gridMain->getColumnConfig(gridview::COMP_LEFT); - auto colAttrMiddle = m_gridMain->getColumnConfig(gridview::COMP_MIDDLE); - auto colAttrRight = m_gridMain->getColumnConfig(gridview::COMP_RIGHT); + auto addCellValue = [&](const wxString& val) + { + if (val.find(L';') != wxString::npos) + exportString += '\"' + utf8CvrtTo<UtfString>(val) + '\"'; + else + exportString += utf8CvrtTo<UtfString>(val); + }; - vector_remove_if(colAttrLeft , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - vector_remove_if(colAttrMiddle, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); - vector_remove_if(colAttrRight , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + if (provLeft && provMiddle && provRight) + { + std::for_each(colAttrLeft.begin(), colAttrLeft.end(), + [&](const Grid::ColumnAttribute& ca) + { + addCellValue(provLeft->getColumnLabel(ca.type_)); + exportString += ';'; + }); + std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), + [&](const Grid::ColumnAttribute& ca) + { + addCellValue(provMiddle->getColumnLabel(ca.type_)); + exportString += ';'; + }); + if (!colAttrRight.empty()) + { + std::for_each(colAttrRight.begin(), colAttrRight.end() - 1, + [&](const Grid::ColumnAttribute& ca) + { + addCellValue(provRight->getColumnLabel(ca.type_)); + exportString += ';'; + }); + addCellValue(provRight->getColumnLabel(colAttrRight.back().type_)); + } + exportString += '\n'; - if (provLeft && provMiddle && provRight) + //main grid + const size_t rowCount = m_gridMain->getRowCount(); + for (size_t row = 0; row < rowCount; ++row) { std::for_each(colAttrLeft.begin(), colAttrLeft.end(), [&](const Grid::ColumnAttribute& ca) { - addCellValue(provLeft->getColumnLabel(ca.type_)); - exportString += L';'; + addCellValue(provLeft->getValue(row, ca.type_)); + exportString += ';'; }); std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), [&](const Grid::ColumnAttribute& ca) { - addCellValue(provMiddle->getColumnLabel(ca.type_)); - exportString += L';'; + addCellValue(provMiddle->getValue(row, ca.type_)); + exportString += ';'; }); if (!colAttrRight.empty()) { std::for_each(colAttrRight.begin(), colAttrRight.end() - 1, [&](const Grid::ColumnAttribute& ca) { - addCellValue(provRight->getColumnLabel(ca.type_)); - exportString += L';'; - }); - addCellValue(provRight->getColumnLabel(colAttrRight.back().type_)); - } - exportString += L'\n'; - - //main grid - const size_t rowCount = m_gridMain->getRowCount(); - for (size_t row = 0; row < rowCount; ++row) - { - std::for_each(colAttrLeft.begin(), colAttrLeft.end(), - [&](const Grid::ColumnAttribute& ca) - { - addCellValue(provLeft->getValue(row, ca.type_)); - exportString += L';'; + addCellValue(provRight->getValue(row, ca.type_)); + exportString += ';'; }); - std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), - [&](const Grid::ColumnAttribute& ca) - { - addCellValue(provMiddle->getValue(row, ca.type_)); - exportString += L';'; - }); - if (!colAttrRight.empty()) - { - std::for_each(colAttrRight.begin(), colAttrRight.end() - 1, - [&](const Grid::ColumnAttribute& ca) - { - addCellValue(provRight->getValue(row, ca.type_)); - exportString += L';'; - }); - addCellValue(provRight->getValue(row, colAttrRight.back().type_)); - } - exportString += L'\n'; + addCellValue(provRight->getValue(row, colAttrRight.back().type_)); } + exportString += '\n'; + } - //write export file - wxFFile output(newFileName.c_str(), wxT("w")); //don't write in binary mode - if (output.IsOpened()) - { - //generate UTF8 representation - size_t bufferSize = 0; - const wxCharBuffer utf8buffer = wxConvUTF8.cWC2MB(exportString.c_str(), exportString.size(), &bufferSize); - - output.Write(BYTE_ORDER_MARK_UTF8, sizeof(BYTE_ORDER_MARK_UTF8) - 1); - output.Write(utf8buffer, bufferSize); - pushStatusInformation(_("File list exported!")); - } - else - { - wxMessageBox(wxString(_("Error writing file:")) + wxT(" \"") + newFileName + wxT("\""), _("Error"), wxOK | wxICON_ERROR); - } + //write export file + wxFFile output(newFileName.c_str(), L"w"); //don't write in binary mode + if (output.IsOpened()) + { + output.Write(BYTE_ORDER_MARK_UTF8, sizeof(BYTE_ORDER_MARK_UTF8) - 1); + output.Write(exportString.c_str(), exportString.size()); + pushStatusInformation(_("File list exported!")); } + else + wxMessageBox(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(toZ(newFileName))), _("Error"), wxOK | wxICON_ERROR, this); } } @@ -3803,9 +3831,10 @@ void MainDialog::OnMenuBatchJob(wxCommandEvent& event) const wxString referenceFile = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : wxString(); - const xmlAccess::XmlBatchConfig batchCfg = convertGuiToBatch(currCfg, referenceFile); + const xmlAccess::XmlBatchConfig batchCfg = convertGuiToBatch(currCfg, toZ(referenceFile)); - showSyncBatchDlg(referenceFile, batchCfg, + showSyncBatchDlg(this, + referenceFile, batchCfg, folderHistoryLeft, folderHistoryRight, globalSettings->gui.onCompletionHistory, @@ -3815,7 +3844,7 @@ void MainDialog::OnMenuBatchJob(wxCommandEvent& event) void MainDialog::OnMenuCheckVersion(wxCommandEvent& event) { - zen::checkForUpdateNow(); + zen::checkForUpdateNow(this); } @@ -3824,7 +3853,7 @@ void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event) //execute just once per startup! Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this); - zen::checkForUpdatePeriodically(globalSettings->gui.lastUpdateCheck); + zen::checkForUpdatePeriodically(this, globalSettings->gui.lastUpdateCheck); } @@ -3847,7 +3876,7 @@ void MainDialog::OnLayoutWindowAsync(wxIdleEvent& event) void MainDialog::OnMenuAbout(wxCommandEvent& event) { - zen::showAboutDialog(); + zen::showAboutDialog(this); } diff --git a/ui/main_dlg.h b/ui/main_dlg.h index 228cef4a..6c0c6974 100644 --- a/ui/main_dlg.h +++ b/ui/main_dlg.h @@ -184,6 +184,7 @@ private: void updateGuiAfterFilterChange(int delay); void excludeExtension(const Zstring& extension); + void excludeShortname(const zen::FileSystemObject& fsObj); void excludeItems(const std::vector<zen::FileSystemObject*>& selection); void updateStatistics(); diff --git a/ui/msg_popup.cpp b/ui/msg_popup.cpp index 1ed892f8..b6067242 100644 --- a/ui/msg_popup.cpp +++ b/ui/msg_popup.cpp @@ -16,7 +16,7 @@ using namespace zen; class ErrorDlg : public ErrorDlgGenerated { public: - ErrorDlg(wxWindow* parentWindow, + ErrorDlg(wxWindow* parent, int activeButtons, const wxString& messageText, bool* ignoreNextErrors); @@ -31,15 +31,15 @@ private: }; -ErrorDlg::ErrorDlg(wxWindow* parentWindow, const int activeButtons, const wxString& messageText, bool* ignoreNextErrors) : - ErrorDlgGenerated(parentWindow), +ErrorDlg::ErrorDlg(wxWindow* parent, const int activeButtons, const wxString& messageText, bool* ignoreNextErrors) : + ErrorDlgGenerated(parent), ignoreErrors(ignoreNextErrors) { #ifdef FFS_WIN new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" #endif - m_bitmap10->SetBitmap(GlobalResources::getImage(wxT("error"))); + m_bitmap10->SetBitmap(GlobalResources::getImage(L"error")); m_textCtrl8->SetValue(messageText); if (ignoreNextErrors) @@ -96,9 +96,9 @@ void ErrorDlg::OnAbort(wxCommandEvent& event) } -ReturnErrorDlg::ButtonPressed zen::showErrorDlg(int activeButtons, const wxString& messageText, bool* ignoreNextErrors) +ReturnErrorDlg::ButtonPressed zen::showErrorDlg(wxWindow* parent, int activeButtons, const wxString& messageText, bool* ignoreNextErrors) { - ErrorDlg errorDlg(nullptr, activeButtons, messageText, ignoreNextErrors); + ErrorDlg errorDlg(parent, activeButtons, messageText, ignoreNextErrors); errorDlg.Raise(); return static_cast<ReturnErrorDlg::ButtonPressed>(errorDlg.ShowModal()); } @@ -108,7 +108,7 @@ ReturnErrorDlg::ButtonPressed zen::showErrorDlg(int activeButtons, const wxStrin class WarningDlg : public WarningDlgGenerated { public: - WarningDlg(wxWindow* parentWindow, int activeButtons, const wxString& messageText, bool& dontShowAgain); + WarningDlg(wxWindow* parent, int activeButtons, const wxString& messageText, bool& dontShowAgain); private: void OnClose(wxCloseEvent& event); @@ -119,15 +119,15 @@ private: }; -WarningDlg::WarningDlg(wxWindow* parentWindow, int activeButtons, const wxString& messageText, bool& dontShowDlgAgain) : - WarningDlgGenerated(parentWindow), +WarningDlg::WarningDlg(wxWindow* parent, int activeButtons, const wxString& messageText, bool& dontShowDlgAgain) : + WarningDlgGenerated(parent), dontShowAgain(dontShowDlgAgain) { #ifdef FFS_WIN new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" #endif - m_bitmap10->SetBitmap(GlobalResources::getImage(wxT("warning"))); + m_bitmap10->SetBitmap(GlobalResources::getImage(L"warning")); m_textCtrl8->SetValue(messageText); m_checkBoxDontShowAgain->SetValue(dontShowAgain); @@ -178,9 +178,9 @@ void WarningDlg::OnAbort(wxCommandEvent& event) } -ReturnWarningDlg::ButtonPressed zen::showWarningDlg(int activeButtons, const wxString& messageText, bool& dontShowAgain) +ReturnWarningDlg::ButtonPressed zen::showWarningDlg(wxWindow* parent, int activeButtons, const wxString& messageText, bool& dontShowAgain) { - WarningDlg warningDlg(nullptr, activeButtons, messageText, dontShowAgain); + WarningDlg warningDlg(parent, activeButtons, messageText, dontShowAgain); warningDlg.Raise(); return static_cast<ReturnWarningDlg::ButtonPressed>(warningDlg.ShowModal()); } @@ -190,7 +190,7 @@ ReturnWarningDlg::ButtonPressed zen::showWarningDlg(int activeButtons, const wxS class QuestionDlg : public QuestionDlgGenerated { public: - QuestionDlg(wxWindow* parentWindow, int activeButtons, const wxString& messageText, CheckBox* checkbox); + QuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, CheckBox* checkbox); private: void OnClose (wxCloseEvent& event) { EndModal(ReturnQuestionDlg::BUTTON_CANCEL); } @@ -202,8 +202,8 @@ private: }; -QuestionDlg::QuestionDlg(wxWindow* parentWindow, int activeButtons, const wxString& messageText, CheckBox* checkbox) : - QuestionDlgGenerated(parentWindow), +QuestionDlg::QuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, CheckBox* checkbox) : + QuestionDlgGenerated(parent), checkbox_(checkbox) { #ifdef FFS_WIN @@ -253,9 +253,9 @@ void QuestionDlg::OnNo(wxCommandEvent& event) EndModal(ReturnQuestionDlg::BUTTON_NO); } -ReturnQuestionDlg::ButtonPressed zen::showQuestionDlg(int activeButtons, const wxString& messageText, CheckBox* checkbox) +ReturnQuestionDlg::ButtonPressed zen::showQuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, CheckBox* checkbox) { - QuestionDlg qtnDlg(nullptr, activeButtons, messageText, checkbox); + QuestionDlg qtnDlg(parent, activeButtons, messageText, checkbox); qtnDlg.Raise(); return static_cast<ReturnQuestionDlg::ButtonPressed>(qtnDlg.ShowModal()); } diff --git a/ui/msg_popup.h b/ui/msg_popup.h index ccdf09e4..19a3c84f 100644 --- a/ui/msg_popup.h +++ b/ui/msg_popup.h @@ -7,10 +7,14 @@ #ifndef MESSAGEPOPUP_H_INCLUDED #define MESSAGEPOPUP_H_INCLUDED +#include <wx/window.h> #include <wx/string.h> + namespace zen { +//parent window, optional: support correct dialog placement above parent on multiple monitor systems + struct ReturnErrorDlg { enum ButtonPressed @@ -20,7 +24,7 @@ struct ReturnErrorDlg BUTTON_ABORT = 4 }; }; -ReturnErrorDlg::ButtonPressed showErrorDlg(int activeButtons, const wxString& messageText, bool* ignoreNextErrors); //ignoreNextErrors may be nullptr +ReturnErrorDlg::ButtonPressed showErrorDlg(wxWindow* parent, int activeButtons, const wxString& messageText, bool* ignoreNextErrors); //ignoreNextErrors may be nullptr struct ReturnWarningDlg @@ -32,7 +36,7 @@ struct ReturnWarningDlg BUTTON_ABORT = 4 }; }; -ReturnWarningDlg::ButtonPressed showWarningDlg(int activeButtons, const wxString& messageText, bool& dontShowAgain); +ReturnWarningDlg::ButtonPressed showWarningDlg(wxWindow* parent, int activeButtons, const wxString& messageText, bool& dontShowAgain); struct ReturnQuestionDlg @@ -53,8 +57,7 @@ struct CheckBox bool& value_; //in/out }; -ReturnQuestionDlg::ButtonPressed showQuestionDlg(int activeButtons, const wxString& messageText, CheckBox* checkbox = nullptr); +ReturnQuestionDlg::ButtonPressed showQuestionDlg(wxWindow* parent, int activeButtons, const wxString& messageText, CheckBox* checkbox = nullptr); } - #endif // MESSAGEPOPUP_H_INCLUDED diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp index 1e4be39a..fba2c801 100644 --- a/ui/progress_indicator.cpp +++ b/ui/progress_indicator.cpp @@ -6,20 +6,19 @@ #include "progress_indicator.h" #include <memory> -#include <zen/basic_math.h> #include <wx/imaglist.h> #include <wx/stopwatch.h> #include <wx/wupdlock.h> +#include <zen/basic_math.h> #include <wx+/mouse_move_dlg.h> #include <wx+/toggle_button.h> -#include <wx+/string_conv.h> #include <wx+/format_unit.h> #include <wx+/image_tools.h> #include <wx+/graph.h> #include <wx+/no_flicker.h> #include "gui_generated.h" #include "../lib/resources.h" -#include "../lib/statistics.h" +#include "../lib/perf_check.h" #include "tray_icon.h" #include "taskbar.h" #include "exec_finished_box.h" @@ -32,8 +31,8 @@ namespace const int GAUGE_FULL_RANGE = 50000; //window size used for statistics in milliseconds -const int windowSizeRemainingTime = 60000; //some usecases have dropouts of 40 seconds -> 60 sec. window size handles them well -const int windowSizeBytesPerSec = 5000; // +const int WINDOW_REMAINING_TIME = 60000; //some usecases have dropouts of 40 seconds -> 60 sec. window size handles them well +const int WINDOW_BYTES_PER_SEC = 5000; // } @@ -42,49 +41,24 @@ class CompareStatus::CompareStatusImpl : public CompareStatusGenerated public: CompareStatusImpl(wxTopLevelWindow& parentWindow); - void init(); //constructor/destructor semantics, but underlying Window is reused + void init(const Statistics& syncStat); //constructor/destructor semantics, but underlying Window is reused void finalize(); // - void switchToCompareBytewise(int totalObjectsToProcess, Int64 totalDataToProcess); - void incScannedObjects_NoUpdate(int number); - void incProcessedCmpData_NoUpdate(int objectsProcessed, Int64 dataProcessed); - void incTotalCmpData_NoUpdate (int objectsProcessed, Int64 dataProcessed); - - void setStatusText_NoUpdate(const wxString& text); + void switchToCompareBytewise(); void updateStatusPanelNow(); private: wxTopLevelWindow& parentWindow_; wxString titleTextBackup; - //status variables - size_t scannedObjects; - wxString currentStatusText; - wxStopWatch timeElapsed; - //gauge variables - int totalObjects; //file/dir/symlink/operation count - Int64 totalData; //unit: [bytes] - int currentObjects; - Int64 currentData; - - void showProgressExternally(const wxString& progressText, double fraction = 0); //between [0, 1] - - enum CurrentStatus - { - SCANNING, - COMPARING_CONTENT, - }; - - CurrentStatus status; + const Statistics* syncStat_; //only bound while sync is running std::unique_ptr<Taskbar> taskbar_; + std::unique_ptr<PerfCheck> perf; //estimate remaining time - //remaining time - std::unique_ptr<Statistics> statistics; - - long lastStatCallSpeed; //used for calculating intervals between statistics update + long lastStatCallSpeed; //used for calculating intervals between showing and collecting perf samples long lastStatCallRemTime; // }; @@ -92,21 +66,17 @@ private: CompareStatus::CompareStatusImpl::CompareStatusImpl(wxTopLevelWindow& parentWindow) : CompareStatusGenerated(&parentWindow), parentWindow_(parentWindow), - scannedObjects(0), - totalObjects(0), - totalData(0), - currentObjects(0), - currentData(0), - status(SCANNING), - lastStatCallSpeed(-1000000), //some big number + syncStat_(nullptr), + lastStatCallSpeed (-1000000), //some big number lastStatCallRemTime(-1000000) { - init(); + //init(); -> needed? } -void CompareStatus::CompareStatusImpl::init() +void CompareStatus::CompareStatusImpl::init(const Statistics& syncStat) { + syncStat_ = &syncStat; titleTextBackup = parentWindow_.GetTitle(); try //try to get access to Windows 7/Ubuntu taskbar @@ -115,65 +85,47 @@ void CompareStatus::CompareStatusImpl::init() } catch (const TaskbarNotAvailable&) {} - status = SCANNING; - //initialize gauge m_gauge2->SetRange(GAUGE_FULL_RANGE); m_gauge2->SetValue(0); + perf.reset(); + timeElapsed.Start(); //measure total time + //initially hide status that's relevant for comparing bytewise only bSizerFilesFound ->Show(true); bSizerFilesRemaining->Show(false); sSizerSpeed ->Show(false); sSizerTimeRemaining ->Show(false); - m_gauge2->Hide(); - bSizer42->Layout(); - - scannedObjects = 0; - currentStatusText.clear(); - - totalObjects = 0; - totalData = 0; - currentObjects = 0; - currentData = 0; - - statistics.reset(); - - timeElapsed.Start(); //measure total time - updateStatusPanelNow(); + m_gauge2->Hide(); + bSizer42->Layout(); Layout(); } void CompareStatus::CompareStatusImpl::finalize() { - taskbar_.reset(); + syncStat_ = nullptr; parentWindow_.SetTitle(titleTextBackup); + taskbar_.reset(); } -void CompareStatus::CompareStatusImpl::switchToCompareBytewise(int totalObjectsToProcess, Int64 totalDataToProcess) +void CompareStatus::CompareStatusImpl::switchToCompareBytewise() { - status = COMPARING_CONTENT; - - currentData = 0; - currentObjects = 0; - totalData = totalDataToProcess; - totalObjects = totalObjectsToProcess; - - //set new statistics handler: 10 seconds "window" for remaining time, 5 seconds for speed - statistics.reset(new Statistics(totalObjectsToProcess, to<double>(totalDataToProcess), windowSizeRemainingTime, windowSizeBytesPerSec)); + //start to measure perf + perf.reset(new PerfCheck(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC)); lastStatCallSpeed = -1000000; //some big number lastStatCallRemTime = -1000000; //show status for comparing bytewise - bSizerFilesFound->Show(false); + bSizerFilesFound ->Show(false); bSizerFilesRemaining->Show(true); - sSizerSpeed->Show(true); - sSizerTimeRemaining->Show(true); + sSizerSpeed ->Show(true); + sSizerTimeRemaining ->Show(true); m_gauge2->Show(); bSizer42->Layout(); @@ -181,154 +133,119 @@ void CompareStatus::CompareStatusImpl::switchToCompareBytewise(int totalObjectsT } -void CompareStatus::CompareStatusImpl::incScannedObjects_NoUpdate(int number) -{ - scannedObjects += number; -} - - -void CompareStatus::CompareStatusImpl::incProcessedCmpData_NoUpdate(int objectsDelta, Int64 dataDelta) -{ - currentData += dataDelta; - currentObjects += objectsDelta; -} - - -void CompareStatus::CompareStatusImpl::incTotalCmpData_NoUpdate(int objectsDelta, Int64 dataDelta) -{ - totalData += dataDelta; - totalObjects += objectsDelta; - - if (statistics) - statistics->setNewTotal(totalObjects, to<double>(totalData)); -} - - -void CompareStatus::CompareStatusImpl::setStatusText_NoUpdate(const wxString& text) +void CompareStatus::CompareStatusImpl::updateStatusPanelNow() { - currentStatusText = text; -} + if (!syncStat_) //no comparison running!! + return; + //wxWindowUpdateLocker dummy(this) -> not needed -void CompareStatus::CompareStatusImpl::showProgressExternally(const wxString& progressText, double fraction) -{ - if (parentWindow_.GetTitle() != progressText) - parentWindow_.SetTitle(progressText); + const wxString& scannedObjects = toStringSep(syncStat_->getObjectsCurrent(ProcessCallback::PHASE_SCANNING)); - //show progress on Windows 7 taskbar + auto setTitle = [&](const wxString& title) + { + if (parentWindow_.GetTitle() != title) + parentWindow_.SetTitle(title); + }; - if (taskbar_.get()) - switch (status) - { - case SCANNING: - taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); - break; - case COMPARING_CONTENT: - taskbar_->setProgress(fraction); - taskbar_->setStatus(Taskbar::STATUS_NORMAL); - break; - } -} + bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary + //status texts + setText(*m_textCtrlStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! -void CompareStatus::CompareStatusImpl::updateStatusPanelNow() -{ - //static RetrieveStatistics statistic; - //statistic.writeEntry(currentData.ToDouble(), currentObjects); + //write status information to taskbar, parent title ect. + switch (syncStat_->currentPhase()) { - //wxWindowUpdateLocker dummy(this) -> not needed - - //add both data + obj-count, to handle "deletion-only" cases - const double fraction = totalData + totalObjects == 0 ? 0 : std::max(0.0, to<double>(currentData + currentObjects) / to<double>(totalData + totalObjects)); + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + //dialog caption, taskbar + setTitle(scannedObjects + L" - " + _("Scanning...")); + if (taskbar_.get()) //support Windows 7 taskbar + taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); + break; - //write status information to taskbar, parent title ect. - switch (status) + case ProcessCallback::PHASE_COMPARING_CONTENT: { - case SCANNING: - showProgressExternally(toStringSep(scannedObjects) + wxT(" - ") + _("Scanning...")); - break; - case COMPARING_CONTENT: - showProgressExternally(fractionToShortString(fraction) + wxT(" - ") + _("Comparing content..."), fraction); - break; - } + auto objectsCurrent = syncStat_->getObjectsCurrent(ProcessCallback::PHASE_COMPARING_CONTENT); + auto objectsTotal = syncStat_->getObjectsTotal (ProcessCallback::PHASE_COMPARING_CONTENT); + auto dataCurrent = syncStat_->getDataCurrent (ProcessCallback::PHASE_COMPARING_CONTENT); + auto dataTotal = syncStat_->getDataTotal (ProcessCallback::PHASE_COMPARING_CONTENT); - bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary + //add both data + obj-count, to handle "deletion-only" cases + const double fraction = dataTotal + objectsTotal == 0 ? 0 : std::max(0.0, to<double>(dataCurrent + objectsCurrent) / to<double>(dataTotal + objectsTotal)); - //remove linebreaks from currentStatusText - wxString statusTextFmt = currentStatusText; - replace(statusTextFmt, L'\n', L' '); + //dialog caption, taskbar + setTitle(fractionToShortString(fraction) + wxT(" - ") + _("Comparing content...")); + if (taskbar_.get()) + { + taskbar_->setProgress(fraction); + taskbar_->setStatus(Taskbar::STATUS_NORMAL); + } - //status texts - if (m_textCtrlStatus->GetValue() != statusTextFmt) //no layout update for status texts! - m_textCtrlStatus->ChangeValue(statusTextFmt); + //progress indicator, shown for binary comparison only + m_gauge2->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); - //nr of scanned objects - setText(*m_staticTextScanned, toStringSep(scannedObjects), &layoutChanged); + //remaining objects and bytes for file comparison + setText(*m_staticTextFilesRemaining, toStringSep(objectsTotal - objectsCurrent), &layoutChanged); + setText(*m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged); - //progress indicator for "compare file content" - m_gauge2->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); + //remaining time and speed: only visible during binary comparison + if (perf) + if (timeElapsed.Time() - lastStatCallSpeed >= 500) //-> Win 7 copy uses 1 sec update interval + { + lastStatCallSpeed = timeElapsed.Time(); - //remaining files left for file comparison - const wxString filesToCompareTmp = toStringSep(totalObjects - currentObjects); - setText(*m_staticTextFilesRemaining, filesToCompareTmp, &layoutChanged); + perf->addSample(objectsCurrent, to<double>(dataCurrent), timeElapsed.Time()); - //remaining bytes left for file comparison - const wxString remainingBytesTmp = zen::filesizeToShortString(totalData - currentData); - setText(*m_staticTextDataRemaining, remainingBytesTmp, &layoutChanged); + //current speed + setText(*m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged); - if (statistics.get()) - { - if (timeElapsed.Time() - lastStatCallSpeed >= 500) //call method every 500 ms - { - lastStatCallSpeed = timeElapsed.Time(); + if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //update GUI every 2 sec + { + lastStatCallRemTime = timeElapsed.Time(); - statistics->addMeasurement(currentObjects, to<double>(currentData)); + //remaining time + setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged); + } + } + } + break; - //current speed - setText(*m_staticTextSpeed, statistics->getBytesPerSecond(), &layoutChanged); + case ProcessCallback::PHASE_SYNCHRONIZING: + assert(false); + break; + } - if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //call method every two seconds only - { - lastStatCallRemTime = timeElapsed.Time(); + //nr of scanned objects + setText(*m_staticTextScanned, scannedObjects, &layoutChanged); - //remaining time - setText(*m_staticTextRemTime, statistics->getRemainingTime(), &layoutChanged); - } - } - } + //time elapsed + const long timeElapSec = timeElapsed.Time() / 1000; + setText(*m_staticTextTimeElapsed, + timeElapSec < 3600 ? + wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : + wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); - //time elapsed - const long timeElapSec = timeElapsed.Time() / 1000; - setText(*m_staticTextTimeElapsed, - timeElapSec < 3600 ? - wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : - wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); + //do the ui update + if (layoutChanged) + bSizer42->Layout(); - //do the ui update - if (layoutChanged) - bSizer42->Layout(); - } updateUiNow(); } //######################################################################################## //redirect to implementation CompareStatus::CompareStatus(wxTopLevelWindow& parentWindow) : - pimpl(new CompareStatusImpl(parentWindow)) {} - -CompareStatus::~CompareStatus() -{ - //DON'T delete pimpl! it relies on wxWidgets destruction (parent window destroys child windows!) -} + pimpl(new CompareStatusImpl(parentWindow)) {} //owned by parentWindow wxWindow* CompareStatus::getAsWindow() { return pimpl; } -void CompareStatus::init() +void CompareStatus::init(const Statistics& syncStat) { - pimpl->init(); + pimpl->init(syncStat); } void CompareStatus::finalize() @@ -336,29 +253,9 @@ void CompareStatus::finalize() pimpl->finalize(); } -void CompareStatus::switchToCompareBytewise(int totalObjectsToProcess, zen::Int64 totalDataToProcess) -{ - pimpl->switchToCompareBytewise(totalObjectsToProcess, totalDataToProcess); -} - -void CompareStatus::incScannedObjects_NoUpdate(int number) -{ - pimpl->incScannedObjects_NoUpdate(number); -} - -void CompareStatus::incProcessedCmpData_NoUpdate(int objectsDelta, Int64 dataDelta) +void CompareStatus::switchToCompareBytewise() { - pimpl->incProcessedCmpData_NoUpdate(objectsDelta, dataDelta); -} - -void CompareStatus::incTotalCmpData_NoUpdate(int objectsDelta, Int64 dataDelta) -{ - pimpl->incTotalCmpData_NoUpdate(objectsDelta, dataDelta); -} - -void CompareStatus::setStatusText_NoUpdate(const wxString& text) -{ - pimpl->setStatusText_NoUpdate(text); + pimpl->switchToCompareBytewise(); } void CompareStatus::updateStatusPanelNow() @@ -399,14 +296,9 @@ public: const int warningCount = log_.getItemCount(TYPE_WARNING); const int infoCount = log_.getItemCount(TYPE_INFO); - m_bpButtonErrors->init(buttonPressed ("error"), wxString(_("Error")) + wxString::Format(wxT(" (%d)"), errorCount), - buttonReleased("error"), wxString(_("Error")) + wxString::Format(wxT(" (%d)"), errorCount)); - - m_bpButtonWarnings->init(buttonPressed ("warning"), wxString(_("Warning")) + wxString::Format(wxT(" (%d)"), warningCount), - buttonReleased("warning"), wxString(_("Warning")) + wxString::Format(wxT(" (%d)"), warningCount)); - - m_bpButtonInfo->init(buttonPressed ("info"), wxString(_("Info")) + wxString::Format(wxT(" (%d)"), infoCount), - buttonReleased("info"), wxString(_("Info")) + wxString::Format(wxT(" (%d)"), infoCount)); + m_bpButtonErrors ->init(buttonPressed ("error" ), buttonReleased("error" ), _("Error" ) + wxString::Format(L" (%d)", errorCount )); + m_bpButtonWarnings->init(buttonPressed ("warning"), buttonReleased("warning"), _("Warning") + wxString::Format(L" (%d)", warningCount)); + m_bpButtonInfo ->init(buttonPressed ("info" ), buttonReleased("info" ), _("Info" ) + wxString::Format(L" (%d)", infoCount )); m_bpButtonErrors ->setActive(true); m_bpButtonWarnings->setActive(true); @@ -421,6 +313,7 @@ public: m_textCtrlInfo->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(LogControl::onKeyEvent), nullptr, this); } +private: virtual void OnErrors(wxCommandEvent& event) { m_bpButtonErrors->toggle(); @@ -439,7 +332,6 @@ public: updateLogText(); } -private: void onKeyEvent(wxKeyEvent& event) { const int keyCode = event.GetKeyCode(); @@ -478,7 +370,7 @@ private: logText += L'\n'; } - if (logText.empty()) //if no messages match selected view filter, show final status message at least + if (logText.empty()) //if no messages match selected view filter, at least show final status message if (!entries.empty()) logText = copyStringTo<zxString>(formatMessage(entries.back())); @@ -490,7 +382,6 @@ private: const ErrorLog log_; }; - //######################################################################################## namespace @@ -498,20 +389,19 @@ namespace class GraphDataBytes : public GraphData { public: - void addCurrentValue(double dataCurrent) + void addRecord(double dataCurrent, long timeMs) { - data.insert(data.end(), std::make_pair(timer.Time(), dataCurrent)); + data.insert(data.end(), std::make_pair(timeMs, dataCurrent)); //documentation differs about whether "hint" should be before or after the to be inserted element! //however "std::map<>::end()" is interpreted correctly by GCC and VS2010 - if (data.size() > MAX_BUFFER_SIZE) //guard against too large a buffer + if (data.size() > MAX_BUFFER_SIZE) //limit buffer size data.erase(data.begin()); } - void pauseTimer () { timer.Pause(); } - void resumeTimer() { timer.Resume(); } + void clear() { data.clear(); } - virtual double getXBegin() const { return 0; } //{ return data.empty() ? 0 : data.begin()->first / 1000.0; } + virtual double getXBegin() const { return data.empty() ? 0 : data.begin()->first / 1000.0; } //need not start with 0, e.g. "binary comparison, graph reset, followed by sync" virtual double getXEnd () const { return data.empty() ? 0 : (--data.end())->first / 1000.0; } private: @@ -526,7 +416,6 @@ private: } //example: two-element range is accessible within [0, 2) - wxStopWatch timer; std::map<long, double> data; }; @@ -560,11 +449,7 @@ struct LabelFormatterBytes : public LabelFormatter bytesProposed *= 1.5; //enlarge block default size - //if (bytesProposed <= 1024 * 1024) //set 1 MB min size: reduce initial rapid changes in y-label - // return 1024 * 1024; - //round to next number which is a convenient to read block size - const double k = std::floor(std::log(bytesProposed) / std::log(2.0)); const double e = std::pow(2.0, k); if (numeric::isNull(e)) @@ -644,132 +529,125 @@ class SyncStatus::SyncStatusImpl : public SyncStatusDlgGenerated { public: SyncStatusImpl(AbortCallback& abortCb, + const Statistics& syncStat, MainDialog* parentWindow, - SyncStatusID startStatus, const wxString& jobName, const std::wstring& execWhenFinished, std::vector<std::wstring>& execFinishedHistory); ~SyncStatusImpl(); - void initNewProcess(SyncStatusID id, int totalObjectsToProcess, Int64 totalDataToProcess); - - void incScannedObjects_NoUpdate(int number); - void incProcessedData_NoUpdate(int objectsDelta, Int64 dataDelta); - void incTotalData_NoUpdate (int objectsDelta, Int64 dataDelta); - - void setStatusText_NoUpdate(const wxString& text); - void updateStatusDialogNow(bool allowYield = true); + void initNewPhase(); + void reportCurrentBytes(Int64 currentData); + void updateProgress(bool allowYield = true); - void processHasFinished(SyncStatus::SyncStatusID id, const ErrorLog& log); //essential to call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater + //call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater + void processHasFinished(SyncResult resultId, const ErrorLog& log); + void closeWindowDirectly(); std::wstring getExecWhenFinishedCommand() const; - void stopTimer(); //halt all internal counters! - void resumeTimer(); // + void stopTimer() //halt all internal counters! + { + m_animationControl1->Stop(); + timeElapsed.Pause (); + } + void resumeTimer() + { + m_animationControl1->Play(); + timeElapsed.Resume(); + } void minimizeToTray(); private: void OnKeyPressed(wxKeyEvent& event); - virtual void OnOkay(wxCommandEvent& event); - virtual void OnPause(wxCommandEvent& event); - virtual void OnAbort(wxCommandEvent& event); - virtual void OnClose(wxCloseEvent& event); + virtual void OnOkay (wxCommandEvent& event); + virtual void OnPause (wxCommandEvent& event); + virtual void OnAbort (wxCommandEvent& event); + virtual void OnClose (wxCloseEvent& event); virtual void OnIconize(wxIconizeEvent& event); - void setCurrentStatus(SyncStatus::SyncStatusID id); + void updateDialogStatus(); void resumeFromSystray(); void OnResumeFromTray(wxCommandEvent& event); - bool currentProcessIsRunning(); - void showProgressExternally(const wxString& progressText, double fraction = 0); //between [0, 1] + void setExternalStatus(const wxString& status, const wxString& progress); //progress may be empty! const wxString jobName_; wxStopWatch timeElapsed; - AbortCallback* abortCb_; //temporarily bound MainDialog* mainDialog; //optional - //gauge variables - int totalObjects; //file/dir/symlink/operation count - Int64 totalData; //unit: [bytes] - int currentObjects; - Int64 currentData; - //status variables - size_t scannedObjects; - wxString currentStatusText; + AbortCallback* abortCb_; //temporarily bound while sync is running + const Statistics* syncStat_; // + bool paused_; //valid only while sync is running + SyncResult finalResult; //set after sync - SyncStatus::SyncStatusID currentStatus; - SyncStatus::SyncStatusID previousStatus; //save old status if "currentStatus == SyncStatus::PAUSED" - - std::unique_ptr<Taskbar> taskbar_; + bool isZombie; //wxGTK sends iconize event *after* wxWindow::Destroy, sigh... //remaining time - std::unique_ptr<Statistics> statistics; - long lastStatCallSpeed; //used for calculating intervals between statistics update + std::unique_ptr<PerfCheck> perf; + long lastStatCallSpeed; //used for calculating intervals between collecting perf samples long lastStatCallRemTime; // std::shared_ptr<GraphDataBytes> graphDataBytes; - //std::shared_ptr<GraphDataConstLine> graphDataBytesCurrent; std::shared_ptr<GraphDataConstLine> graphDataBytesTotal; wxString titelTextBackup; - std::unique_ptr<FfsTrayIcon> trayIcon; //optional: if filled all other windows should be hidden and conversely + std::unique_ptr<Taskbar> taskbar_; }; SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, + const Statistics& syncStat, MainDialog* parentWindow, - SyncStatusID startStatus, const wxString& jobName, const std::wstring& execWhenFinished, std::vector<std::wstring>& execFinishedHistory) : SyncStatusDlgGenerated(parentWindow, wxID_ANY, - parentWindow ? wxString(wxEmptyString) : (wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")), + parentWindow ? wxString() : (wxString(L"FreeFileSync - ") + _("Folder Comparison and Synchronization")), wxDefaultPosition, wxSize(640, 350), parentWindow ? wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT : //wxTAB_TRAVERSAL is needed for standard button handling: wxID_OK/wxID_CANCEL wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL), - jobName_(jobName), - abortCb_(&abortCb), + jobName_ (jobName), mainDialog(parentWindow), - totalObjects(0), - totalData(0), - currentObjects(0), - currentData(0), - scannedObjects(0), - currentStatus (SyncStatus::ABORTED), - previousStatus(SyncStatus::ABORTED), - lastStatCallSpeed(-1000000), //some big number + abortCb_ (&abortCb), + syncStat_ (&syncStat), + paused_ (false), + finalResult(RESULT_ABORTED), //dummy value + isZombie(false), + lastStatCallSpeed (-1000000), //some big number lastStatCallRemTime(-1000000) { #ifdef FFS_WIN - new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" + new MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" #endif - if (mainDialog) //save old title (will be used as progress indicator) - titelTextBackup = mainDialog->GetTitle(); + if (mainDialog) + { + titelTextBackup = mainDialog->GetTitle(); //save old title (will be used as progress indicator) + mainDialog->disableAllElements(false); //disable all child elements + } m_animationControl1->SetAnimation(GlobalResources::instance().animationSync); m_animationControl1->Play(); + SetIcon(GlobalResources::instance().programIcon); + //initialize gauge m_gauge1->SetRange(GAUGE_FULL_RANGE); m_gauge1->SetValue(0); - EnableCloseButton(false); if (IsShown()) //don't steal focus when starting in sys-tray! m_buttonAbort->SetFocus(); - if (mainDialog) - mainDialog->disableAllElements(false); //disable all child elements - timeElapsed.Start(); //measure total time try //try to get access to Windows 7/Ubuntu taskbar @@ -780,39 +658,29 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, //hide "processed" statistics until end of process bSizerFinalStat ->Show(false); - m_buttonOK ->Show(false); m_staticTextLabelItemsProc->Show(false); bSizerItemsProc ->Show(false); - - SetIcon(GlobalResources::instance().programIcon); //set application icon + m_buttonOK ->Show(false); //register key event Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncStatusImpl::OnKeyPressed), nullptr, this); - setCurrentStatus(startStatus); //first state: will be shown while waiting for dir locks (if at all) - //init graph - graphDataBytes = std::make_shared<GraphDataBytes>(); - //graphDataBytesCurrent = std::make_shared<GraphDataConstLine>(); - graphDataBytesTotal = std::make_shared<GraphDataConstLine>(); + graphDataBytes = std::make_shared<GraphDataBytes>(); + graphDataBytesTotal = std::make_shared<GraphDataConstLine>(); m_panelGraph->setAttributes(Graph2D::GraphAttributes(). setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()). setLabelY(Graph2D::Y_LABEL_RIGHT, 60, std::make_shared<LabelFormatterBytes>())); - m_panelGraph->setData(graphDataBytesTotal, Graph2D::LineAttributes().setLineWidth(2).setColor(wxColor(0, 64, 0))); //green - //m_panelGraph->addData(graphDataBytesCurrent, Graph2D::LineAttributes().setLineWidth(2).setColor(wxColor(0, 128, 0))); //green - m_panelGraph->addData(graphDataBytes, Graph2D::LineAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0))); //medium green + m_panelGraph->setData(graphDataBytesTotal, Graph2D::LineAttributes().setLineWidth(2).setColor(wxColor(0, 64, 0))); //green + m_panelGraph->addData(graphDataBytes, Graph2D::LineAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0))); //medium green //allow changing on completion command - m_comboBoxExecFinished->setValue(execWhenFinished); + m_comboBoxExecFinished->setValue (execWhenFinished); m_comboBoxExecFinished->setHistoryRef(execFinishedHistory); - //Fit() height only: - //fitHeight(*this); - - m_staticTextSpeed ->SetLabel(L""); //clear "dummy" values - m_staticTextRemTime->SetLabel(L""); // + updateDialogStatus(); //null-status will be shown while waiting for dir locks (if at all) } @@ -838,12 +706,14 @@ void SyncStatus::SyncStatusImpl::OnKeyPressed(wxKeyEvent& event) //simulate click on abort button if (m_buttonAbort->IsShown()) //delegate to "abort" button if available { - m_buttonAbort->GetEventHandler()->ProcessEvent(dummy); + if (wxEvtHandler* handler = m_buttonAbort->GetEventHandler()) + handler->ProcessEvent(dummy); return; } - else if (m_buttonOK->IsShown()) //delegate to "abort" button if available + else if (m_buttonOK->IsShown()) { - m_buttonOK->GetEventHandler()->ProcessEvent(dummy); + if (wxEvtHandler* handler = m_buttonOK->GetEventHandler()) + handler->ProcessEvent(dummy); return; } } @@ -852,119 +722,37 @@ void SyncStatus::SyncStatusImpl::OnKeyPressed(wxKeyEvent& event) } -void SyncStatus::SyncStatusImpl::initNewProcess(SyncStatusID id, int totalObjectsToProcess, Int64 totalDataToProcess) +void SyncStatus::SyncStatusImpl::initNewPhase() { - setCurrentStatus(id); + updateDialogStatus(); //evaluates "syncStat_->currentPhase()" - currentData = 0; - currentObjects = 0; - totalData = totalDataToProcess; - totalObjects = totalObjectsToProcess; - - incProcessedData_NoUpdate(0, 0); //update graph - incTotalData_NoUpdate (0, 0); // - - //set new statistics handler: 10 seconds "window" for remaining time, 5 seconds for speed - statistics.reset(new Statistics(totalObjectsToProcess, to<double>(totalDataToProcess), windowSizeRemainingTime, windowSizeBytesPerSec)); + //reset graph (e.g. after binary comparison) + graphDataBytes->clear(); + reportCurrentBytes(0); + //start new measurement + perf.reset(new PerfCheck(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC)); lastStatCallSpeed = -1000000; //some big number lastStatCallRemTime = -1000000; //set to 0 even if totalDataToProcess is 0: due to a bug in wxGauge::SetValue, it doesn't change to determinate mode when setting the old value again - //so give updateStatusDialogNow() a chance to set a different value + //so give updateProgress() a chance to set a different value m_gauge1->SetValue(0); - updateStatusDialogNow(false); //get rid of "dummy" texts! -} - - -void SyncStatus::SyncStatusImpl::incProcessedData_NoUpdate(int objectsDelta, Int64 dataDelta) -{ - currentData += dataDelta; - currentObjects += objectsDelta; - - //update graph data - graphDataBytes->addCurrentValue(to<double>(currentData)); + updateProgress(false); } -void SyncStatus::SyncStatusImpl::incTotalData_NoUpdate(int objectsDelta, Int64 dataDelta) +void SyncStatus::SyncStatusImpl::reportCurrentBytes(Int64 currentData) { - totalData += dataDelta; - totalObjects += objectsDelta; - - if (statistics) - statistics->setNewTotal(totalObjects, to<double>(totalData)); - - graphDataBytesTotal->setValue(to<double>(totalData)); - //m_panelGraph->setAttributes(m_panelGraph->getAttributes().setMaxY(to<double>(totalDataToProcess))); + //add sample for perf measurements + calc. of remaining time + graphDataBytes->addRecord(to<double>(currentData), timeElapsed.Time()); } -void SyncStatus::SyncStatusImpl::incScannedObjects_NoUpdate(int number) -{ - scannedObjects += number; -} - - -void SyncStatus::SyncStatusImpl::setStatusText_NoUpdate(const wxString& text) -{ - currentStatusText = text; -} - - -void SyncStatus::SyncStatusImpl::showProgressExternally(const wxString& progressText, double fraction) -{ - //write status information to systray, if window is minimized - if (trayIcon.get()) - trayIcon->setToolTip(progressText, fraction); - - wxString progressTextFmt = progressText; - progressTextFmt.Replace(wxT("\n"), wxT(" - ")); - - if (mainDialog) //show percentage in maindialog title (and thereby in taskbar) - { - if (mainDialog->GetTitle() != progressTextFmt) - mainDialog->SetTitle(progressTextFmt); - } - else //show percentage in this dialog's title (and thereby in taskbar) - { - if (this->GetTitle() != progressTextFmt) - this->SetTitle(progressTextFmt); - } - - - //show progress on Windows 7 taskbar - if (taskbar_.get()) - { - switch (currentStatus) - { - case SyncStatus::SCANNING: - taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); - break; - case SyncStatus::FINISHED_WITH_SUCCESS: - case SyncStatus::COMPARING_CONTENT: - case SyncStatus::SYNCHRONIZING: - taskbar_->setProgress(fraction); - taskbar_->setStatus(Taskbar::STATUS_NORMAL); - break; - case SyncStatus::PAUSE: - taskbar_->setProgress(fraction); - taskbar_->setStatus(Taskbar::STATUS_PAUSED); - break; - case SyncStatus::ABORTED: - case SyncStatus::FINISHED_WITH_ERROR: - taskbar_->setProgress(fraction); - taskbar_->setStatus(Taskbar::STATUS_ERROR); - break; - } - } -} - - -#ifdef FFS_WIN namespace { +#ifdef FFS_WIN enum Zorder { ZORDER_CORRECT, @@ -988,131 +776,183 @@ Zorder evaluateZorder(const wxWindow& top, const wxWindow& bottom) return ZORDER_INDEFINITE; } -} #endif -void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) +std::wstring getDialogStatusText(const Statistics* syncStat, bool paused, SyncStatus::SyncResult finalResult) +{ + if (syncStat) //sync running + { + if (paused) + return _("Paused"); + else + switch (syncStat->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + return _("Initializing..."); //dialog is shown *before* sync starts, so this text may be visible! + case ProcessCallback::PHASE_SCANNING: + return _("Scanning..."); + case ProcessCallback::PHASE_COMPARING_CONTENT: + return _("Comparing content..."); + case ProcessCallback::PHASE_SYNCHRONIZING: + return _("Synchronizing..."); + } + } + else //sync finished + switch (finalResult) + { + case SyncStatus::RESULT_ABORTED: + return _("Aborted"); + case SyncStatus::RESULT_FINISHED_WITH_ERROR: + case SyncStatus::RESULT_FINISHED_WITH_SUCCESS: + return _("Completed"); + } + return std::wstring(); +} +} + + +void SyncStatus::SyncStatusImpl::setExternalStatus(const wxString& status, const wxString& progress) //progress may be empty! { - //static RetrieveStatistics statistic; - //statistic.writeEntry(currentData.ToDouble(), currentObjects); + //sys tray: order "top-down": jobname, status, progress + wxString newTrayInfo = jobName_.empty() ? status : L"\"" + jobName_ + L"\"\n" + status; + if (!progress.empty()) + newTrayInfo += L" " + progress; - //add both data + obj-count, to handle "deletion-only" cases - const double fraction = totalData + totalObjects == 0 ? 1 : std::max(0.0, to<double>(currentData + currentObjects) / to<double>(totalData + totalObjects)); - //yes, this may legitimately become < 0: failed rename operation falls-back to copy + delete, reducing "currentData" to potentially < 0! + //window caption/taskbar; inverse order: progress, status, jobname + wxString newCaption = progress.empty() ? status : progress + L" - " + status; + if (!jobName_.empty()) + newCaption += L" - \"" + jobName_ + L"\""; - //write status information to systray, taskbar, parent title ect. + //systray tooltip, if window is minimized + if (trayIcon.get()) + trayIcon->setToolTip(newTrayInfo); - const wxString postFix = jobName_.empty() ? wxString() : (wxT("\n\"") + jobName_ + wxT("\"")); - switch (currentStatus) + //show text in dialog title (and at the same time in taskbar) + if (mainDialog) { - case SyncStatus::SCANNING: - showProgressExternally(wxString() + toStringSep(scannedObjects) + wxT(" - ") + _("Scanning...") + postFix); - break; - case SyncStatus::COMPARING_CONTENT: - showProgressExternally(wxString() + fractionToShortString(fraction) + wxT(" - ") + _("Comparing content...") + postFix, fraction); - break; - case SyncStatus::SYNCHRONIZING: - showProgressExternally(wxString() + fractionToShortString(fraction) + wxT(" - ") + _("Synchronizing...") + postFix, fraction); - break; - case SyncStatus::PAUSE: - showProgressExternally(wxString() + fractionToShortString(fraction) + wxT(" - ") + _("Paused") + postFix, fraction); - break; - case SyncStatus::ABORTED: - showProgressExternally(_("Aborted") + postFix, fraction); - break; - case SyncStatus::FINISHED_WITH_SUCCESS: - case SyncStatus::FINISHED_WITH_ERROR: - showProgressExternally(_("Completed") + postFix, fraction); - break; + if (mainDialog->GetTitle() != newCaption) + mainDialog->SetTitle(newCaption); } - - //write regular status information (whether dialog is visible or not) + else { - //wxWindowUpdateLocker dummy(this); -> not needed + if (this->GetTitle() != newCaption) + this->SetTitle(newCaption); + } +} - bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary - //progress indicator - switch (currentStatus) - { - case SyncStatus::SCANNING: - m_gauge1->Pulse(); - break; - case SyncStatus::COMPARING_CONTENT: - case SyncStatus::SYNCHRONIZING: - case SyncStatus::FINISHED_WITH_SUCCESS: - case SyncStatus::FINISHED_WITH_ERROR: - case SyncStatus::ABORTED: - m_gauge1->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); - break; - case SyncStatus::PAUSE: //no change to gauge: don't switch between indeterminate/determinate modus - break; - } +void SyncStatus::SyncStatusImpl::updateProgress(bool allowYield) +{ + assert(syncStat_); + if (!syncStat_) //no sync running!! + return; + //wxWindowUpdateLocker dummy(this); -> not needed - wxString statusTextFmt = currentStatusText; //remove linebreaks - replace(statusTextFmt, L'\n', L' '); // + bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary - //status text - if (m_textCtrlInfo->GetValue() != statusTextFmt) //no layout update for status texts! - m_textCtrlInfo->ChangeValue(statusTextFmt); + //sync status text + setText(*m_textCtrlInfo, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! - //remaining objects - const wxString remainingObjTmp = toStringSep(totalObjects - currentObjects); - setText(*m_staticTextRemainingObj, remainingObjTmp, &layoutChanged); + switch (syncStat_->currentPhase()) //no matter if paused or not + { + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + //dialog caption, taskbar, systray tooltip + setExternalStatus(getDialogStatusText(syncStat_, paused_, finalResult), toStringSep(syncStat_->getObjectsCurrent(ProcessCallback::PHASE_SCANNING))); //status text may be "paused"! - //remaining bytes left for copy - const wxString remainingBytesTmp = zen::filesizeToShortString(totalData - currentData); - setText(*m_staticTextDataRemaining, remainingBytesTmp, &layoutChanged); + //progress indicators + m_gauge1->Pulse(); + if (trayIcon.get()) trayIcon->setProgress(1); //1 = regular FFS logo - //statistics - if (statistics.get()) - { - if (timeElapsed.Time() - lastStatCallSpeed >= 500) //call method every 500 ms - { - lastStatCallSpeed = timeElapsed.Time(); + //taskbar_ status is Taskbar::STATUS_INDETERMINATE + + //constant line graph + graphDataBytesTotal->setValue(0); - statistics->addMeasurement(currentObjects, to<double>(currentData)); + //remaining objects and data + setText(*m_staticTextRemainingObj , L"-", &layoutChanged); + setText(*m_staticTextDataRemaining, L"", &layoutChanged); - //current speed - setText(*m_staticTextSpeed, statistics->getBytesPerSecond(), &layoutChanged); + //remaining time and speed + setText(*m_staticTextSpeed, L"-", &layoutChanged); + setText(*m_staticTextRemTime, L"-", &layoutChanged); + break; - if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //call method every two seconds only + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + { + auto objectsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase()); + auto objectsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase()); + auto dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase()); + auto dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase()); + + //add both data + obj-count, to handle "deletion-only" cases + const double fraction = dataTotal + objectsTotal == 0 ? 1 : std::max(0.0, to<double>(dataCurrent + objectsCurrent) / to<double>(dataTotal + objectsTotal)); + //yes, this may legitimately become < 0: failed rename operation falls-back to copy + delete, reducing "dataCurrent" to potentially < 0! + //---------------------------------------------------------------------------------------------------- + + //dialog caption, taskbar, systray tooltip + setExternalStatus(getDialogStatusText(syncStat_, paused_, finalResult), fractionToShortString(fraction)); //status text may be "paused"! + + //progress indicators + m_gauge1->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); + if (trayIcon.get()) trayIcon->setProgress(fraction); + if (taskbar_.get()) taskbar_->setProgress(fraction); + + //constant line graph + graphDataBytesTotal->setValue(to<double>(dataTotal)); + + //remaining objects and data + setText(*m_staticTextRemainingObj, toStringSep(objectsTotal - objectsCurrent), &layoutChanged); + setText(*m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged); + + //remaining time and speed + assert(perf); + if (perf) + if (timeElapsed.Time() - lastStatCallSpeed >= 500) //-> Win 7 copy uses 1 sec update interval { - lastStatCallRemTime = timeElapsed.Time(); + lastStatCallSpeed = timeElapsed.Time(); - //remaining time - setText(*m_staticTextRemTime, statistics->getRemainingTime(), &layoutChanged); - } - } - } + perf->addSample(objectsCurrent, to<double>(dataCurrent), timeElapsed.Time()); - { - m_panelGraph->setAttributes(m_panelGraph->getAttributes().setMinX(graphDataBytes->getXBegin())); - m_panelGraph->setAttributes(m_panelGraph->getAttributes().setMaxX(graphDataBytes->getXEnd())); + //current speed + setText(*m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged); - m_panelGraph->Refresh(); + if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //update GUI every 2 sec + { + lastStatCallRemTime = timeElapsed.Time(); + + //remaining time + setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged); + } + } } + break; + } - //time elapsed - const long timeElapSec = timeElapsed.Time() / 1000; - setText(*m_staticTextTimeElapsed, - timeElapSec < 3600 ? - wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : - wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); + m_panelGraph->setAttributes(m_panelGraph->getAttributes().setMinX(graphDataBytes->getXBegin())); + m_panelGraph->setAttributes(m_panelGraph->getAttributes().setMaxX(graphDataBytes->getXEnd())); + m_panelGraph->Refresh(); - //do the ui update - if (layoutChanged) - { - // Layout(); - // bSizerItemsRem->Layout(); - // bSizer171->Layout(); - bSizerProgressStat->Layout(); // - m_panelProgress->Layout(); //both needed - //m_panelBackground->Layout(); //we use a dummy panel as actual background: replaces simple "Layout()" call - //-> it seems this layout is not required, and even harmful: resets m_comboBoxExecFinished dropdown while user is selecting! - } + //time elapsed + const long timeElapSec = timeElapsed.Time() / 1000; + setText(*m_staticTextTimeElapsed, + timeElapSec < 3600 ? + wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : + wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged); + + //do the ui update + if (layoutChanged) + { + // Layout(); + // bSizerItemsRem->Layout(); + // bSizer171->Layout(); + bSizerProgressStat->Layout(); // + m_panelProgress->Layout(); //both needed + //m_panelBackground->Layout(); //we use a dummy panel as actual background: replaces simple "Layout()" call + //-> it seems this layout is not required, and even harmful: resets m_comboBoxExecFinished dropdown while user is selecting! } #ifdef FFS_WIN @@ -1132,92 +972,195 @@ void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) if (allowYield) { //support for pause button - while (currentStatus == SyncStatus::PAUSE && currentProcessIsRunning()) + if (paused_) { - wxMilliSleep(UI_UPDATE_INTERVAL); - updateUiNow(); + stopTimer(); + while (paused_) + { + wxMilliSleep(UI_UPDATE_INTERVAL); + updateUiNow(); //receive UI message that ends pause + } + resumeTimer(); } - /* /|\ - | keep this order to ensure one full statistics update before entering pause mode + | keep this sequence to ensure one full progress update before entering pause mode! \|/ */ - updateUiNow(); + updateUiNow(); //receive UI message that sets pause status } else Update(); //don't wait until next idle event (who knows what blocking process comes next?) } -bool SyncStatus::SyncStatusImpl::currentProcessIsRunning() -{ - return abortCb_ != nullptr; -} - - std::wstring SyncStatus::SyncStatusImpl::getExecWhenFinishedCommand() const { return m_comboBoxExecFinished->getValue(); } -void SyncStatus::SyncStatusImpl::setCurrentStatus(SyncStatus::SyncStatusID id) +void SyncStatus::SyncStatusImpl::updateDialogStatus() //depends on "syncStat_, paused_, finalResult" { - switch (id) + m_staticTextStatus->SetLabel(getDialogStatusText(syncStat_, paused_, finalResult)); + + if (syncStat_) //sync running { - case SyncStatus::ABORTED: - m_bitmapStatus->SetBitmap(GlobalResources::getImage(wxT("statusError"))); - m_staticTextStatus->SetLabel(_("Aborted")); - break; + if (paused_) + m_buttonPause->SetLabel(_("Continue")); + else + m_buttonPause->SetLabel(_("Pause")); + + if (paused_) + m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusPause")); + else + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + break; - case SyncStatus::FINISHED_WITH_SUCCESS: - m_bitmapStatus->SetBitmap(GlobalResources::getImage(wxT("statusSuccess"))); - m_staticTextStatus->SetLabel(_("Completed")); - break; + case ProcessCallback::PHASE_SCANNING: + m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusScanning")); + break; - case SyncStatus::FINISHED_WITH_ERROR: - m_bitmapStatus->SetBitmap(GlobalResources::getImage(wxT("statusWarning"))); - m_staticTextStatus->SetLabel(_("Completed")); - break; + case ProcessCallback::PHASE_COMPARING_CONTENT: + m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusBinaryCompare")); + break; - case SyncStatus::PAUSE: - m_bitmapStatus->SetBitmap(GlobalResources::getImage(wxT("statusPause"))); - m_staticTextStatus->SetLabel(_("Paused")); - break; + case ProcessCallback::PHASE_SYNCHRONIZING: + m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusSyncing")); + break; + } + } + else //sync finished + switch (finalResult) + { + case RESULT_ABORTED: + m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusError")); + break; - case SyncStatus::SCANNING: - m_bitmapStatus->SetBitmap(GlobalResources::getImage(wxT("statusScanning"))); - m_staticTextStatus->SetLabel(_("Scanning...")); - break; + case RESULT_FINISHED_WITH_ERROR: + m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusWarning")); + break; - case SyncStatus::COMPARING_CONTENT: - m_bitmapStatus->SetBitmap(GlobalResources::getImage(wxT("statusBinaryCompare"))); - m_staticTextStatus->SetLabel(_("Comparing content...")); - break; + case RESULT_FINISHED_WITH_SUCCESS: + m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusSuccess")); + break; + } - case SyncStatus::SYNCHRONIZING: - m_bitmapStatus->SetBitmap(GlobalResources::getImage(wxT("statusSyncing"))); - m_staticTextStatus->SetLabel(_("Synchronizing...")); - break; + //show status on Windows 7 taskbar + if (taskbar_.get()) + { + if (syncStat_) //sync running + { + if (paused_) + taskbar_->setStatus(Taskbar::STATUS_PAUSED); + else + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); + break; + + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + taskbar_->setStatus(Taskbar::STATUS_NORMAL); + break; + } + } + else //sync finished + switch (finalResult) + { + case RESULT_ABORTED: + case RESULT_FINISHED_WITH_ERROR: + taskbar_->setStatus(Taskbar::STATUS_ERROR); + break; + + case RESULT_FINISHED_WITH_SUCCESS: + taskbar_->setStatus(Taskbar::STATUS_NORMAL); + break; + } } - currentStatus = id; - m_panelBackground->Layout(); //we use a dummy panel as actual background: replaces simple "Layout()" call } -void SyncStatus::SyncStatusImpl::processHasFinished(SyncStatus::SyncStatusID id, const ErrorLog& log) //essential to call this in StatusHandler derived class destructor +void SyncStatus::SyncStatusImpl::closeWindowDirectly() //this should really be called: do not call back + schedule deletion +{ + paused_ = false; //you never know? + + //ATTENTION: dialog may live a little longer, so cut callbacks! + //e.g. wxGTK calls OnIconize after wxWindow::Close() (better not ask why) and before physical destruction! => indirectly calls updateDialogStatus(), which reads syncStat_!!! + + //------- change class state ------- + abortCb_ = nullptr; //avoid callback to (maybe) deleted parent process + syncStat_ = nullptr; //set *after* last call to "updateProgress" + //---------------------------------- + + Close(); +} + + +void SyncStatus::SyncStatusImpl::processHasFinished(SyncResult resultId, const ErrorLog& log) //essential to call this in StatusHandler derived class destructor { //at the LATEST(!) to prevent access to currentStatusHandler //enable okay and close events; may be set in this method ONLY wxWindowUpdateLocker dummy(this); //badly needed - abortCb_ = nullptr; //avoid callback to (maybe) deleted parent process + paused_ = false; //you never know? - setCurrentStatus(id); + //update numbers one last time (as if sync were still running) + updateProgress(false); + + switch (syncStat_->currentPhase()) //no matter if paused or not + { + case ProcessCallback::PHASE_NONE: + case ProcessCallback::PHASE_SCANNING: + //set overall speed -> not needed + //items processed -> not needed + break; + + case ProcessCallback::PHASE_COMPARING_CONTENT: + case ProcessCallback::PHASE_SYNCHRONIZING: + { + auto objectsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase()); + auto objectsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase()); + auto dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase()); + auto dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase()); + + //set overall speed (instead of current speed) + assert(perf); + if (perf) + m_staticTextSpeed->SetLabel(perf->getOverallBytesPerSecond()); //note: we can't simply divide "sync total bytes" by "timeElapsed" + + //show new element "items processed" + m_staticTextLabelItemsProc->Show(true); + bSizerItemsProc ->Show(true); + m_staticTextProcessedObj ->SetLabel(toStringSep(objectsCurrent)); + m_staticTextDataProcessed->SetLabel(L"(" + filesizeToShortString(dataCurrent) + L")"); + + //hide remaining elements... + if (objectsCurrent == objectsTotal && //...if everything was processed successfully + dataCurrent == dataTotal) + { + m_staticTextLabelItemsRem->Show(false); + bSizerItemsRem ->Show(false); + } + } + break; + } + + //------- change class state ------- + abortCb_ = nullptr; //avoid callback to (maybe) deleted parent process + syncStat_ = nullptr; //set *after* last call to "updateProgress" + finalResult = resultId; + //---------------------------------- + + updateDialogStatus(); + setExternalStatus(getDialogStatusText(syncStat_, paused_, finalResult), wxString()); resumeFromSystray(); //if in tray mode... @@ -1244,45 +1187,30 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncStatus::SyncStatusID id, //show and prepare final statistics bSizerFinalStat->Show(true); - if (totalObjects == currentObjects && //if everything was processed successfully - totalData == currentData) - { - m_staticTextLabelItemsRem->Show(false); - bSizerItemsRem ->Show(false); - } - - m_staticTextLabelItemsProc->Show(true); - bSizerItemsProc ->Show(true); - m_staticTextProcessedObj ->SetLabel(toStringSep(currentObjects)); - m_staticTextDataProcessed->SetLabel(zen::filesizeToShortString(currentData)); - + //show total time m_staticTextLabelElapsedTime->SetLabel(_("Total time:")); //it's not "elapsed time" anymore + //hide remaining time m_staticTextLabelRemTime->Show(false); m_staticTextRemTime ->Show(false); - updateStatusDialogNow(false); //keep this sequence to avoid display distortion, if e.g. only 1 item is sync'ed - - //changed meaning: from current to overall speed: -> make sure to call after "updateStatusDialogNow" - const long timeElapMs = timeElapsed.Time(); - m_staticTextSpeed->SetLabel(timeElapMs <= 0 ? L"-" : zen::filesizeToShortString(currentData * 1000 / timeElapMs) + _("/sec")); - - //fill result listbox: - //workaround wxListBox bug on Windows XP: labels are drawn on top of each other assert(m_listbookResult->GetImageList()); //make sure listbook keeps *any* image list //due to some crazy reasons that aren't worth debugging, this needs to be done directly in wxFormBuilder, - //the following call is *not* sufficient: m_listbookResult->AssignImageList(new wxImageList(0, 0)); + //the following call is *not* sufficient: m_listbookResult->AssignImageList(new wxImageList(180, 1)); //note: alternative solutions involving wxLC_LIST, wxLC_REPORT and SetWindowStyleFlag() do not work portably! wxListBook using wxLC_ICON is obviously a class invariant! //1. re-arrange graph into results listbook bSizerTop->Detach(m_panelProgress); m_panelProgress->Reparent(m_listbookResult); +#ifdef FFS_LINUX + wxYield(); //wxGTK 2.9.3 fails miserably at "reparent" whithout this +#endif m_listbookResult->AddPage(m_panelProgress, _("Statistics"), true); //AddPage() takes ownership! //2. log file const size_t posLog = 1; - LogControl* logControl = new LogControl(m_listbookResult, log); + LogControl* logControl = new LogControl(m_listbookResult, log); //owned by m_listbookResult m_listbookResult->AddPage(logControl, _("Logging"), false); //bSizerHoldStretch->Insert(0, logControl, 1, wxEXPAND); @@ -1304,62 +1232,19 @@ void SyncStatus::SyncStatusImpl::OnOkay(wxCommandEvent& event) void SyncStatus::SyncStatusImpl::OnAbort(wxCommandEvent& event) { - if (currentStatus == SyncStatus::PAUSE) - { - wxCommandEvent dummy; - OnPause(dummy); - } - - if (currentProcessIsRunning()) - { - m_buttonAbort->Disable(); - m_buttonAbort->Hide(); - m_buttonPause->Disable(); - m_buttonPause->Hide(); - - setStatusText_NoUpdate(_("Abort requested: Waiting for current operation to finish...")); - //no Layout() or UI-update here to avoid cascaded Yield()-call + paused_ = false; + updateDialogStatus(); //update status + pause button + //no Layout() or UI-update here to avoid cascaded Yield()-call + if (abortCb_) abortCb_->requestAbortion(); - } -} - - -void SyncStatus::SyncStatusImpl::stopTimer() -{ - timeElapsed.Pause(); - if (statistics.get()) statistics->pauseTimer(); - graphDataBytes->pauseTimer(); -} - - -void SyncStatus::SyncStatusImpl::resumeTimer() -{ - timeElapsed.Resume(); - if (statistics.get()) statistics->resumeTimer(); - graphDataBytes->resumeTimer(); } void SyncStatus::SyncStatusImpl::OnPause(wxCommandEvent& event) { - if (currentStatus == SyncStatus::PAUSE) - { - resumeTimer(); - setCurrentStatus(previousStatus); - - m_buttonPause->SetLabel(_("Pause")); - m_animationControl1->Play(); - } - else - { - stopTimer(); - previousStatus = currentStatus; //save current status - setCurrentStatus(SyncStatus::PAUSE); - - m_buttonPause->SetLabel(_("Continue")); - m_animationControl1->Stop(); - } + paused_ = !paused_; + updateDialogStatus(); //update status + pause button } @@ -1367,16 +1252,19 @@ void SyncStatus::SyncStatusImpl::OnClose(wxCloseEvent& event) { //this event handler may be called due to a system shutdown DURING synchronization! //try to stop sync gracefully and cross fingers: - if (currentProcessIsRunning()) + if (abortCb_) abortCb_->requestAbortion(); - //Note: we must NOT veto dialog destruction, else we will cancel system shutdown if this dialog is application main window (like in batch mode) + //Note: we must NOT veto dialog destruction, else we will cancel system shutdown if this dialog is application main window (as in batch mode) + isZombie = true; //it "lives" until cleanup in next idle event Destroy(); } void SyncStatus::SyncStatusImpl::OnIconize(wxIconizeEvent& event) { + if (isZombie) return; //wxGTK sends iconize event *after* wxWindow::Destroy, sigh... + if (event.IsIconized()) //ATTENTION: iconize event is also triggered on "Restore"! (at least under Linux) minimizeToTray(); else @@ -1399,7 +1287,8 @@ void SyncStatus::SyncStatusImpl::minimizeToTray() //tray icon has shorter lifetime than this => no need to disconnect event later } - updateStatusDialogNow(false); //set tooltip: e.g. in pause mode there was no GUI update, so this is the last chance + if (syncStat_) + updateProgress(false); //set tray tooltip + progress: e.g. no updates while paused Hide(); if (mainDialog) @@ -1425,7 +1314,9 @@ void SyncStatus::SyncStatusImpl::resumeFromSystray() Raise(); SetFocus(); - updateStatusDialogNow(false); //restore Windows 7 task bar status (e.g. required in pause mode) + updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode) + if (syncStat_) + updateProgress(false); //restore Windows 7 task bar progress (e.g. required in pause mode) } @@ -1434,18 +1325,18 @@ void SyncStatus::SyncStatusImpl::resumeFromSystray() //redirect to implementation SyncStatus::SyncStatus(AbortCallback& abortCb, + const Statistics& syncStat, MainDialog* parentWindow, - SyncStatusID startStatus, bool showProgress, const wxString& jobName, const std::wstring& execWhenFinished, std::vector<std::wstring>& execFinishedHistory) : - pimpl(new SyncStatusImpl(abortCb, parentWindow, startStatus, jobName, execWhenFinished, execFinishedHistory)) + pimpl(new SyncStatusImpl(abortCb, syncStat, parentWindow, jobName, execWhenFinished, execFinishedHistory)) { if (showProgress) { pimpl->Show(); - pimpl->updateStatusDialogNow(false); //clear gui flicker: window must be visible to make this work! + pimpl->updateProgress(false); //clear gui flicker, remove dummy texts: window must be visible to make this work! } else pimpl->minimizeToTray(); @@ -1461,34 +1352,19 @@ wxWindow* SyncStatus::getAsWindow() return pimpl; } -void SyncStatus::initNewProcess(SyncStatusID id, int totalObjectsToProcess, Int64 totalDataToProcess) -{ - pimpl->initNewProcess(id, totalObjectsToProcess, totalDataToProcess); -} - -void SyncStatus::incScannedObjects_NoUpdate(int number) -{ - pimpl->incScannedObjects_NoUpdate(number); -} - -void SyncStatus::incProcessedData_NoUpdate(int objectsDelta, Int64 dataDelta) -{ - pimpl->incProcessedData_NoUpdate(objectsDelta, dataDelta); -} - -void SyncStatus::incTotalData_NoUpdate(int objectsDelta, Int64 dataDelta) +void SyncStatus::initNewPhase() { - pimpl->incTotalData_NoUpdate(objectsDelta, dataDelta); + pimpl->initNewPhase(); } -void SyncStatus::setStatusText_NoUpdate(const wxString& text) +void SyncStatus::reportCurrentBytes(Int64 currentData) { - pimpl->setStatusText_NoUpdate(text); + pimpl->reportCurrentBytes(currentData); } -void SyncStatus::updateStatusDialogNow() +void SyncStatus::updateProgress() { - pimpl->updateStatusDialogNow(); + pimpl->updateProgress(); } std::wstring SyncStatus::getExecWhenFinishedCommand() const @@ -1506,12 +1382,12 @@ void SyncStatus::resumeTimer() return pimpl->resumeTimer(); } -void SyncStatus::processHasFinished(SyncStatusID id, const ErrorLog& log) +void SyncStatus::processHasFinished(SyncResult resultId, const ErrorLog& log) { - pimpl->processHasFinished(id, log); + pimpl->processHasFinished(resultId, log); } void SyncStatus::closeWindowDirectly() //don't wait for user (silent mode) { - pimpl->Destroy(); + pimpl->closeWindowDirectly(); } diff --git a/ui/progress_indicator.h b/ui/progress_indicator.h index 858f6ce9..cd974408 100644 --- a/ui/progress_indicator.h +++ b/ui/progress_indicator.h @@ -18,18 +18,13 @@ class CompareStatus { public: CompareStatus(wxTopLevelWindow& parentWindow); //CompareStatus will be owned by parentWindow! - ~CompareStatus(); wxWindow* getAsWindow(); //convenience! don't abuse! - void init(); //make visible, initialize all status values - void finalize(); //hide again + void init(const zen::Statistics& syncStat); //begin of sync: make visible, set pointer to "syncStat", initialize all status values + void finalize(); //end of sync: hide again, clear pointer to "syncStat" - void switchToCompareBytewise(int totalObjectsToProcess, zen::Int64 totalDataToProcess); - void incScannedObjects_NoUpdate(int number); - void incProcessedCmpData_NoUpdate(int objectsDelta, zen::Int64 dataDelta); - void incTotalCmpData_NoUpdate (int objectsDelta, zen::Int64 dataDelta); - void setStatusText_NoUpdate(const wxString& text); + void switchToCompareBytewise(); void updateStatusPanelNow(); private: @@ -41,20 +36,9 @@ private: class SyncStatus { public: - enum SyncStatusID - { - ABORTED, - FINISHED_WITH_SUCCESS, - FINISHED_WITH_ERROR, - PAUSE, - SCANNING, - COMPARING_CONTENT, - SYNCHRONIZING - }; - - SyncStatus(AbortCallback& abortCb, + SyncStatus(zen::AbortCallback& abortCb, + const zen::Statistics& syncStat, MainDialog* parentWindow, //may be nullptr - SyncStatusID startStatus, bool showProgress, const wxString& jobName, const std::wstring& execWhenFinished, @@ -63,22 +47,25 @@ public: wxWindow* getAsWindow(); //convenience! don't abuse! - void initNewProcess(SyncStatusID id, int totalObjectsToProcess, zen::Int64 totalDataToProcess); + void initNewPhase(); //call after "StatusHandler::initNewPhase" - void incScannedObjects_NoUpdate(int number); - void incProcessedData_NoUpdate(int objectsDelta, zen::Int64 dataDelta); - void incTotalData_NoUpdate (int objectsDelta, zen::Int64 dataDelta); - void setStatusText_NoUpdate(const wxString& text); - void updateStatusDialogNow(); + void reportCurrentBytes(zen::Int64 currentData); //throw (), required by graph! + void updateProgress(); std::wstring getExecWhenFinishedCommand() const; //final value (after possible user modification) void stopTimer(); //halt all internal counters! void resumeTimer(); // + enum SyncResult + { + RESULT_ABORTED, + RESULT_FINISHED_WITH_ERROR, + RESULT_FINISHED_WITH_SUCCESS + }; //essential to call one of these two methods in StatusUpdater derived class destructor at the LATEST(!) //to prevent access to callback to updater (e.g. request abort) - void processHasFinished(SyncStatusID id, const zen::ErrorLog& log); + void processHasFinished(SyncResult resultId, const zen::ErrorLog& log); //sync finished, still dialog may live on void closeWindowDirectly(); //don't wait for user private: diff --git a/ui/search.cpp b/ui/search.cpp index af62686b..80e4aa26 100644 --- a/ui/search.cpp +++ b/ui/search.cpp @@ -17,16 +17,17 @@ using namespace zen; class SearchDlg : public SearchDialogGenerated { public: - SearchDlg(wxWindow& parentWindow, wxString& searchText, bool& respectCase); + SearchDlg(wxWindow* parent, wxString& searchText, bool& respectCase); enum ReturnCodes { - BUTTON_OKAY = 1 //mustn't be 0 + BUTTON_CANCEL, + BUTTON_OKAY }; private: - void OnClose (wxCloseEvent& event) { EndModal(0); } - void OnCancel(wxCommandEvent& event) { EndModal(0); } + void OnClose (wxCloseEvent& event) { EndModal(BUTTON_CANCEL); } + void OnCancel(wxCommandEvent& event) { EndModal(BUTTON_CANCEL); } void OnFindNext(wxCommandEvent& event); void OnText(wxCommandEvent& event); @@ -35,8 +36,8 @@ private: }; -SearchDlg::SearchDlg(wxWindow& parentWindow, wxString& searchText, bool& respectCase) : - SearchDialogGenerated(&parentWindow), +SearchDlg::SearchDlg(wxWindow* parent, wxString& searchText, bool& respectCase) : + SearchDialogGenerated(parent), searchText_(searchText), respectCase_(respectCase) { @@ -47,7 +48,6 @@ SearchDlg::SearchDlg(wxWindow& parentWindow, wxString& searchText, bool& respect m_checkBoxMatchCase->SetValue(respectCase_); m_textCtrlSearchTxt->SetValue(searchText_); - CentreOnParent(); //this requires a parent window! m_textCtrlSearchTxt->SetFocus(); } @@ -79,7 +79,7 @@ class FindInText { public: FindInText(const wxString& textToFind) : textToFind_(textToFind) {} - bool found(const wxString& phrase) const { return phrase.Find(textToFind_) != wxNOT_FOUND; } + bool found(const wxString& phrase) const { return contains(phrase, textToFind_); } private: wxString textToFind_; @@ -95,7 +95,7 @@ public: { //wxWidgets::MakeUpper() is inefficient! But performance is not THAT important for this high-level search functionality phrase.MakeUpper(); - return phrase.Find(textToFind_) != wxNOT_FOUND; + return contains(phrase, textToFind_); } private: @@ -147,7 +147,7 @@ wxString lastSearchString; //this variable really is conceptionally global... void executeSearch(bool forceShowDialog, bool& respectCase, - wxWindow& parentWindow, + wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight) { @@ -155,7 +155,7 @@ void executeSearch(bool forceShowDialog, if (forceShowDialog || lastSearchString.IsEmpty()) { - SearchDlg searchDlg(parentWindow, lastSearchString, respectCase); //wxWidgets deletion handling -> deleted by parentWindow + SearchDlg searchDlg(parent, lastSearchString, respectCase); //wxWidgets deletion handling -> deleted by parentWindow if (static_cast<SearchDlg::ReturnCodes>(searchDlg.ShowModal()) != SearchDlg::BUTTON_OKAY) return; @@ -195,24 +195,22 @@ void executeSearch(bool forceShowDialog, return; } - wxString messageNotFound = _("Cannot find %x"); - messageNotFound.Replace(wxT("%x"), wxString(wxT("\"")) + lastSearchString + wxT("\""), false); - wxMessageBox(messageNotFound, _("Find"), wxOK); + wxMessageBox(replaceCpy(_("Cannot find %x"), L"%x", L"\"" + lastSearchString + L"\"", false), _("Find"), wxOK, parent); //show search dialog again if (searchDialogWasShown) - executeSearch(true, respectCase, parentWindow, grid, compPosLeft, compPosRight); + executeSearch(true, respectCase, parent, grid, compPosLeft, compPosRight); } //########################################################################################### -void zen::startFind(wxWindow& parentWindow, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase) //Strg + F +void zen::startFind(wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase) //Strg + F { - executeSearch(true, respectCase, parentWindow, grid, compPosLeft, compPosRight); + executeSearch(true, respectCase, parent, grid, compPosLeft, compPosRight); } -void zen::findNext(wxWindow& parentWindow, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase) //F3 +void zen::findNext(wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase) //F3 { - executeSearch(false, respectCase, parentWindow, grid, compPosLeft, compPosRight); + executeSearch(false, respectCase, parent, grid, compPosLeft, compPosRight); } diff --git a/ui/search.h b/ui/search.h index 1c26ea78..6120562e 100644 --- a/ui/search.h +++ b/ui/search.h @@ -11,8 +11,8 @@ namespace zen { -void startFind(wxWindow& parentWindow, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase); //Strg + F -void findNext( wxWindow& parentWindow, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase); //F3 +void startFind(wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase); //Strg + F +void findNext( wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase); //F3 } #endif // SEARCH_H_INCLUDED diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp index 20e5da4e..c8c88de0 100644 --- a/ui/small_dlgs.cpp +++ b/ui/small_dlgs.cpp @@ -34,8 +34,8 @@ public: AboutDlg(wxWindow* parent); private: - void OnClose(wxCloseEvent& event) { EndModal(0); } - void OnOK (wxCommandEvent& event) { EndModal(0); } + void OnClose(wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnOK (wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_OKAY); } }; @@ -72,22 +72,19 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent) //build information wxString build = __TDATE__; #if wxUSE_UNICODE - build += wxT(" - Unicode"); + build += L" - Unicode"; #else - build += wxT(" - ANSI"); + build += L" - ANSI"; #endif //wxUSE_UNICODE //compile time info about 32/64-bit build if (zen::is64BitBuild) - build += wxT(" x64"); + build += L" x64"; else - build += wxT(" x86"); + build += L" x86"; assert_static(zen::is32BitBuild || zen::is64BitBuild); - wxString buildFormatted = _("(Build: %x)"); - buildFormatted.Replace(wxT("%x"), build); - - m_build->SetLabel(buildFormatted); + m_build->SetLabel(replaceCpy(_("(Build: %x)"), L"%x", build)); //m_animationControl1->SetAnimation(GlobalResources::instance().animationMoney); //m_animationControl1->Play(); @@ -101,9 +98,9 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent) } -void zen::showAboutDialog() +void zen::showAboutDialog(wxWindow* parent) { - AboutDlg aboutDlg(nullptr); + AboutDlg aboutDlg(parent); aboutDlg.ShowModal(); } //######################################################################################## @@ -118,8 +115,8 @@ public: ~FilterDlg() {} private: - void OnClose ( wxCloseEvent& event) { EndModal(0); } - void OnCancel (wxCommandEvent& event) { EndModal(0); } + void OnClose ( wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnCancel (wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } void OnHelp (wxCommandEvent& event); void OnDefault (wxCommandEvent& event); void OnApply (wxCommandEvent& event); @@ -302,9 +299,9 @@ void FilterDlg::OnApply(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showFilterDialog(bool isGlobalFilter, FilterConfig& filter) +ReturnSmallDlg::ButtonPressed zen::showFilterDialog(wxWindow* parent, bool isGlobalFilter, FilterConfig& filter) { - FilterDlg filterDlg(nullptr, + FilterDlg filterDlg(parent, isGlobalFilter, //is main filter dialog filter); return static_cast<ReturnSmallDlg::ButtonPressed>(filterDlg.ShowModal()); @@ -417,12 +414,13 @@ void DeleteDialog::OnUseRecycler(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showDeleteDialog(const std::vector<zen::FileSystemObject*>& rowsOnLeft, +ReturnSmallDlg::ButtonPressed zen::showDeleteDialog(wxWindow* parent, + const std::vector<zen::FileSystemObject*>& rowsOnLeft, const std::vector<zen::FileSystemObject*>& rowsOnRight, bool& deleteOnBothSides, bool& useRecycleBin) { - DeleteDialog confirmDeletion(nullptr, + DeleteDialog confirmDeletion(parent, rowsOnLeft, rowsOnRight, deleteOnBothSides, @@ -440,15 +438,14 @@ public: const zen::SyncStatistics& statistics, bool& dontShowAgain); private: - void OnClose(wxCloseEvent& event); - void OnCancel(wxCommandEvent& event); + void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } void OnStartSync(wxCommandEvent& event); bool& m_dontShowAgain; }; - SyncPreviewDlg::SyncPreviewDlg(wxWindow* parent, const wxString& variantName, const zen::SyncStatistics& statistics, @@ -487,18 +484,6 @@ SyncPreviewDlg::SyncPreviewDlg(wxWindow* parent, } -void SyncPreviewDlg::OnClose(wxCloseEvent& event) -{ - EndModal(ReturnSmallDlg::BUTTON_CANCEL); -} - - -void SyncPreviewDlg::OnCancel(wxCommandEvent& event) -{ - EndModal(ReturnSmallDlg::BUTTON_CANCEL); -} - - void SyncPreviewDlg::OnStartSync(wxCommandEvent& event) { m_dontShowAgain = m_checkBoxDontShowAgain->GetValue(); @@ -506,12 +491,12 @@ void SyncPreviewDlg::OnStartSync(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showSyncPreviewDlg( - const wxString& variantName, - const zen::SyncStatistics& statistics, - bool& dontShowAgain) +ReturnSmallDlg::ButtonPressed zen::showSyncPreviewDlg(wxWindow* parent, + const wxString& variantName, + const zen::SyncStatistics& statistics, + bool& dontShowAgain) { - SyncPreviewDlg preview(nullptr, + SyncPreviewDlg preview(parent, variantName, statistics, dontShowAgain); @@ -524,13 +509,12 @@ ReturnSmallDlg::ButtonPressed zen::showSyncPreviewDlg( class CompareCfgDialog : public CmpCfgDlgGenerated { public: - CompareCfgDialog(wxWindow* parent, - CompConfig& cmpConfig); + CompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig); private: void OnOkay(wxCommandEvent& event); - void OnClose(wxCloseEvent& event) { EndModal(0); } - void OnCancel(wxCommandEvent& event) { EndModal(0); } + void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } void OnShowHelp(wxCommandEvent& event); void OnTimeSize(wxCommandEvent& event) { m_radioBtnSizeDate->SetValue(true); } @@ -632,10 +616,9 @@ void CompareCfgDialog::OnShowHelp(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showCompareCfgDialog(CompConfig& cmpConfig) +ReturnSmallDlg::ButtonPressed zen::showCompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig) { - CompareCfgDialog syncDlg(nullptr, cmpConfig); - + CompareCfgDialog syncDlg(parent, cmpConfig); return static_cast<ReturnSmallDlg::ButtonPressed>(syncDlg.ShowModal()); } //######################################################################################## @@ -650,8 +633,8 @@ private: void OnOkay(wxCommandEvent& event); void OnResetDialogs(wxCommandEvent& event); void OnDefault(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); + void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } void OnAddRow(wxCommandEvent& event); void OnRemoveRow(wxCommandEvent& event); void OnResize(wxSizeEvent& event); @@ -741,7 +724,7 @@ void GlobalSettingsDlg::OnOkay(wxCommandEvent& event) void GlobalSettingsDlg::OnResetDialogs(wxCommandEvent& event) { - if (showQuestionDlg(ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_CANCEL, + if (showQuestionDlg(this, ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_CANCEL, _("Make hidden dialogs and warning messages visible again?")) == ReturnQuestionDlg::BUTTON_YES) settings.optDialogs.resetDialogs(); } @@ -758,18 +741,6 @@ void GlobalSettingsDlg::OnDefault(wxCommandEvent& event) } -void GlobalSettingsDlg::OnCancel(wxCommandEvent& event) -{ - EndModal(0); -} - - -void GlobalSettingsDlg::OnClose(wxCloseEvent& event) -{ - EndModal(0); -} - - void GlobalSettingsDlg::set(const xmlAccess::ExternalApps& extApp) { auto extAppTmp = extApp; @@ -835,9 +806,9 @@ void GlobalSettingsDlg::OnRemoveRow(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showGlobalSettingsDlg(xmlAccess::XmlGlobalSettings& globalSettings) +ReturnSmallDlg::ButtonPressed zen::showGlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings) { - GlobalSettingsDlg settingsDlg(nullptr, globalSettings); + GlobalSettingsDlg settingsDlg(parent, globalSettings); return static_cast<ReturnSmallDlg::ButtonPressed>(settingsDlg.ShowModal()); } //######################################################################################## @@ -850,8 +821,8 @@ public: private: void OnOkay(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event) { EndModal(0); } - void OnClose(wxCloseEvent& event) { EndModal(0); } + void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } virtual void OnChangeSelectionFrom(wxCalendarEvent& event) { @@ -960,8 +931,8 @@ void SelectTimespanDlg::OnOkay(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showSelectTimespanDlg(Int64& timeFrom, Int64& timeTo) +ReturnSmallDlg::ButtonPressed zen::showSelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo) { - SelectTimespanDlg timeSpanDlg(nullptr, timeFrom, timeTo); + SelectTimespanDlg timeSpanDlg(parent, timeFrom, timeTo); return static_cast<ReturnSmallDlg::ButtonPressed>(timeSpanDlg.ShowModal()); } diff --git a/ui/small_dlgs.h b/ui/small_dlgs.h index b42f56eb..98a072cf 100644 --- a/ui/small_dlgs.h +++ b/ui/small_dlgs.h @@ -13,6 +13,8 @@ namespace zen { +//parent window, optional: support correct dialog placement above parent on multiple monitor systems + struct ReturnSmallDlg { enum ButtonPressed @@ -22,27 +24,26 @@ struct ReturnSmallDlg }; }; +void showAboutDialog(wxWindow* parent); -void showAboutDialog(); - -ReturnSmallDlg::ButtonPressed showFilterDialog(bool isGlobalFilter, FilterConfig& filter); +ReturnSmallDlg::ButtonPressed showFilterDialog(wxWindow* parent, bool isGlobalFilter, FilterConfig& filter); -ReturnSmallDlg::ButtonPressed showDeleteDialog( - const std::vector<FileSystemObject*>& rowsOnLeft, - const std::vector<FileSystemObject*>& rowsOnRight, - bool& deleteOnBothSides, - bool& useRecycleBin); +ReturnSmallDlg::ButtonPressed showDeleteDialog(wxWindow* parent, + const std::vector<FileSystemObject*>& rowsOnLeft, + const std::vector<FileSystemObject*>& rowsOnRight, + bool& deleteOnBothSides, + bool& useRecycleBin); -ReturnSmallDlg::ButtonPressed showSyncPreviewDlg( - const wxString& variantName, - const SyncStatistics& statistics, - bool& dontShowAgain); +ReturnSmallDlg::ButtonPressed showSyncPreviewDlg(wxWindow* parent, + const wxString& variantName, + const SyncStatistics& statistics, + bool& dontShowAgain); -ReturnSmallDlg::ButtonPressed showCompareCfgDialog(CompConfig& cmpConfig); +ReturnSmallDlg::ButtonPressed showCompareCfgDialog(wxWindow* parent, CompConfig& cmpConfig); -ReturnSmallDlg::ButtonPressed showGlobalSettingsDlg(xmlAccess::XmlGlobalSettings& globalSettings); +ReturnSmallDlg::ButtonPressed showGlobalSettingsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); -ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(Int64& timeFrom, Int64& timeTo); +ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(wxWindow* parent, Int64& timeFrom, Int64& timeTo); } #endif // SMALLDIALOGS_H_INCLUDED diff --git a/ui/sorting.h b/ui/sorting.h index 807a9ce3..ad54bfbe 100644 --- a/ui/sorting.h +++ b/ui/sorting.h @@ -156,16 +156,19 @@ bool lessExtension(const FileSystemObject& a, const FileSystemObject& b) else if (b.isEmpty<side>()) return true; //empty rows always last - - const FileMapping* fileObjA = dynamic_cast<const FileMapping*>(&a); - const FileMapping* fileObjB = dynamic_cast<const FileMapping*>(&b); - - if (!fileObjA) + if (dynamic_cast<const DirMapping*>(&a)) return false; //directories last - else if (!fileObjB) + else if (dynamic_cast<const DirMapping*>(&b)) return true; //directories last - return makeSortDirection(LessFilename(), Int2Type<ascending>())(fileObjA->getExtension<side>(), fileObjB->getExtension<side>()); + auto getExtension = [&](const FileSystemObject& fsObj) -> Zstring + { + const Zstring& shortName = fsObj.getShortName<side>(); + const size_t pos = shortName.rfind(Zchar('.')); + return pos == Zstring::npos ? Zstring() : Zstring(shortName.c_str() + pos + 1); + }; + + return makeSortDirection(LessFilename(), Int2Type<ascending>())(getExtension(a), getExtension(b)); } diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp index 4f6df013..87e1b44d 100644 --- a/ui/sync_cfg.cpp +++ b/ui/sync_cfg.cpp @@ -26,7 +26,7 @@ using namespace xmlAccess; class SyncCfgDialog : public SyncCfgDlgGenerated { public: - SyncCfgDialog(wxWindow* window, + SyncCfgDialog(wxWindow* parent, CompareVariant compareVar, SyncConfig& syncCfg, xmlAccess::OnGuiError* handleError, // @@ -50,9 +50,9 @@ private: virtual void OnDifferent( wxCommandEvent& event); virtual void OnConflict( wxCommandEvent& event); - virtual void OnClose( wxCloseEvent& event) { EndModal(0); } - virtual void OnCancel( wxCommandEvent& event) { EndModal(0); } - virtual void OnApply( wxCommandEvent& event); + virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } + virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } + virtual void OnApply (wxCommandEvent& event); void updateGui(); @@ -194,12 +194,12 @@ void updateConfigIcons(const DirectionConfig& directionCfg, } -SyncCfgDialog::SyncCfgDialog(wxWindow* window, +SyncCfgDialog::SyncCfgDialog(wxWindow* parent, CompareVariant compareVar, SyncConfig& syncCfg, xmlAccess::OnGuiError* handleError, ExecWhenFinishedCfg* execWhenFinished) : - SyncCfgDlgGenerated(window), + SyncCfgDlgGenerated(parent), cmpVariant(compareVar), currentDirectionCfg(syncCfg.directionCfg), //make working copy syncCfgOut(syncCfg), @@ -529,12 +529,13 @@ void SyncCfgDialog::OnConflict(wxCommandEvent& event) } -ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(CompareVariant compareVar, +ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(wxWindow* parent, + CompareVariant compareVar, SyncConfig& syncCfg, xmlAccess::OnGuiError* handleError, // ExecWhenFinishedCfg* execWhenFinished) //optional input parameter { - SyncCfgDialog syncDlg(nullptr, + SyncCfgDialog syncDlg(parent, compareVar, syncCfg, handleError, diff --git a/ui/sync_cfg.h b/ui/sync_cfg.h index dae79d1d..3d5eb88c 100644 --- a/ui/sync_cfg.h +++ b/ui/sync_cfg.h @@ -7,8 +7,10 @@ #ifndef SYNCCONFIG_H_INCLUDED #define SYNCCONFIG_H_INCLUDED +#include <wx/window.h> #include "../lib/process_xml.h" + namespace zen { struct ReturnSyncConfig @@ -16,7 +18,7 @@ struct ReturnSyncConfig enum ButtonPressed { BUTTON_CANCEL, - BUTTON_OKAY = 1 + BUTTON_OKAY }; }; @@ -28,7 +30,8 @@ struct ExecWhenFinishedCfg }; -ReturnSyncConfig::ButtonPressed showSyncConfigDlg(CompareVariant compareVar, +ReturnSyncConfig::ButtonPressed showSyncConfigDlg(wxWindow* parent, + CompareVariant compareVar, SyncConfig& syncCfg, xmlAccess::OnGuiError* handleError, // ExecWhenFinishedCfg* execWhenFinished); //optional input parameter diff --git a/ui/taskbar.cpp b/ui/taskbar.cpp index 7219be14..0611d739 100644 --- a/ui/taskbar.cpp +++ b/ui/taskbar.cpp @@ -24,7 +24,7 @@ using namespace zen; using namespace tbseven; -class Taskbar::Pimpl //throw (TaskbarNotAvailable) +class Taskbar::Pimpl //throw TaskbarNotAvailable { public: Pimpl(const wxTopLevelWindow& window) : @@ -145,7 +145,7 @@ public: //######################################################################################################## Taskbar::Taskbar(const wxTopLevelWindow& window) : pimpl_(new Pimpl(window)) {} //throw TaskbarNotAvailable -Taskbar::~Taskbar() {} //std::unique_ptr ... +Taskbar::~Taskbar() {} void Taskbar::setStatus(Status status) { pimpl_->setStatus(status); } void Taskbar::setProgress(double fraction) { pimpl_->setProgress(fraction); } diff --git a/ui/taskbar.h b/ui/taskbar.h index bf40adde..048bf9e8 100644 --- a/ui/taskbar.h +++ b/ui/taskbar.h @@ -11,7 +11,7 @@ #include <wx/toplevel.h> /* -Windows 7; show progress in windows superbar via ITaskbarList3 Interface (http://msdn.microsoft.com/en-us/library/dd391692(VS.85).aspx) +Windows 7; show progress in windows superbar via ITaskbarList3 Interface: http://msdn.microsoft.com/en-us/library/dd391692(VS.85).aspx Ubuntu: use Unity interface (optional) diff --git a/ui/tray_icon.cpp b/ui/tray_icon.cpp index 51441e36..2fc76f15 100644 --- a/ui/tray_icon.cpp +++ b/ui/tray_icon.cpp @@ -185,9 +185,10 @@ private: FfsTrayIcon::FfsTrayIcon() : - trayIcon(new TaskBarImpl(*this)) + trayIcon(new TaskBarImpl(*this)), + fractionLast(1) //show FFS logo by default { - trayIcon->SetIcon(generateIcon(0), wxT("FreeFileSync")); + trayIcon->SetIcon(generateIcon(fractionLast), L"FreeFileSync"); trayIcon->Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(FfsTrayIcon::OnDoubleClick), nullptr, this); //register double-click } @@ -204,23 +205,30 @@ FfsTrayIcon::~FfsTrayIcon() } -void FfsTrayIcon::setToolTip(const wxString& toolTipText, double fraction) +void FfsTrayIcon::setToolTip(const wxString& toolTip) { - trayIcon->SetIcon(generateIcon(fraction), toolTipText); + toolTipLast = toolTip; + trayIcon->SetIcon(generateIcon(fractionLast), toolTip); //another wxWidgets design bug: non-orthogonal method! +} + + +void FfsTrayIcon::setProgress(double fraction) +{ + fractionLast = fraction; + trayIcon->SetIcon(generateIcon(fraction), toolTipLast); } void FfsTrayIcon::OnContextMenuSelection(wxCommandEvent& event) { - const Selection eventId = static_cast<Selection>(event.GetId()); - switch (eventId) + switch (static_cast<Selection>(event.GetId())) { case CONTEXT_ABOUT: { //ATTENTION: the modal dialog below does NOT disable all GUI input, e.g. user may still double-click on tray icon //which will implicitly destroy the tray icon while still showing the modal dialog trayIcon->SetEvtHandlerEnabled(false); - zen::showAboutDialog(); + zen::showAboutDialog(nullptr); trayIcon->SetEvtHandlerEnabled(true); } break; diff --git a/ui/tray_icon.h b/ui/tray_icon.h index d1522602..50480b54 100644 --- a/ui/tray_icon.h +++ b/ui/tray_icon.h @@ -19,7 +19,8 @@ public: FfsTrayIcon(); ~FfsTrayIcon(); - void setToolTip(const wxString& toolTipText, double fraction = 0); //number between [0, 1], for small progress indicator + void setToolTip(const wxString& toolTip); + void setProgress(double fraction); //number between [0, 1], for small progress indicator private: FfsTrayIcon(const FfsTrayIcon&); @@ -30,6 +31,9 @@ private: class TaskBarImpl; TaskBarImpl* trayIcon; //actual tray icon (don't use inheritance to enable delayed deletion) + + wxString toolTipLast; + double fractionLast; }; #endif // TRAYICON_H_INCLUDED diff --git a/ui/tree_view.cpp b/ui/tree_view.cpp index 49548142..b3a9595a 100644 --- a/ui/tree_view.cpp +++ b/ui/tree_view.cpp @@ -23,10 +23,10 @@ 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.subDirs.size() == 1 && // + (cont.firstFile == nullptr && //single dir node... + cont.subDirs.size() == 1 && // cont.subDirs[0].firstFile == nullptr && //...that is empty - cont.subDirs[0].subDirs.empty())) // + cont.subDirs[0].subDirs.empty())) // { cont.subDirs.clear(); cont.firstFile = nullptr; @@ -624,7 +624,7 @@ public: fileIcon(IconBuffer(IconBuffer::SIZE_SMALL).genericFileIcon()), dirIcon (IconBuffer(IconBuffer::SIZE_SMALL).genericDirIcon ()), rootBmp(GlobalResources::getImage(L"rootFolder").ConvertToImage().Scale(fileIcon.GetWidth(), fileIcon.GetHeight(), wxIMAGE_QUALITY_HIGH)), - widthNodeIcon(fileIcon.GetWidth()), + widthNodeIcon(dirIcon.GetWidth()), widthLevelStep(widthNodeIcon), widthNodeStatus(GlobalResources::getImage(L"nodeExpanded").GetWidth()), grid_(grid), diff --git a/ui/tree_view.h b/ui/tree_view.h index 3bb741e6..01c737bc 100644 --- a/ui/tree_view.h +++ b/ui/tree_view.h @@ -151,7 +151,7 @@ private: /* /|\ | (update...) | */ - std::vector<RootNodeImpl> folderCmpView; //partial view on folderCmp -> unsorted(cannot be, because of files!) + std::vector<RootNodeImpl> folderCmpView; //partial view on folderCmp -> unsorted (cannot be, because files are not a separate entity) /* /|\ | (update...) | */ |