diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:57:45 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:57:45 +0200 |
commit | 2a3ebac62eb6dd88122c0f447ea90ce368373d3a (patch) | |
tree | fae5c18deaecfb6f39d4d66dd3de8ce730b2025b | |
parent | 1.17 (diff) | |
download | FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.gz FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.bz2 FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.zip |
1.18
72 files changed, 7869 insertions, 4831 deletions
diff --git a/Application.cpp b/Application.cpp index dcd887a2..fee24b79 100644 --- a/Application.cpp +++ b/Application.cpp @@ -8,22 +8,19 @@ #include "ui/mainDialog.h" #include <wx/stdpaths.h> #include <wx/filename.h> -#include <wx/ffile.h> #include "library/globalFunctions.h" #include <wx/msgdlg.h> -#include "library/processXml.h" -#include <wx/stopwatch.h> #include "comparison.h" -#include "synchronization.h" #include "algorithm.h" -#include <wx/taskbar.h> -#include "ui/smallDialogs.h" +#include "synchronization.h" #include <memory> +#include "ui/batchStatusHandler.h" +#include "ui/checkVersion.h" +#include "library/filter.h" IMPLEMENT_APP(Application); - bool Application::OnInit() { returnValue = 0; @@ -83,7 +80,7 @@ void Application::OnStartApplication(wxIdleEvent& event) } catch (const FileError& error) { - if (wxFileExists(FreeFileSync::GLOBAL_CONFIG_FILE)) + if (wxFileExists(xmlAccess::GLOBAL_CONFIG_FILE)) { //show messagebox and quit program immediately wxMessageBox(error.show().c_str(), _("Error"), wxOK | wxICON_ERROR); return; @@ -126,7 +123,7 @@ void Application::OnStartApplication(wxIdleEvent& event) } else //start in GUI mode (standard) { - MainDialog* frame = new MainDialog(NULL, FreeFileSync::LAST_CONFIG_FILE, &programLanguage, globalSettings); + MainDialog* frame = new MainDialog(NULL, xmlAccess::LAST_CONFIG_FILE, &programLanguage, globalSettings); frame->SetIcon(*globalResource.programIcon); //set application icon frame->Show(); } @@ -178,110 +175,6 @@ int Application::OnExit() } -class LogFile -{ -public: - LogFile(const wxString& logfileDirectory) - { - wxString timeNow = wxDateTime::Now().FormatISOTime(); - timeNow.Replace(wxT(":"), wxEmptyString); - - wxString logfileName; - if (logfileDirectory.empty()) - { //create subfolder "log" to hold logfiles - if (!wxDirExists(wxT("Logs"))) - wxMkdir(wxT("Logs")); - logfileName = wxString(wxT("Logs")) + GlobalResources::FILE_NAME_SEPARATOR + wxT("FFS_") + wxDateTime::Now().FormatISODate() + wxChar('_') + timeNow + wxT(".log"); - } - else - { //use alternate logfile directory - if (!wxDirExists(logfileDirectory)) - try - { - FreeFileSync::createDirectory(logfileDirectory.c_str(), Zstring(), false); - } - catch (FileError&) - { - readyToWrite = false; - return; - } - - logfileName = logfileDirectory; - if (!endsWithPathSeparator(logfileName.c_str())) - logfileName += GlobalResources::FILE_NAME_SEPARATOR; - - logfileName += wxT("FFS_") + wxDateTime::Now().FormatISODate() + wxChar('_') + timeNow + wxT(".log"); - } - - - logFile.Open(logfileName.c_str(), wxT("w")); - readyToWrite = logFile.IsOpened(); - if (readyToWrite) - { - wxString headerLine = wxString(wxT("FreeFileSync - ")) + - _("Batch execution") + wxT(" (") + - _("Date") + wxT(": ") + wxDateTime::Now().FormatDate() + wxT(" ") + //"Date" is used at other places too - _("Time") + wxT(":") + wxT(" ") + wxDateTime::Now().FormatTime() + wxT(")"); - logFile.Write(headerLine + wxChar('\n')); - logFile.Write(wxString().Pad(headerLine.Len(), wxChar('-')) + wxChar('\n') + wxChar('\n')); - - wxString caption = _("Log-messages:"); - logFile.Write(caption + wxChar('\n')); - logFile.Write(wxString().Pad(caption.Len(), wxChar('-')) + wxChar('\n')); - - write(wxString(_("Start")) + wxChar('\n')); - - totalTime.Start(); //measure total time - } - } - - ~LogFile() - { - if (readyToWrite) - close(); - } - - bool isOkay() - { - return readyToWrite; - } - - void write(const wxString& logText, const wxString& problemType = wxEmptyString) - { - logFile.Write(wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ")); - - if (problemType != wxEmptyString) - logFile.Write(problemType + wxT(": ")); - - //remove linebreaks - wxString formattedText = logText; - for (wxString::iterator i = formattedText.begin(); i != formattedText.end(); ++i) - if (*i == wxChar('\n')) - *i = wxChar(' '); - - logFile.Write(formattedText + wxChar('\n')); - } - -private: - - void close() - { - logFile.Write(wxChar('\n')); - - long time = totalTime.Time(); //retrieve total time - - logFile.Write(wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ")); - logFile.Write(wxString(_("Stop")) + wxT(" (") + _("Total time:") + wxT(" ") + (wxTimeSpan::Milliseconds(time)).Format() + wxT(")")); - - //logFile.close(); <- not needed - } - - bool readyToWrite; - wxFFile logFile; - wxStopWatch totalTime; -}; - - void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSettings& globalSettings) { //load XML settings @@ -297,6 +190,11 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet } //all settings have been read successfully... + //regular check for program updates + if (!batchCfg.silent) + FreeFileSync::checkForUpdatePeriodically(globalSettings.shared.lastUpdateCheck); + + try //begin of synchronization process (all in one try-catch block) { //class handling status updates and error messages @@ -307,7 +205,7 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet statusHandler = std::auto_ptr<BatchStatusHandler>(new BatchStatusHandlerGui(batchCfg.handleError, returnValue)); //COMPARE DIRECTORIES - FileCompareResult currentGridData; + FreeFileSync::FolderComparison folderCmp; FreeFileSync::CompareProcess comparison(globalSettings.shared.traverseDirectorySymlinks, globalSettings.shared.fileTimeTolerance, globalSettings.shared.warningDependentFolders, @@ -315,15 +213,18 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet comparison.startCompareProcess(batchCfg.directoryPairs, batchCfg.mainCfg.compareVar, - currentGridData); + folderCmp); //APPLY FILTERS if (batchCfg.mainCfg.filterIsActive) - FreeFileSync::filterGridData(currentGridData, batchCfg.mainCfg.includeFilter, batchCfg.mainCfg.excludeFilter); + FreeFileSync::filterGridData(folderCmp, batchCfg.mainCfg.includeFilter, batchCfg.mainCfg.excludeFilter); //check if there are files/folders to be sync'ed at all - if (!synchronizationNeeded(currentGridData, batchCfg.mainCfg.syncConfiguration)) - statusHandler->exitAndSetStatus(_("Nothing to synchronize according to configuration!"), BatchStatusHandler::FINISHED); //inform about this special case + if (!synchronizationNeeded(folderCmp, batchCfg.mainCfg.syncConfiguration)) + { + statusHandler->addFinalInfo(_("Nothing to synchronize according to configuration!")); //inform about this special case + return; + } //START SYNCHRONIZATION FreeFileSync::SyncProcess synchronization( @@ -331,532 +232,13 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet globalSettings.shared.copyFileSymlinks, globalSettings.shared.traverseDirectorySymlinks, globalSettings.shared.warningSignificantDifference, + globalSettings.shared.warningNotEnoughDiskSpace, statusHandler.get()); - synchronization.startSynchronizationProcess(currentGridData, batchCfg.mainCfg.syncConfiguration); - } - catch (AbortThisProcess&) //exit used by statusHandler - { //don't set returnValue here! Program flow may arrive here in non-error situations also! E.g. "nothing to synchronize" - return; - } - - return; //exit program -} - - -//###################################################################################################### - -class FfsTrayIcon : public wxTaskBarIcon -{ -public: - FfsTrayIcon(StatusHandler* statusHandler) : - m_statusHandler(statusHandler), - processPaused(false) - { - running.reset(new wxIcon(*globalResource.programIcon)); - paused.reset(new wxIcon); - paused->CopyFromBitmap(*globalResource.bitmapFFSPaused); - - wxTaskBarIcon::SetIcon(*running); - } - - ~FfsTrayIcon() {} - - enum - { - CONTEXT_PAUSE, - CONTEXT_ABORT, - CONTEXT_ABOUT - }; - - virtual wxMenu* CreatePopupMenu() - { - wxMenu* contextMenu = new wxMenu; - contextMenu->Append(CONTEXT_PAUSE, _("&Pause"), wxEmptyString, wxITEM_CHECK); - contextMenu->Check(CONTEXT_PAUSE, processPaused); - contextMenu->Append(CONTEXT_ABORT, _("&Abort")); - contextMenu->AppendSeparator(); - contextMenu->Append(CONTEXT_ABOUT, _("&About...")); - //event handling - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FfsTrayIcon::onContextMenuSelection), NULL, this); - - return contextMenu; //ownership transferred to library - } - - void onContextMenuSelection(wxCommandEvent& event) - { - int eventId = event.GetId(); - if (eventId == CONTEXT_PAUSE) - { - processPaused = !processPaused; - if (processPaused) - wxTaskBarIcon::SetIcon(*paused); - else - wxTaskBarIcon::SetIcon(*running); - } - else if (eventId == CONTEXT_ABORT) - { - processPaused = false; - wxTaskBarIcon::SetIcon(*running); - m_statusHandler->requestAbortion(); - } - else if (eventId == CONTEXT_ABOUT) - { - AboutDlg* aboutDlg = new AboutDlg(NULL); - aboutDlg->ShowModal(); - aboutDlg->Destroy(); - } - } - - void updateSysTray() - { - updateUiNow(); - - //support for pause button - while (processPaused) - { - wxMilliSleep(UI_UPDATE_INTERVAL); - updateUiNow(); - } - } - -private: - StatusHandler* m_statusHandler; - bool processPaused; - std::auto_ptr<wxIcon> running; - std::auto_ptr<wxIcon> paused; -}; - - -BatchStatusHandlerSilent::BatchStatusHandlerSilent(const xmlAccess::OnError handleError, const wxString& logfileDirectory, int& returnVal) : - m_handleError(handleError), - currentProcess(StatusHandler::PROCESS_NONE), - returnValue(returnVal), - trayIcon(new FfsTrayIcon(this)), - m_log(new LogFile(logfileDirectory)) -{ - //test if log was instantiated successfully - if (!m_log->isOkay()) - { //handle error: file load - wxMessageBox(_("Unable to create logfile!"), _("Error"), wxOK | wxICON_ERROR); - throw AbortThisProcess(); + synchronization.startSynchronizationProcess(folderCmp, batchCfg.mainCfg.syncConfiguration); } -} - - -BatchStatusHandlerSilent::~BatchStatusHandlerSilent() -{ - unsigned int failedItems = unhandledErrors.GetCount(); - - //write result - if (abortRequested) + catch (FreeFileSync::AbortThisProcess&) //exit used by statusHandler { - returnValue = -4; - m_log->write(_("Synchronization aborted!"), _("Error")); - } - else if (failedItems) - { - returnValue = -5; - m_log->write(_("Synchronization completed with errors!"), _("Info")); - } - else - m_log->write(_("Synchronization completed successfully!"), _("Info")); -} - - -inline -void BatchStatusHandlerSilent::updateStatusText(const Zstring& text) -{ - if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING) - m_log->write(text.c_str(), _("Info")); -} - - -inline -void BatchStatusHandlerSilent::initNewProcess(int objectsTotal, double dataTotal, StatusHandler::Process processID) -{ - currentProcess = processID; -} - - -ErrorHandler::Response BatchStatusHandlerSilent::reportError(const Zstring& errorMessage) -{ - switch (m_handleError) - { - case xmlAccess::ON_ERROR_POPUP: - { - bool ignoreNextErrors = false; - ErrorDlg* errorDlg = new ErrorDlg(NULL, - ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, - wxString(errorMessage) + wxT("\n\n") + _("Ignore this error, retry or abort?"), - ignoreNextErrors); - const int rv = errorDlg->ShowModal(); - errorDlg->Destroy(); - switch (rv) - { - case ErrorDlg::BUTTON_IGNORE: - if (ignoreNextErrors) //falsify only - m_handleError = xmlAccess::ON_ERROR_IGNORE; - unhandledErrors.Add(errorMessage.c_str()); - m_log->write(errorMessage.c_str(), _("Error")); - return ErrorHandler::IGNORE_ERROR; - - case ErrorDlg::BUTTON_RETRY: - return ErrorHandler::RETRY; - - case ErrorDlg::BUTTON_ABORT: - unhandledErrors.Add(errorMessage.c_str()); - m_log->write(errorMessage.c_str(), _("Error")); - abortThisProcess(); - } - } - break; //used if last switch didn't find a match - - case xmlAccess::ON_ERROR_EXIT: //abort - unhandledErrors.Add(errorMessage.c_str()); - m_log->write(errorMessage.c_str(), _("Error")); - abortThisProcess(); - - case xmlAccess::ON_ERROR_IGNORE: - unhandledErrors.Add(errorMessage.c_str()); - m_log->write(errorMessage.c_str(), _("Error")); - return ErrorHandler::IGNORE_ERROR; - } - - assert(false); - return ErrorHandler::IGNORE_ERROR; //dummy value -} - - -void BatchStatusHandlerSilent::reportFatalError(const Zstring& errorMessage) -{ - switch (m_handleError) - { - case xmlAccess::ON_ERROR_POPUP: - { - bool dummy = false; - ErrorDlg* errorDlg = new ErrorDlg(NULL, - ErrorDlg::BUTTON_ABORT, - errorMessage.c_str(), dummy); - errorDlg->ShowModal(); - errorDlg->Destroy(); - } - break; - - case xmlAccess::ON_ERROR_EXIT: - break; - - case xmlAccess::ON_ERROR_IGNORE: - break; - } - - unhandledErrors.Add(errorMessage.c_str()); - m_log->write(errorMessage.c_str(), _("Error")); - abortThisProcess(); -} - - -void BatchStatusHandlerSilent::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) -{ - switch (m_handleError) - { - case xmlAccess::ON_ERROR_POPUP: - { - //show popup and ask user how to handle warning - bool dontWarnAgain = false; - WarningDlg* warningDlg = new WarningDlg(NULL, - WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, - warningMessage.c_str(), - dontWarnAgain); - const int rv = warningDlg->ShowModal(); - warningDlg->Destroy(); - switch (rv) - { - case WarningDlg::BUTTON_ABORT: - unhandledErrors.Add(warningMessage.c_str()); - m_log->write(warningMessage.c_str(), _("Warning")); - abortThisProcess(); - case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! - dontShowAgain = dontWarnAgain; - m_log->write(warningMessage.c_str(), _("Warning")); - return; - } - } - break; //keep it! last switch might not find match - - case xmlAccess::ON_ERROR_EXIT: //abort - unhandledErrors.Add(warningMessage.c_str()); - m_log->write(warningMessage.c_str(), _("Warning")); - abortThisProcess(); - - case xmlAccess::ON_ERROR_IGNORE: //no unhandled error situation! - m_log->write(warningMessage.c_str(), _("Warning")); return; } - - assert(false); -} - - -void BatchStatusHandlerSilent::forceUiRefresh() -{ - trayIcon->updateSysTray(); //needed by sys-tray icon only -} - - -void BatchStatusHandlerSilent::abortThisProcess() //used by sys-tray menu -{ - abortRequested = true; - throw AbortThisProcess(); -} - - -void BatchStatusHandlerSilent::exitAndSetStatus(const wxString& message, ExitCode code) //abort externally -{ - switch (code) - { - case BatchStatusHandler::ABORTED: - unhandledErrors.Add(message); - m_log->write(message, _("Error")); - abortRequested = true; - throw AbortThisProcess(); - - case BatchStatusHandler::FINISHED: - m_log->write(message, _("Info")); - throw AbortThisProcess(); - default: - assert(false); - } -} - - -//###################################################################################################### -BatchStatusHandlerGui::BatchStatusHandlerGui(const xmlAccess::OnError handleError, int& returnVal) : - m_handleError(handleError), - currentProcess(StatusHandler::PROCESS_NONE), - returnValue(returnVal) -{ - syncStatusFrame = new SyncStatus(this, NULL); - syncStatusFrame->Show(); -} - - -BatchStatusHandlerGui::~BatchStatusHandlerGui() -{ - //display result - wxString finalMessage; - - unsigned int failedItems = unhandledErrors.GetCount(); - if (failedItems) - { - finalMessage = wxString(_("Warning: Synchronization failed for %x item(s):")) + wxT("\n\n"); - finalMessage.Replace(wxT("%x"), globalFunctions::numberToWxString(failedItems), false); - - for (unsigned int j = 0; j < failedItems; ++j) - { //remove linebreaks - wxString errorMessage = unhandledErrors[j]; - for (wxString::iterator i = errorMessage.begin(); i != errorMessage.end(); ++i) - if (*i == wxChar('\n')) - *i = wxChar(' '); - - finalMessage += errorMessage + wxT("\n"); - } - finalMessage += wxT("\n"); - } - - if (!additionalStatusInfo.IsEmpty()) - finalMessage += additionalStatusInfo + wxT("\n\n"); - - //notify to syncStatusFrame that current process has ended - if (abortRequested) - { - returnValue = -4; - finalMessage += _("Synchronization aborted!"); - syncStatusFrame->setStatusText_NoUpdate(finalMessage.c_str()); - syncStatusFrame->processHasFinished(SyncStatus::ABORTED); //enable okay and close events - } - else if (failedItems) - { - returnValue = -5; - finalMessage += _("Synchronization completed with errors!"); - syncStatusFrame->setStatusText_NoUpdate(finalMessage.c_str()); - syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_ERROR); - } - else - { - finalMessage += _("Synchronization completed successfully!"); - syncStatusFrame->setStatusText_NoUpdate(finalMessage.c_str()); - syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS); - } -} - - -inline -void BatchStatusHandlerGui::updateStatusText(const Zstring& text) -{ - syncStatusFrame->setStatusText_NoUpdate(text); -} - - -void BatchStatusHandlerGui::initNewProcess(int objectsTotal, double dataTotal, StatusHandler::Process processID) -{ - currentProcess = processID; - - if (currentProcess == StatusHandler::PROCESS_SCANNING) - syncStatusFrame->setCurrentStatus(SyncStatus::SCANNING); - - else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) - { - syncStatusFrame->resetGauge(objectsTotal, dataTotal); - syncStatusFrame->setCurrentStatus(SyncStatus::COMPARING); - } - - else if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING) - { - syncStatusFrame->resetGauge(objectsTotal, dataTotal); - syncStatusFrame->setCurrentStatus(SyncStatus::SYNCHRONIZING); - } - else assert(false); -} - - -inline -void BatchStatusHandlerGui::updateProcessedData(int objectsProcessed, double dataProcessed) -{ - if (currentProcess == StatusHandler::PROCESS_SCANNING) - ; - else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) - syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); - else if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING) - syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); - else assert(false); -} - - -ErrorHandler::Response BatchStatusHandlerGui::reportError(const Zstring& errorMessage) -{ - //add current time before error message - wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + errorMessage.c_str(); - - switch (m_handleError) - { - case xmlAccess::ON_ERROR_POPUP: - { - syncStatusFrame->updateStatusDialogNow(); - - bool ignoreNextErrors = false; - ErrorDlg* errorDlg = new ErrorDlg(syncStatusFrame, - ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, - wxString(errorMessage) + wxT("\n\n") + _("Ignore this error, retry or abort?"), - ignoreNextErrors); - switch (errorDlg->ShowModal()) - { - case ErrorDlg::BUTTON_IGNORE: - if (ignoreNextErrors) //falsify only - m_handleError = xmlAccess::ON_ERROR_IGNORE; - unhandledErrors.Add(errorWithTime); - return ErrorHandler::IGNORE_ERROR; - case ErrorDlg::BUTTON_RETRY: - return ErrorHandler::RETRY; - case ErrorDlg::BUTTON_ABORT: - unhandledErrors.Add(errorWithTime); - abortThisProcess(); - } - } - break; //used IF last switch didn't find a match - - case xmlAccess::ON_ERROR_EXIT: //abort - unhandledErrors.Add(errorWithTime); - abortThisProcess(); - - case xmlAccess::ON_ERROR_IGNORE: - unhandledErrors.Add(errorWithTime); - return ErrorHandler::IGNORE_ERROR; - } - - assert(false); - return ErrorHandler::IGNORE_ERROR; //dummy value -} - - -void BatchStatusHandlerGui::reportFatalError(const Zstring& errorMessage) -{ //add current time before error message - wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + errorMessage.c_str(); - - unhandledErrors.Add(errorWithTime); - abortThisProcess(); -} - - -void BatchStatusHandlerGui::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) -{ //add current time before warning message - wxString warningWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + warningMessage.c_str(); - - switch (m_handleError) - { - case xmlAccess::ON_ERROR_POPUP: - case xmlAccess::ON_ERROR_EXIT: //show popup in this case also - { - //show popup and ask user how to handle warning - bool dontWarnAgain = false; - WarningDlg* warningDlg = new WarningDlg(NULL, - WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, - warningMessage.c_str(), - dontWarnAgain); - const int rv = warningDlg->ShowModal(); - warningDlg->Destroy(); - switch (rv) - { - case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! - dontShowAgain = dontWarnAgain; - return; - case WarningDlg::BUTTON_ABORT: - unhandledErrors.Add(warningWithTime); - abortThisProcess(); - } - } - break; //keep it! last switch might not find match - - case xmlAccess::ON_ERROR_IGNORE: //no unhandled error situation! - return; - } - - assert(false); -} - - -inline -void BatchStatusHandlerGui::forceUiRefresh() -{ - if (currentProcess == StatusHandler::PROCESS_SCANNING) - syncStatusFrame->m_gauge1->Pulse(); //expensive! So put it here! - - syncStatusFrame->updateStatusDialogNow(); -} - - -void BatchStatusHandlerGui::abortThisProcess() -{ - abortRequested = true; - throw AbortThisProcess(); //abort can be triggered by syncStatusFrame -} - - -void BatchStatusHandlerGui::exitAndSetStatus(const wxString& message, ExitCode code) //abort externally -{ - switch (code) - { - case BatchStatusHandler::ABORTED: - unhandledErrors.Add(message); - abortRequested = true; - throw AbortThisProcess(); - break; - - case BatchStatusHandler::FINISHED: - additionalStatusInfo = message; - throw AbortThisProcess(); - break; - default: - assert(false); - } } diff --git a/Application.h b/Application.h index 2cf69d9a..1a71b619 100644 --- a/Application.h +++ b/Application.h @@ -3,18 +3,15 @@ * Purpose: Defines Application Class * Author: ZenJu (zhnmju123@gmx.de) * Created: 2008-07-16 - * Copyright: ZenJu () - * License: **************************************************************/ #ifndef FREEFILESYNCAPP_H #define FREEFILESYNCAPP_H #include <wx/app.h> -#include "ui/smallDialogs.h" -#include "library/misc.h" +#include "library/localization.h" #include "library/processXml.h" -#include <memory> + class Application : public wxApp { @@ -34,84 +31,4 @@ private: xmlAccess::XmlGlobalSettings globalSettings; //settings used by GUI, batch mode or both }; - -class BatchStatusHandler : public StatusHandler -{ -public: - BatchStatusHandler() {} - virtual ~BatchStatusHandler() {} - - enum ExitCode - { - NONE, - ABORTED, - FINISHED - }; - virtual void exitAndSetStatus(const wxString& message, ExitCode code) = 0; //overwrite final status message text -}; - - -class LogFile; -class FfsTrayIcon; - -class BatchStatusHandlerSilent : public BatchStatusHandler -{ -public: - BatchStatusHandlerSilent(const xmlAccess::OnError handleError, const wxString& logfileDirectory, int& returnVal); - ~BatchStatusHandlerSilent(); - - - virtual void updateStatusText(const Zstring& text); - virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID); - virtual void updateProcessedData(int objectsProcessed, double dataProcessed) {} - virtual void forceUiRefresh(); - - virtual ErrorHandler::Response reportError(const Zstring& errorMessage); - virtual void reportFatalError(const Zstring& errorMessage); - virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); - - virtual void exitAndSetStatus(const wxString& message, ExitCode code); //abort externally - -private: - virtual void abortThisProcess(); - - xmlAccess::OnError m_handleError; - wxArrayString unhandledErrors; //list of non-resolved errors - Process currentProcess; - int& returnValue; - std::auto_ptr<FfsTrayIcon> trayIcon; - - std::auto_ptr<LogFile> m_log; -}; - - -class BatchStatusHandlerGui : public BatchStatusHandler -{ -public: - BatchStatusHandlerGui(const xmlAccess::OnError handleError, int& returnVal); - ~BatchStatusHandlerGui(); - - virtual void updateStatusText(const Zstring& text); - virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID); - virtual void updateProcessedData(int objectsProcessed, double dataProcessed); - virtual void forceUiRefresh(); - - virtual ErrorHandler::Response reportError(const Zstring& errorMessage); - virtual void reportFatalError(const Zstring& errorMessage); - virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); - - virtual void exitAndSetStatus(const wxString& message, ExitCode code); //abort externally - -private: - virtual void abortThisProcess(); - - xmlAccess::OnError m_handleError; - wxArrayString unhandledErrors; //list of non-resolved errors - Process currentProcess; - int& returnValue; - - SyncStatus* syncStatusFrame; - wxString additionalStatusInfo; //workaround to display "Nothing to synchronize..." -}; - #endif // FREEFILESYNCAPP_H diff --git a/Changelog.txt b/Changelog.txt index 1df1f927..416071e3 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,6 +1,31 @@ FreeFileSync ------------ +Changelog v1.18 +--------------- +Linux build officially released: all major problems solved! +New statistic: remaining time +New statistic: bytes per second +Automatically check for program updates every week +Finally got rid of scrollbar in middle grid for Linux build +Fixed issue with file icon display +Fixed overlapping grid cells +Alternate logfile directory configurable via GUI +Added drag & drop support for batch job assembly +Simplyfied filter usage: - <dirname> matches "<dirname>\*" as well as "<dirname>\" + - only distinct filter entries are considered +Platform dependent linebreaks in configuration *.xml files +"Significant difference check" runs at folder pair level +Sorting runs at folder pair level +New check for sufficient free disk space (considering recycle bin usage) +New optional grid column: directory +New sort by directory name +Reduced memory consumption by 10% +A lot of smaller improvements +Added Brazilian Portuguese translation +Updated translation files + + Changelog v1.17 --------------- Full support for Windows/Linux symbolic links: diff --git a/FreeFileSync.cbp b/FreeFileSync.cbp index eb3e4d40..c3b5640f 100644 --- a/FreeFileSync.cbp +++ b/FreeFileSync.cbp @@ -105,7 +105,8 @@ <Add library="libws2_32.a" /> <Add directory="C:\Programme\C++\wxWidgets\lib\gcc_lib" /> </Linker> - <Unit filename="FreeFileSync.h"> + <Unit filename="structures.cpp" /> + <Unit filename="structures.h"> <Option target="Debug" /> <Option target="Release" /> </Unit> @@ -146,6 +147,8 @@ </Unit> <Unit filename="library\fileHandling.cpp" /> <Unit filename="library\fileHandling.h" /> + <Unit filename="library\filter.cpp" /> + <Unit filename="library\filter.h" /> <Unit filename="library\globalFunctions.cpp" /> <Unit filename="library\globalFunctions.h"> <Option target="Debug" /> @@ -181,8 +184,8 @@ <Unit filename="library\gtest\unittest3.cpp"> <Option target="Unit Test" /> </Unit> - <Unit filename="library\misc.cpp" /> - <Unit filename="library\misc.h"> + <Unit filename="library\localization.cpp" /> + <Unit filename="library\localization.h"> <Option target="Debug" /> <Option target="Release" /> </Unit> @@ -201,7 +204,11 @@ <Option target="Debug" /> <Option target="Release" /> </Unit> - <Unit filename="library\sorting.h"> + <Unit filename="library\statistics.cpp"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="library\statistics.h"> <Option target="Debug" /> <Option target="Release" /> </Unit> @@ -226,6 +233,38 @@ <Option target="Debug" /> <Option target="Release" /> </Unit> + <Unit filename="ui\batchStatusHandler.cpp"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\batchStatusHandler.h"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\checkVersion.cpp"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\checkVersion.h"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\dragAndDrop.cpp"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\dragAndDrop.h"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\gridView.cpp"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\gridView.h"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> <Unit filename="ui\guiGenerated.cpp"> <Option target="Debug" /> <Option target="Release" /> @@ -234,6 +273,14 @@ <Option target="Debug" /> <Option target="Release" /> </Unit> + <Unit filename="ui\guiStatusHandler.cpp"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> + <Unit filename="ui\guiStatusHandler.h"> + <Option target="Debug" /> + <Option target="Release" /> + </Unit> <Unit filename="ui\mainDialog.cpp"> <Option target="Debug" /> <Option target="Release" /> @@ -250,19 +297,15 @@ <Option target="Debug" /> <Option target="Release" /> </Unit> - <Unit filename="ui\syncDialog.cpp"> + <Unit filename="ui\sorting.h"> <Option target="Debug" /> <Option target="Release" /> </Unit> - <Unit filename="ui\syncDialog.h"> - <Option target="Debug" /> - <Option target="Release" /> - </Unit> - <Unit filename="ui\checkVersion.cpp"> + <Unit filename="ui\syncDialog.cpp"> <Option target="Debug" /> <Option target="Release" /> </Unit> - <Unit filename="ui\checkVersion.h"> + <Unit filename="ui\syncDialog.h"> <Option target="Debug" /> <Option target="Release" /> </Unit> diff --git a/Languages/chinese_simple.lng b/Languages/chinese_simple.lng index f35875e0..af963b6e 100644 --- a/Languages/chinese_simple.lng +++ b/Languages/chinese_simple.lng @@ -1,5 +1,3 @@ - MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE - MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE Byte Byte GB @@ -28,8 +26,8 @@ 关于(&A)... &Advanced 高级(&A) -&Back -返回(&B) +&Apply +应用(&Y) &Cancel 撒消(&C) &Compare @@ -42,12 +40,16 @@ 导出文件列表(&E) &File 文件(&F) +&Global settings +全局设置(&G) &Help 帮助(&H) &Ignore 忽略(&I) &Language 切换语言(&L) +&Load +加载(&L) &Load configuration 加载配置(&L) &OK @@ -56,10 +58,10 @@ 暂停(&P) &Quit 退出(&Q) -&Resolve -解决(&R) &Retry 重试(&R) +&Save +保存(&S) &Start 开始(&S) &Synchronize... @@ -104,12 +106,12 @@ 2. 使用通配符"*"和"?". 3. Exclude files directly on main grid via context menu. 3. 直接通过上下文菜单的主要网格排除文件. -4. Keep the number of entries small for best performance. -4. 保持小型项目的数量以获得最佳的性能. << left file is newer\n << 左侧文件较新\n <Directory> <目录> +<Last session> +<最后会话> <multiple selection> <多选> <| file on left side only\n @@ -118,8 +120,6 @@ == 文件相同\n\n >> right file is newer\n >> 右侧文件较新\n -Abort -取消 Abort requested: Waiting for current operation to finish... 取消请求: 正在等待当前操作完成... Aborted @@ -138,14 +138,22 @@ As a result the files are separated into the following categories: 由该文件分为以下几类: As the name suggests, two files which share the same name are marked as equal if and only if they have the same content. This option is useful for consistency checks rather than backup operations. Therefore the file times are not taken into account at all.\n\nWith this option enabled the decision tree is smaller: 顾名思义,这两个文件共享相同的名称被标记为相同当且仅当它们具有相同的内容。\n此选项是有用的一致性检查,而不是备份行动.因此,文件时间没有被考虑到. \n\n通过此选项使决策树较小: -Assemble a batch file with the following settings. To start synchronization in batch mode simply execute this file or schedule it in your operating system's task planner. -装配一个批处理文件使用以下设置:\n开始同步批处理模式只是执行此文件或将它计划在您的操作系统的任务规划. +Assemble a batch file for automated synchronization. To start in batch mode simply pass the name of the file to the FreeFileSync executable: FreeFileSync.exe <batchfile>. This can also be scheduled in your operating system's task planner. +装配一个批处理文件自动同步.开始只是通过批处理模式的文件的名称为FreeFileSync可执行:FreeFileSync.exe <batchfile> .这也可以安排在您的操作系统的工作规划. +Batch execution +批处理执行 Batch file created successfully! 批处理文件创建成功! +Batch job +批处理作业 Big thanks for localizing FreeFileSync goes out to: 非常感谢做本地化FreeFileSync工作的以下人员: Build: 开发: +Cancel +放弃 +Check all +检查所有 Choose to hide filtered files/directories from list 从列表中选择隐藏过滤文件/文件夹 Comma separated list @@ -182,8 +190,6 @@ Configure your own synchronization rules. 配置自己的同步规则. Confirm 确认 -Consider this when setting up synchronization rules: You might want to avoid write access to these directories so that synchronization of both does not interfere. -设置同步规则时审核:您可能要避免写入权限能存取到这些目录,以便同步时两个不干预 Continue 继续 Conversion error: @@ -200,12 +206,8 @@ Copy new or updated files to right folder. 复制新的或修改过的文件到右侧文件夹 Copy to clipboard\tCTRL+C 复制到剪贴板\t使用CTRL+C键 -Copying file %x overwriting %y -正复制文件 %x 正覆盖 %y Copying file %x to %y 正复制文件 %x 到 %y -Could not retrieve file info for: -无法检索下列文件信息: Could not set working directory: 无法设置工作目录: Create a batch job @@ -218,6 +220,8 @@ Current operation: 当前操作: Custom 自定义 +Customize columns +自定义行 DECISION TREE 决策树 Data remaining: @@ -232,32 +236,32 @@ Delete files/folders existing on right side only 仅从左侧中删除文件/方件夹 Delete files\tDEL 删除文件\t使用DEL键 +Delete on both sides +删除两侧 +Delete on both sides even if the file is selected on one side only +删除两侧(仅对在一侧已选择文件) Delete: 删除: Deleting file %x 正删除文件 %x Deleting folder %x 正删除文件夹 %x -Directories are dependent: -目录已经存在: +Directories are dependent! Be careful when setting up synchronization rules: +目录有依赖性!注意设立同步的规则: Directory does not exist: 目录不存在: -Do not show graphical status and error messages but write to a logfile instead -不显示图示状态和消息但写入日志 Do not show this warning again 不再显示警告信息 Do nothing 保持不动 Do you really want to delete the following objects(s)? 你确定要删除下列项目吗? -Do you really want to move the following objects(s) to the recycle bin? -你确定要移到下列项目到回收站吗? +Do you really want to move the following objects(s) to the Recycle Bin? +你确定要移动下列项目到回收站吗? Donate with PayPal 通过PayPal捐赠 Drag && drop 拖拽 -Email: -电子邮件: Error 错误 Error changing modification time: @@ -270,14 +274,26 @@ Error deleting directory: 删除目录出错: Error deleting file: 删除文件出错: -Error moving to recycle bin: -移到回收站出错: +Error handling +错误处理 +Error loading library function: +加载库函数出错: +Error moving to Recycle Bin: +移到到回收站出错: +Error opening file: +打开文件出错: Error parsing configuration file: 分析配置文件出错: +Error reading file attributes: +读文件属性出错: Error reading file: 读取文件出错: +Error resolving symbolic link: +解决字符链接出错: Error traversing directory: 遍历目录出错: +Error writing file attributes: +写文件属性出错: Error writing file: 写入文件出错: Error: Source directory does not exist anymore: @@ -286,8 +302,18 @@ Example 例如 Exclude 例外 +Exclude temporarily +暂时除外 +Exclude via filter: +通过过滤器排除: +Exit immediately and set returncode < 0 +立即退出,并传回 +Exit with RC < 0 +带参数退出 Feedback and suggestions are welcome at: 欢迎在下面提出反馈意见和建议: +File Manager integration: +文件管理集成器: File already exists. Overwrite? 文件已经存在.覆盖? File content @@ -296,6 +322,8 @@ File list exported! 文件清单已经列出! File size and date 文件大小和日期 +File times that differ by up to the specified number of seconds are still handled as having same time. +文件相差最多指定秒数但仍然具有相同的处理时间. Filename 文件名 Files are found equal if\n - file content\nis the same. @@ -320,14 +348,14 @@ Files/folders that exist on left side only 仅在左侧存在的档案/文件夹 Files/folders that exist on right side only 仅在左侧存在的档案/文件夹 -Filter -过滤 Filter active: Press again to deactivate 过滤激活: 请按键以关闭激活 Filter files 过滤文件 Filter view 过滤查看 +Folder Comparison and Synchronization +文件夹比较与同步 Folder pair 文件夹对 FreeFileSync - Folder Comparison and Synchronization @@ -340,8 +368,16 @@ FreeFileSync batch file FreeFileSync 批处理文件 FreeFileSync configuration FreeFileSync 配置 +Full name +全称 +Generating file list... +生成文件列表... +Global settings +全局设置 Help 帮助 +Hide all error and warning messages +隐藏所有错误与警告信息 Hide files that are different 隐藏不同的档案 Hide files that are equal @@ -362,46 +398,44 @@ Hides error messages during synchronization:\nThey are collected and shown as a 隐藏同步时的错误信息:在结束进程时收集和显示的清单 Hints: 提示: -Homepage: -网站主页: -If you like FFS -个人捐助 Ignore errors 忽略错误 -Ignore next errors -忽略下一个错误 +Ignore subsequent errors +忽略随后的错误 Ignore this error, retry or abort synchronization? 忽略这个错误,重试或取消同步? Ignore this error, retry or abort? 忽略这个错误,重试或取消? Include 包括 +Include temporarily +包括暂时的 Include: *.doc;*.zip;*.exe\nExclude: *\\temp\\* 包括:*.doc;*.zip;*.exe 除外:*\\temp\\* Info 信息 Information 信息 -Information: If you ignore the error or abort a re-compare will be necessary! -信息:如果你忽略或取消这个错误则有必要作再次比较! Initialization of Recycle Bin failed! 初始化回收站失败! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) 初始化回收站是不大可能了!估计你使用的不是Windows系统.如果你想未来在此系统上应用请联系作者. :) -Left folder: -左侧方件夹: Legend 联想 -Load configuration via...\n - this list (press DEL to delete items)\n - drag & drop to this window\n - startup parameter -加载配置(通过)...\n - 这个列表(用DEL键删除项目)\n - 拖拽到这个窗口\n - 启动参数 -Load from file... -从文件加载... +Load configuration from file +从文件加载配置 +Load configuration history (press DEL to delete items) +加载配置历史记录(按DEL键删除项目) Log-messages: 日志信息: Mirror ->> 镜像 ->> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. 左侧文件夹镜像备份: 同步后右侧文件夹将被覆盖(完全匹配左边的文件夹). +Move column down +移动下一行 +Move column up +移动上一行 Not all items were synchronized! Have a look at the list. 不是所有项目被同步!看一下列表 Nothing to synchronize according to configuration! @@ -412,10 +446,14 @@ Number of files and directories that will be deleted 一些文件和目录将被删除 Number of files that will be overwritten 一些文件和目录将被覆盖 +OK +确定 Only files/directories that pass filtering will be selected for synchronization. The filter will be applied to the full name including path prefix. 只有过滤的文件/目录将选定做同步.该过滤器将被应用到全称包括路径前缀. Open synchronization dialog 打开同步对话框 +Open with File Manager\tD-Click +打开文件管理 Operation aborted! 操作已取消! Operation: @@ -436,10 +474,14 @@ Relative path 相对路径 Remove folder pair 删除文件夹对 +Reset +重置 +Reset all warning messages? +重置所有警告信息? +Resets all warning messages +重置所有警告信息 Result 结果 -Right folder: -右侧文件夹: S&ave configuration 保存配置(&A) Save aborted! @@ -466,10 +508,16 @@ Show files that exist on left side only 显示仅存在左侧的文件 Show files that exist on right side only 显示仅存在右侧的文件 +Show popup +查看弹出 +Show popup on errors or warnings +查看弹出的错误或警告 Silent mode 静默模式 Size 大小 +Sorting file list... +排序文件列表... Source code written completely in C++ utilizing: 使用C++编写的源代码已完全写好: Start @@ -486,6 +534,8 @@ Synchronization completed successfully! 同步成功完成! Synchronization completed with errors! 同步已完成但有错误. +Synchronization filter +同步过滤 Synchronization settings 同步设置 Synchronization status @@ -498,16 +548,18 @@ Synchronizing... 同步中... System out of memory! 系统内在溢出! +Target file already existing! +目标文件已经存在! The file does not contain a valid configuration: 该文件不包含有效的配置: -The selected file does not exist: -选定的文件不存在: -This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Notice that the file time is allowed to deviate by up to 2 seconds. This ensures synchronization with the lower-precision file system FAT32 works correctly. -这两个同样变异评估命名文件时,在同等条件下,\n他们有同样的文件大小相同的最后收件日期和时间.\n请注意,该文件的时间允许偏差的2秒钟.\n这可确保同步较低文件系统FAT32的精密正常工作. +This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. +这一指令将被执行,每次你双击一个文件名。 %name作为预留位置选定的文件。 +This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. +这两个同样变异评估命名文件时,在同等条件下,\n他们有同样的文件大小相同的最后收件日期和时间. +Time +时间 Time elapsed: 已用时间: -Time: -时间: Total amount of data that will be transferred 将被转移的总数据 Total time: @@ -518,6 +570,8 @@ Unable to create logfile! 无法创建日志! Unable to initialize Recycle Bin! 无法初始化回收站! +Uncheck all +全部取消选中 Update -> 升级 -> Update: @@ -530,8 +584,10 @@ Warning 警告 Warning: Synchronization failed for %x item(s): 警告:同步失败 %x (某些)项目: -When \"Compare\" is triggered with this option set the following decision tree is processed: -当"比较" 被触发则按照决定树这个办法规定了做以下处理: +Warnings: +警告: +When the comparison is started with this option set the following decision tree is processed: +当比较是开始使用此选项设置以下处理决定树: You may try to synchronize remaining items again (WITHOUT having to re-compare)! 您可能会尝试再次同步,其余的项目(而不必再比较)! different diff --git a/Languages/dutch.lng b/Languages/dutch.lng index b85ea511..1a35e1f3 100644 --- a/Languages/dutch.lng +++ b/Languages/dutch.lng @@ -10,14 +10,24 @@ PB TB TB + day(s) + dag(en) + hour(s) + u(u)r(en) kB kB + min + min + sec + sec != files are different\n != Bestanden zijn verschillend\n %x directories %x paden %x files, %x bestanden, +%x is not a valid FreeFileSync batch file! +%x is geen geldig FreeFileSync batch-bestand! %x of %y rows in view %x van de %y rijen in zicht %x of 1 row in view @@ -32,6 +42,8 @@ &Toepassen &Cancel &Annuleren +&Check for new version +&Controleer op nieuwe versie &Compare &Vergelijken &Create batch job @@ -94,6 +106,8 @@ -Open-Source bestandssynchronisatie- . , +/sec +/sec 1 directory 1 pad 1 file, @@ -108,8 +122,8 @@ 2. U kunt gebruik maken van wildcard karakters zoals '*' en '?'. 3. Exclude files directly on main grid via context menu. 3. Sluit bestanden direct uit in het hoofscherm via een contextmenu -4. Keep the number of entries small for best performance. -4. Houd het aantal klein voor de beste prestaties +4. Keep the number of (different) entries small for best performance. +4. Hou het aantal (verschillende) posten klein voor de beste prestaties. << left file is newer\n << linker bestand is nieuwer\n <Directory> @@ -124,8 +138,8 @@ == bestanden zijn gelijk\n\n >> right file is newer\n >> rechter bestand is nieuwer\n -Abort -Afbreken +A newer version of FreeFileSync is available: +Een nieuwe versie van FreeFileSync is beschikbaar: Abort requested: Waiting for current operation to finish... Bezig met afbreken: wachten op beëindigen huidige handeling... Aborted @@ -212,10 +226,10 @@ Copy new or updated files to right folder. Kopieer nieuwe of geupdate bestanden naar de rechter map. Copy to clipboard\tCTRL+C Kopieer naar het klembord\tCTRL+C -Copying file %x overwriting %y -Bestand %y wordt overschreven door een kopie van %x Copying file %x to %y Bestand %x wordt gekopieerd naar %y +Copying file %x to %y overwriting target +Aan het overschrijven van bestand %x naar %y Could not set working directory: Kan het pad in gebruik niet instellen: Create a batch job @@ -256,10 +270,12 @@ Deleting folder %x Map %x wordt verwijderd Directories are dependent! Be careful when setting up synchronization rules: Mappen zijn afhankelijk van elkaar! Wees voorzichtig met het maken van synchronisatieregels: +Directory +Map Directory does not exist: Map bestaat niet: -Do not show graphical status and error messages but write to a logfile instead -Geef geen grafische status en foutmeldingen weer, maar sla het op in een logbestand +Do not display visual status information but write to a logfile instead +In plaats van visuele statusinformatie weergeven, alles naar een logbestand schrijven Do not show this warning again Deze waarschuwing niet meer tonen Do nothing @@ -268,12 +284,18 @@ Do you really want to delete the following objects(s)? Weet u zeker dat u de volgende bestanden wilt verwijderen? Do you really want to move the following objects(s) to the Recycle Bin? Weet u zeker dat u de/het volgende bestand(en) wilt verplaatsen naar de prullenbak? +Do you want FreeFileSync to automatically check for updates every week? +Wilt u FreeFileSync automatisch elke week laten controleren of er een nieuwe versie is? Donate with PayPal Doneer met PayPal +Download now? +Nu downloaden? Drag && drop Drag en drop -Email: -Email: +Email +E-mail +Enable filter to exclude files from synchronization +Filter gebruiken om bestanden uit te sluiten van synchronisatie Error Fout Error changing modification time: @@ -302,6 +324,8 @@ Error reading file: Er is een fout opgetreden bij het lezen van het bestand: Error resolving symbolic link: Er is een fout opgetreden bij het ophalen van een symbolische koppeling: +Error retrieving full path: +Er is een fout opgetreden bij het achterhalen van de volledige bestandslocatie: Error traversing directory: Er is een fout opgetreden bij het doorzoeken van map: Error writing file attributes: @@ -326,12 +350,14 @@ Feedback and suggestions are welcome at: Tips en suggesties zijn welkom op: File Manager integration: Integratie bestandsbeheer: -File Time Tolerance: -Bestandstijd-tolerantie +File Time tolerance (seconds): +Bestandtijd tolerantie (seconden): File already exists. Overwrite? Het bestand bestaat al. Overschrijven? File content Bestandsinhoud +File does not exist: +Het bestand bestaat niet: File list exported! Bestandslijst geëxporteerd! File size and date @@ -362,6 +388,8 @@ Files/folders that exist on left side only Bestanden/mappen die alleen aan de linkerkant bestaan Files/folders that exist on right side only Bestanden/mappen die alleen aan de rechterkant bestaan +Filter +Filter Filter active: Press again to deactivate Filter actief: nogmaals klikken om uit te zetten Filter files @@ -382,6 +410,8 @@ FreeFileSync batch file FreeFileSync batchbestand FreeFileSync configuration FreeFileSync configuratie +FreeFileSync is up to date! +U gebruikt de nieuwste versie van FreeFileSync! Full name Volledige naam Generating file list... @@ -412,8 +442,8 @@ Hides error messages during synchronization:\nThey are collected and shown as a Verbergt foutmeldingen tijdens het synchroniseren:\nze worden verzameld en op het eind in een lijst getoond Hints: Tips: -Homepage: -Homepage: +Homepage +Homepage If you like FFS Als het programma u bevalt Ignore errors @@ -438,8 +468,8 @@ Initialization of Recycle Bin failed! Initialiseren van de prullenbak is mislukt. It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Het was niet mogelijk de prullenbak te initialiseren!\n\nHet is waarschijnlijk dat u niet Windows gebruikt.\nAls u deze optie wel wilt, neem dan alstublieft contact op met de auteur. :) -Left folder: -Linker map: +Left: +Links: Legend Legenda Load configuration from file @@ -448,16 +478,22 @@ Load configuration history (press DEL to delete items) Laad configuratiegeschiedenis (druk op DEL om items te verwijderen) Log-messages: Logberichten: +Logging +Loggen Mirror ->> Spiegelen ->> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. Spiegel backup van de linkerkant: de rechterkant wordt overschreven en komt na synchronisatie exact overeen met de linkerkant. +More than 50% of the total number of files will be copied or deleted! +Meer dan 50% van alle bestanden zal worden gekopieerd of verwijderd! Move column down Verplaats kolom naar beneden Move column up Verplaats kolom naar boven Not all items were synchronized! Have a look at the list. Niet alle bestanden zijn gesynchroniseerd! Bekijk de lijst. +Not enough free disk space available in: +Niet genoeg schijfruimte beschikbaar op: Nothing to synchronize according to configuration! Volgens de gebruikte configuratie hoeft er niks gesynchroniseerd te worden! Number of files and directories that will be created @@ -478,8 +514,12 @@ Operation aborted! Operatie afgebroken! Operation: Voortgang: +Overview +Overzicht Pause Pause +Paused +Gepauseerd Please fill all empty directory fields. Vul alstublieft aan beide kanten een pad in. Press button to activate filter @@ -502,8 +542,8 @@ Resets all warning messages Reset alle waarschuwingen Result Resultaat -Right folder: -Rechter map: +Right: +Rechts: S&ave configuration S&la de instellingen op Save aborted! @@ -516,6 +556,8 @@ Scanning: Scannen: Select a folder Selecteer een map +Select logfile directory: +Selecteer een map voor het logbestand: Select variant: Selecteer een variant: Show files that are different @@ -534,8 +576,8 @@ Show popup Pop-up weergeven Show popup on errors or warnings Pop-up weergeven bij foutmeldingen of waarschuwingen -Significant difference detected: More than 50% of files will be overwritten/deleted! -Significant verschil gedetecteerd: meer dan 50% van de bestanden zal worden overschreven/verwijderd! +Significant difference detected: +Significant verschil gedetecteerd: Silent mode Stille modus Size @@ -544,6 +586,8 @@ Sorting file list... Bestandslijst sorteren... Source code written completely in C++ utilizing: Broncode compleet geschreven in C++ met behulp van: +Speed: +Snelheid: Start Start Start synchronization @@ -576,24 +620,26 @@ Target file already existing! Doelbestand bestaat al! The file does not contain a valid configuration: Het bestand bevat geen geldige configuratie: -File does not exist: -Het bestand bestaat niet: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Deze opdrachtregel wordt bij elke dubbelklik op een bestandsnaam uitgevoerd. %name doet dienst als plaatshouder voor het betreffende bestand. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Deze opdrachtregel zal elke keer dat u dubbelklikt op een bestandsnaam, worden uitgevoerd. %x doet dienst als opslagplaats voor het geselecteerde bestand. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Deze variant ziet twee gelijknamige bestanden als gelijk wanneer ze dezelfde bestandsgrootte EN tijdstempel hebben. Time Tijd Time elapsed: Verstreken tijd: +Time remaining: +Benodigde tijd: Total amount of data that will be transferred Hoeveelheid data die verplaatst word +Total required free disk space: +Benodigde hoeveelheid vrije schijfruimte: Total time: Totale tijd: Two way <-> Beide zijden <-> +Unable to connect to sourceforge.net! +Niet in staat verbinding te maken met sourceforge.net! Unable to create logfile! Niet mogelijk om een logbestand aan te maken! Unable to initialize Recycle Bin! diff --git a/Languages/french.lng b/Languages/french.lng index fbfa0095..f970f508 100644 --- a/Languages/french.lng +++ b/Languages/french.lng @@ -10,14 +10,24 @@ Po TB To + day(s) + jour(s) + hour(s) + heure(s) kB ko + min + min + sec + sec != files are different\n != les fichiers sont différents\n %x directories %x dossiers %x files, %x fichiers, +%x is not a valid FreeFileSync batch file! +%x n'est pas un fichier batch FreeFileSync valide! %x of %y rows in view %x sur %y lignes affichées %x of 1 row in view @@ -32,6 +42,8 @@ &Appliquer &Cancel &Annuler +&Check for new version +&Rechercher une nouvelle version &Compare &Comparer &Create batch job @@ -94,6 +106,8 @@ -Synchronisation de fichiers Open-Source- . , +/sec +/sec 1 directory 1 dossier 1 file, @@ -107,9 +121,9 @@ 2. Use wildcard characters '*' and '?'. 2. Les caractères génériques '*' et '?' sont acceptés. 3. Exclude files directly on main grid via context menu. -3. Excluez les fichiers directement sur le tableau principal à l'aide du menu contextuel. -4. Keep the number of entries small for best performance. -4. Réduisez le nombre d'entrées pour améliorer les performances. +3. Exclure les fichiers directement sur le tableau principal à l'aide du menu contextuel. +4. Keep the number of (different) entries small for best performance. +4. Réduire le nombre d'entrées (différentes) pour une meilleure performance << left file is newer\n << le fichier de gauche est plus récent\n <Directory> @@ -124,8 +138,8 @@ == Les fichiers sont identiques\n\n >> right file is newer\n >> le fichier de droite est plus récent\n -Abort -Abandon +A newer version of FreeFileSync is available: +Une version de FreeFileSync plus récente est disponible: Abort requested: Waiting for current operation to finish... Abandon demandé: En attente de la fin de l'opération en cours... Aborted @@ -212,10 +226,10 @@ Copy new or updated files to right folder. Copie de fichiers nouveaux ou modifiés dans le dossier de droite. Copy to clipboard\tCTRL+C Copier dans le presse-papiers\tCTRL+C -Copying file %x overwriting %y -Copie fichier %x écrasant %y Copying file %x to %y -Copie fichier %x vers %y +Copie le fichier %x vers %y +Copying file %x to %y overwriting target +Copie le fichier %x vers %y avec écrasement Could not set working directory: Impossible de définir le répertoire de travail: Create a batch job @@ -256,10 +270,10 @@ Deleting folder %x Suppression du dossier %x Directories are dependent! Be careful when setting up synchronization rules: Les dossiers sont imbriqués! Attention à la mise à jour des règles de synchronisation: +Directory +Répertoire Directory does not exist: Le répertoire n'existe pas: -Do not show graphical status and error messages but write to a logfile instead -Ne pas afficher l'état graphique ni les messages mais les écrire sur un fichier log Do not show this warning again Ne plus afficher cet avertissement Do nothing @@ -268,12 +282,18 @@ Do you really want to delete the following objects(s)? Voulez-vous vraiment supprimer les objets suivants ? Do you really want to move the following objects(s) to the Recycle Bin? Voulez-vous vraiment déplacer les objets suivants dans la corbeille? +Do you want FreeFileSync to automatically check for updates every week? +Voulez-vous que FreeFileSync recherche automatiquement de nouvelles versions chaque semaine ? Donate with PayPal Faites un don avec PayPal +Download now? +Télécharger maintenant ? Drag && drop Glisser && Déposer -Email: -Email: +Email +Email +Enable filter to exclude files from synchronization +Activer le filtrage pour exclure les fichiers de la synchronisation Error Erreur Error changing modification time: @@ -302,6 +322,8 @@ Error reading file: Erreur lors de la lecture du fichier: Error resolving symbolic link: Erreur lors de la résolution du lien symbolique: +Error retrieving full path: +Erreur lors de la recherche du chemin: Error traversing directory: Erreur lors du parcours du répertoire: Error writing file attributes: @@ -326,12 +348,14 @@ Feedback and suggestions are welcome at: Commentaires et suggestions sont les bienvenus à: File Manager integration: Choix du Gestionnaire de Fichiers: -File Time Tolerance: -Tolérance horaire: +File Time tolerance (seconds): +Tolérance sur l'heure de création du fichier (en secondes): File already exists. Overwrite? Le fichier existe déjà. Voulez-vous le remplacer? File content Contenu du fichier +File does not exist: +Le fichier n'existe pas: File list exported! Liste des fichiers exportée! File size and date @@ -362,6 +386,8 @@ Files/folders that exist on left side only Fichiers/répertoires existants seulement à gauche Files/folders that exist on right side only Fichiers/répertoires existants seulement à droite +Filter +Filtrage Filter active: Press again to deactivate Filtrage actif: Cliquez de nouveau pour le désactiver Filter files @@ -382,6 +408,8 @@ FreeFileSync batch file FreeFileSync fichier de commandes FreeFileSync configuration FreeFileSync configuration +FreeFileSync is up to date! +FreeFileSync a été mis à jour ! Full name Nom complet Generating file list... @@ -412,8 +440,8 @@ Hides error messages during synchronization:\nThey are collected and shown as a Masquer les messages d'erreur pendant la synchronisation:\nIls sont collationnés et listés à la fin de l'opération Hints: Conseils: -Homepage: -Page d'accueil: +Homepage +Accueil If you like FFS Si vous aimez FFS Ignore errors @@ -438,8 +466,8 @@ Initialization of Recycle Bin failed! Erreur lors de l'initialisation de la corbeille ! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Impossible d'accéder à la corbeille!\n\nIl est probable que vous n'utilisez pas Windows.\nSi vous désirez utilisee cette fonctionnalité, veuillez contacter l'auteur. :) -Left folder: -Répertoire de gauche: +Left: +Gauche: Legend Legende Load configuration from file @@ -448,16 +476,22 @@ Load configuration history (press DEL to delete items) Charger l'historique des configuration (appuyez sur Suppr pour supprimer des éléments) Log-messages: Messages log: +Logging +Connexion Mirror ->> Mirroir ->> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. Sauvegarde miroir du répertoire de gauche : Le répertoire de droite sera écrasé et exactement identique au répertoire de gauche après la synchronisation. +More than 50% of the total number of files will be copied or deleted! +Plus de 50% des fichiers seront copiés ou détruits! Move column down Déplacer la colonne vers le bas Move column up Déplacer la colonne vers le haut Not all items were synchronized! Have a look at the list. Tous les éléments n'ont pas été synchronisés! Veuillez vérifier la liste. +Not enough free disk space available in: +Espace disque insuffisant sur : Nothing to synchronize according to configuration! Rien à synchroniser dans cette configuration! Number of files and directories that will be created @@ -478,8 +512,12 @@ Operation aborted! Opération abandonnée! Operation: Opération: +Overview +Présentation Pause Pause +Paused +En pause Please fill all empty directory fields. Veuillez remplir tous les champs vides du répertoire. Press button to activate filter @@ -502,8 +540,8 @@ Resets all warning messages Réinitialise tous les avertissements Result Situation -Right folder: -Répertoire de droite: +Right: +Droite : S&ave configuration S&auvegarder la configuration Save aborted! @@ -516,6 +554,8 @@ Scanning: Lecture en cours: Select a folder Choisissez un répertoire +Select logfile directory: +Choisissez un dossier pour le fichier .log Select variant: Choisissez une variante: Show files that are different @@ -534,8 +574,8 @@ Show popup Affich Show popup on errors or warnings Affiche une boîte de dialogue pour chaque erreur ou avertissement -Significant difference detected: More than 50% of files will be overwritten/deleted! -Différence significative détectée: Plus de 50% des fichiers seront écrasés ou détruits! +Significant difference detected: +Différence significative détectée : Silent mode Mode silencieux Size @@ -544,12 +584,14 @@ Sorting file list... Tri de la liste des fichiers... Source code written completely in C++ utilizing: Code source écrit totalement en C++ et utilisant: +Speed: +Vitesse : Start Démarrer Start synchronization Démarrer la synchronisation Stop -Stop +Arrêt Swap sides Permuter les côtés Synchronization aborted! @@ -576,24 +618,26 @@ Target file already existing! Le fichier de destination existe déjà! The file does not contain a valid configuration: Le fichier ne contient pas de configuration valide -File does not exist: -Le fichier n'existe pas: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Cette ligne de commandes sera exécutée chaque fois que vous double-cliquerez sur un fichier. %name sert d'emplacement pour le fichier sélectionné. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Cette commande sera exécutée à chaque fois que vous double-cliquez sur un nom de fichier. %x est un espace réservé pour le fichier sélectionné. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Cette variante définit comme identiques deux fichiers de même nom lorsqu'ils ont la même taille et le même date et heure de modification. Time Heure Time elapsed: Temps écoulé: +Time remaining: +Temps restant : Total amount of data that will be transferred Volume de données à transférer +Total required free disk space: +Espace disque nécessaire : Total time: Temps total: Two way <-> Des 2 côtés <-> +Unable to connect to sourceforge.net! +Impossible de se connecter à sourceforge.net! Unable to create logfile! Impossible de créer un fichier log! Unable to initialize Recycle Bin! diff --git a/Languages/german.lng b/Languages/german.lng index 2dea1857..caa12ac1 100644 --- a/Languages/german.lng +++ b/Languages/german.lng @@ -1,6 +1,8 @@ MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI Framework\n wxFormBuilder\t- wxWidgets GUI-Builder\n CodeBlocks \t- Open-Source IDE - Byte +Browse +Suchen + Byte Byte GB GB @@ -10,14 +12,24 @@ PB TB TB + day(s) + Tag(e) + hour(s) + Stunde(n) kB kB + min + Min. + sec + Sek. != files are different\n != Dateien sind verschieden\n %x directories %x Verzeichnisse %x files, %x Dateien, +%x is not a valid FreeFileSync batch file! +%x ist keine gültige FreeFileSync-Batchdatei! %x of %y rows in view %x von %y Zeilen in Ansicht %x of 1 row in view @@ -96,6 +108,8 @@ Konfiguration &laden -Open-Source Datei-Synchronisation- . , +/sec +/s 1 directory 1 Verzeichnis 1 file, @@ -110,8 +124,8 @@ Konfiguration &laden 2. Die Platzhalter '*' und '?' werden unterstützt. 3. Exclude files directly on main grid via context menu. 3. Dateien können direkt über das Kontextmenü im Hauptfenster ausgeschlossen werden. -4. Keep the number of entries small for best performance. -4. Für beste Performance möglichst wenige Einträge filtern. +4. Keep the number of (different) entries small for best performance. +4. Für beste Performance möglichst wenige (verschiedene) Einträge filtern. << left file is newer\n << Linke Datei ist neuer\n <Directory> @@ -126,10 +140,8 @@ Konfiguration &laden == Dateien sind gleich\n\n >> right file is newer\n >> Rechte Datei ist neuer\n -A newer version is available: -Eine neuere Version steht zum Download bereit: -Abort -Abbrechen +A newer version of FreeFileSync is available: +Eine neuere Version von FreeFileSync ist verfügbar: Abort requested: Waiting for current operation to finish... Abbruch initiiert: Warte, bis aktuelle Operation beendet ist... Aborted @@ -216,10 +228,10 @@ Copy new or updated files to right folder. Neue oder aktualisierte Dateien vom linken in das rechte Verzeichnis kopieren. Copy to clipboard\tCTRL+C In die Zwischenablage kopieren\tCTRL+C -Copying file %x overwriting %y -Kopiere Datei %x und überschreibe %y Copying file %x to %y Kopiere Datei %x nach %y +Copying file %x to %y overwriting target +Kopiere Datei %x nach %y und überschreibe Ziel Could not set working directory: Das Arbeitsverzeichnis konnte nicht gesetzt werden: Create a batch job @@ -260,10 +272,12 @@ Deleting folder %x Lösche Verzeichnis %x Directories are dependent! Be careful when setting up synchronization rules: Die Verzeichnisse sind voneinander abhängig! Achtung beim Festlegen der Synchronisationseinstellungen: +Directory +Verzeichnis Directory does not exist: Das Verzeichnis existiert nicht: -Do not show graphical status and error messages but write to a logfile instead -Keine graphischen Status- und Fehlermeldungen anzeigen, sondern eine Protokolldatei erstellen +Do not display visual status information but write to a logfile instead +Keine graphischen Statusinformationen anzeigen, sondern eine Logdatei schreiben Do not show this warning again Diese Warnung nicht mehr anzeigen Do nothing @@ -272,6 +286,8 @@ Do you really want to delete the following objects(s)? Sollen folgende Elemente wirklich gelöscht werden? Do you really want to move the following objects(s) to the Recycle Bin? Sollen folgende Elemente wirklich in den Papierkorb verschoben werden? +Do you want FreeFileSync to automatically check for updates every week? +Soll FreeFileSync automatisch jede Woche nach Aktualisierungen suchen? Donate with PayPal Mit PayPal spenden Download now? @@ -280,6 +296,8 @@ Drag && drop Drag && Drop Email Email +Enable filter to exclude files from synchronization +Aktiviere Filter, um Dateien von der Synchronisation auszuschließen Error Fehler Error changing modification time: @@ -334,8 +352,8 @@ Feedback and suggestions are welcome at: Feedback und Vorschläge sind willkommen unter: File Manager integration: Einbinden des Dateimanagers: -File Time Tolerance: -Abweichung der Dateizeit: +File Time tolerance (seconds): +Abweichung der Dateizeit (Sekunden): File already exists. Overwrite? Die Datei existiert bereits. Überschreiben? File content @@ -372,6 +390,8 @@ Files/folders that exist on left side only Nur links exisitierende Dateien/Verzeichnisse Files/folders that exist on right side only Nur rechts exisitierende Dateien/Verzeichnisse +Filter +Filter Filter active: Press again to deactivate Filter aktiviert: Zum Deaktivieren erneut anwählen Filter files @@ -450,8 +470,8 @@ Initialization of Recycle Bin failed! Die Initialisierung des Papierkorbs ist fehlgeschlagen! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Die Papierkorbfunktion steht nicht zur Verfügung!\n\nWahrscheinlich benutzen Sie nicht Microsoft Windows.\nWenn Sie diese Funktion wirklich benötigen, kontaktieren Sie bitte den Autor. :) -Left folder: -Linkes Verzeichnis: +Left: +Links: Legend Legende Load configuration from file @@ -460,16 +480,22 @@ Load configuration history (press DEL to delete items) Lade Konfigurationshistorie (DEL-Taste löscht Einträge) Log-messages: Protokollmeldungen: +Logging +Protokoll Mirror ->> Spiegeln ->> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. Spiegelkopie des linken Verzeichnisses erstellen: Das rechte Verzeichnis wird dabei überschrieben und nach der Synchronisation dem linken exakt entsprechen. +More than 50% of the total number of files will be copied or deleted! +Mehr als 50% aller Dateien werden kopiert oder gelöscht! Move column down Spalte nach unten verschieben Move column up Spalte nach oben verschieben Not all items were synchronized! Have a look at the list. Nicht alle Elemente wurden synchronisiert! Siehe verbliebene Elemente im Hauptfenster. +Not enough free disk space available in: +Nicht genügend freier Speicher verfügbar unter: Nothing to synchronize according to configuration! Nichts zu synchronisieren gemäß den aktuellen Einstellungen! Number of files and directories that will be created @@ -490,10 +516,14 @@ Operation aborted! Vorgang abgebrochen! Operation: Vorgang: +Overview +Übersicht Pause Pause +Paused +Angehalten Please fill all empty directory fields. -Bitte alle leeren Verzeichnisfelder füllen. +Bitte die leeren Verzeichnisfelder füllen. Press button to activate filter Taste drücken, um Filter zu aktivieren Preview @@ -514,8 +544,8 @@ Resets all warning messages Setzt alle Warnmeldungen zurück Result Ergebnis -Right folder: -Rechtes Verzeichnis: +Right: +Rechts: S&ave configuration Konfiguration s&peichern Save aborted! @@ -528,6 +558,8 @@ Scanning: Suche Dateien: Select a folder Verzeichnis auswählen +Select logfile directory: +Verzeichnis für Logdatei wählen: Select variant: Variante auswählen: Show files that are different @@ -546,8 +578,8 @@ Show popup Popup zeigen Show popup on errors or warnings Zeigt ein Auswahlfenster bei Fehlern oder Warnungen an -Significant difference detected: More than 50% of files will be overwritten/deleted! -Ein erheblicher Unterschied wurde festgestellt: Mehr als 50% der Dateien werden überschrieben/gelöscht! +Significant difference detected: +Ein erheblicher Unterschied wurde festgestellt: Silent mode Stiller Modus Size @@ -556,6 +588,8 @@ Sorting file list... Sortiere Dateiliste... Source code written completely in C++ utilizing: Sourcecode komplett in C++ geschrieben mit Hilfe von: +Speed: +Geschwindigkeit: Start Start Start synchronization @@ -596,8 +630,12 @@ Time Uhrzeit Time elapsed: Vergangene Zeit: +Time remaining: +Verbliebene Zeit: Total amount of data that will be transferred Gesamtmenge der zu übertragenden Daten +Total required free disk space: +Insgesamt benötigter freier Speicherplatz: Total time: Gesamtzeit: Two way <-> diff --git a/Languages/hungarian.lng b/Languages/hungarian.lng index 7c43b257..78f11de6 100644 --- a/Languages/hungarian.lng +++ b/Languages/hungarian.lng @@ -33,7 +33,7 @@ &Cancel &Mégsem &Compare -&Összehasonlít +Öss&zehasonlítás &Create batch job &Kötegelt feladat létrehozása &Default @@ -108,8 +108,6 @@ 2. A csillag ('*') és a kérdőjel ('?') helyettesítő karakterek megengedettek. 3. Exclude files directly on main grid via context menu. 3. Fájlok közvetlen kizárása a fő listából helyi menü segítségével. -4. Keep the number of entries small for best performance. -4. A bejegyzések számának minimalizálása a jobb teljesítmény érdekében. << left file is newer\n << a bal oldali fájl újabb\n <Directory> @@ -124,8 +122,6 @@ == a fájlok egyformák\n\n >> right file is newer\n >> a jobb oldali fájl újabb\n -Abort -Megszakítás Abort requested: Waiting for current operation to finish... Megszakítási kérelem: Várakozás a folyamatban lévő művelet befejezésére... Aborted @@ -212,8 +208,6 @@ Copy new or updated files to right folder. Új vagy frissített fájlok másolása a jobb oldali könyvtárba. Copy to clipboard\tCTRL+C Másolás a vágólapra\tCTRL+C -Copying file %x overwriting %y -%x fájl másolása felülírva a(z) %y fájlt Copying file %x to %y %x fájl másolása a(z) %y fájlba Could not set working directory: @@ -258,8 +252,6 @@ Directories are dependent! Be careful when setting up synchronization rules: A könyvtárak függenek egymástól! Legyen óvatos, amikor megadja a szinkronizálási szabályokat: Directory does not exist: A könyvtár nem létezik: -Do not show graphical status and error messages but write to a logfile instead -Grafikusan ne jelenítse meg az állapotot és hibaüzeneteket, helyette írja a naplófájlba Do not show this warning again Ennek a figyelmeztetésnek az elrejtése legközelebb Do nothing @@ -269,11 +261,9 @@ Valóban törölni akarja a az alábbi objektumo(ka)t? Do you really want to move the following objects(s) to the Recycle Bin? Valóban a Lomtárba (Recycle Bin) akarja mozgatni az alábbi objektumo(ka)t? Donate with PayPal -Támogasd a PayPal segítségével +Ha szereted a FreeFileSync-et, támogasd a PayPal segítségével. Drag && drop Húzd && Ejtsd -Email: -E-mail: Error Hiba Error changing modification time: @@ -326,12 +316,12 @@ Feedback and suggestions are welcome at: A visszajelzéseket és javaslatokat ide várjuk: File Manager integration: Beépülés a Fájlkezelőbe: -File Time Tolerance: -Fájl dátumának toleranciája: File already exists. Overwrite? A fájl már létezik. Felülírjuk? File content fájl tartalma alapján +File does not exist: +A következő fájl nem létezik: File list exported! A fájllista exportálása befejeződött! File size and date @@ -412,10 +402,8 @@ Hides error messages during synchronization:\nThey are collected and shown as a Hibaüzenetek elrejtése szinkronizálás közben:\nÖssze lesznek gyűjtve és a folyamat végén meg lesznek jelenítve egy listában Hints: Tippek: -Homepage: -Honlap: If you like FFS -Ha szereted az FFS-t +FFS támogatása Ignore errors Hibák figyelmen kívül hagyása Ignore subsequent errors @@ -438,8 +426,6 @@ Initialization of Recycle Bin failed! A Lomtár (Recycle Bin) inicializálása sikertelen! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Lehetetlen a Lomtár (Recycle Bin) inicializálása!\n\nValószínűleg azért, mert nem Windost használ.\nHa szeretné ezt a funkciót használni, kérjük, lépjen kapcsolatba a szerzővel. :) -Left folder: -Bal oldali könyvtár: Legend Előzmény Load configuration from file @@ -502,8 +488,6 @@ Resets all warning messages Az összes figyelmeztető üzenet helyreállítása Result Eredmény -Right folder: -Jobb oldali könyvtár: S&ave configuration Beállítások mentés&e Save aborted! @@ -534,8 +518,6 @@ Show popup Felbukkanó ablak mutatása Show popup on errors or warnings Értesítés felbukkanó ablakban a hibákról és figyelmeztetésekről -Significant difference detected: More than 50% of files will be overwritten/deleted! -Nagymennyiségű változások felismerve: Több mint 50%-a a fájloknak felülírva/törölve! Silent mode Csendes mód Size @@ -576,12 +558,8 @@ Target file already existing! A célként megadott fájl már létezik! The file does not contain a valid configuration: A következő fájl nem tartalmaz érvényes beállításokat: -File does not exist: -A következő fájl nem létezik: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Ez a parancssor lesz végrehajtva minden fájlnéven történő dupla kattintás esetében. %name helyettesíti a kiválasztott fájlt. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Ez a parancssor lesz végrehajtva minden fájlnéven történő dupla kattintás esetében. %x helyettesíti a kiválasztott fájlt. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Ez a változat akkor tekint egyformának két azonos nevű fájlt, ha azok mérete ÉS az utolsó módosításuk ideje azonos. Time diff --git a/Languages/italian.lng b/Languages/italian.lng index c0535e6e..6ebfab73 100644 --- a/Languages/italian.lng +++ b/Languages/italian.lng @@ -108,8 +108,6 @@ 2. Sono ammessi i caratteri generici '*' e '?'. 3. Exclude files directly on main grid via context menu. 3. Escludi i file direttamente sul main grid tramite il menu contestuale. -4. Keep the number of entries small for best performance. -4. Riduci il numero di ingressi per migliorare le performances. << left file is newer\n << il file di sinistra è più recente\n <Directory> @@ -124,8 +122,6 @@ == i file sono identici\n\n >> right file is newer\n >> il file di destra è più recente\n -Abort -Abbandona Abort requested: Waiting for current operation to finish... Abbandono richiesto: in attesa della fine dell'operazione in corso... Aborted @@ -212,8 +208,6 @@ Copy new or updated files to right folder. Copia file nuovi o aggiornati nella cartella di destra. Copy to clipboard\tCTRL+C Copia nella clipboard\tCTRL+C -Copying file %x overwriting %y -Copia di %x file sovrascrivendone %y Copying file %x to %y Copia di file da %x a %y Could not set working directory: @@ -258,8 +252,6 @@ Directories are dependent! Be careful when setting up synchronization rules: Le directory sono dipendenti! Fai attenzione quando configuri le regole di sincronizzazione: Directory does not exist: La directory non esiste: -Do not show graphical status and error messages but write to a logfile instead -Non mostrare lo stato grafico nè i messaggi d'errore ma scrivi un file log Do not show this warning again Non mostrare più questo messaggio Do nothing @@ -272,8 +264,6 @@ Donate with PayPal Fai una donazione con PayPal Drag && drop Drag && drop -Email: -Email: Error Errore Error changing modification time: @@ -326,12 +316,12 @@ Feedback and suggestions are welcome at: Commenti e suggerimenti sono i benvenuti: File Manager integration: Integrazione File Manager: -File Time Tolerance: -Tolleranza di File Time: File already exists. Overwrite? Il file esiste già. Lo vuoi sovrascrivere? File content Contenuto del file +File does not exist: +Il file non esiste: File list exported! Lista dei file esportata! File size and date @@ -412,8 +402,6 @@ Hides error messages during synchronization:\nThey are collected and shown as a Non mostrare i messaggi d'errore durante la sincronizzazione:\nVerranno raccolti e mostrati alla fine del processo Hints: Consigli: -Homepage: -Homepage: If you like FFS Se ti piace FFS Ignore errors @@ -438,8 +426,6 @@ Initialization of Recycle Bin failed! Inizializzazione del Cestino fallita! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Impossibile inizializzare il Cestino!\n\nE'probabile che non si stia utilizzando Windows.\nSe si vuole usare questa funzionalità, contattare l'autore. :) -Left folder: -Cartella di sinistra: Legend Legenda Load configuration from file @@ -502,8 +488,6 @@ Resets all warning messages Resetta tutti gli avvisi Result Risultato -Right folder: -Cartella Destra: S&ave configuration S&alva la configurazione Save aborted! @@ -534,8 +518,6 @@ Show popup Mostra popup Show popup on errors or warnings Mostra popup degli errori o avvisi -Significant difference detected: More than 50% of files will be overwritten/deleted! -Riscontrate differenze significative: Più del 50% dei file saranno sovrascritti/cancellati! Silent mode Modalità Silenziosa Size @@ -576,12 +558,8 @@ Target file already existing! File destinazione già esistente! The file does not contain a valid configuration: Il file non contiene una configurazione valida -File does not exist: -Il file non esiste: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Questa riga di comando verrà eseguita ad ogni doppio click sul nome di un file. %name é lo spazio riservato per il file selezionato. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Questa riga di comando verrà eseguita ad ogni doppio click sul nome di un file. %x é lo spazio riservato per il file selezionato. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Questa variante definisce identici due file con lo stesso nome quando hanno la stessa dimensione E la stessa data e ora. Time diff --git a/Languages/japanese.lng b/Languages/japanese.lng index 6dba0ba1..5f80339f 100644 --- a/Languages/japanese.lng +++ b/Languages/japanese.lng @@ -10,14 +10,24 @@ PB TB TB + day(s) + 日(間) + hour(s) + 時(間) kB kB + min + 分 + sec + 秒 != files are different\n -!= ファイルは異なっています\n +!= ファイルに差異あり\n %x directories %x ディレクトリ %x files, %x 個のファイル: +%x is not a valid FreeFileSync batch file! +%x は有効なバッチファイルではありません! %x of %y rows in view %x / %y 行を表示 %x of 1 row in view @@ -32,6 +42,8 @@ 適用(&A) &Cancel キャンセル(&C) +&Check for new version +バージョン更新の確認(&C) &Compare 比較(&C) &Create batch job @@ -94,6 +106,8 @@ -Open-Source ファイル同期ツール- . , +/sec +/秒 1 directory 1 ディレクトリ 1 file, @@ -108,8 +122,8 @@ 2. ワイルドカードに ' * ' と ' ? ' を使用出来ます。 3. Exclude files directly on main grid via context menu. 3. コンテキストメニューから直接ファイルを除外出来ます。 -4. Keep the number of entries small for best performance. -4. 保持エントリ数を小さくすることで、最適な性能を発揮します。 +4. Keep the number of (different) entries small for best performance. +4. 保持エントリ数を小さい値にすることで、より最適な性能を発揮します。 << left file is newer\n << 左側の方が新しい\n <Directory> @@ -124,8 +138,8 @@ == 同じ内容のファイル\n\n >> right file is newer\n >> 右側の方が新しい\n -Abort -中断 +A newer version of FreeFileSync is available: +FreeFileSync の新しいバージョンが利用可能です: Abort requested: Waiting for current operation to finish... ユーザによる中断: 現在の処理を終了しています.. お待ちください... Aborted @@ -212,10 +226,10 @@ Copy new or updated files to right folder. 新しい(更新)ファイルを右フォルダにコピー Copy to clipboard\tCTRL+C クリップボードにコピー\tCTRL+C -Copying file %x overwriting %y -ファイル %x をコピー、%y に上書き中 Copying file %x to %y ファイル %x を %y にコピー中 +Copying file %x to %y overwriting target +ファイル %x をコピー、%y に上書き中 Could not set working directory: 作業ディレクトリが設定できません: Create a batch job @@ -256,10 +270,12 @@ Deleting folder %x フォルダ %x を削除中 Directories are dependent! Be careful when setting up synchronization rules: ディレクトリが依存関係にあります! 同期規則の設定時には注意してください: +Directory +ディレクトリ Directory does not exist: ディレクトリが存在しません: -Do not show graphical status and error messages but write to a logfile instead -進捗状況、及びエラーを表示しないで、代わりにログファイルに書き込む +Do not display visual status information but write to a logfile instead +進捗状況などのメッセージを表示しないで、ログファイルの書き込みは実施する。 Do not show this warning again 次回からこの警告を表示しない Do nothing @@ -268,12 +284,18 @@ Do you really want to delete the following objects(s)? 本当に以下のオブジェクトを削除しますか? Do you really want to move the following objects(s) to the Recycle Bin? 本当に以下のオブジェクト(複)をゴミ箱に移動しますか? +Do you want FreeFileSync to automatically check for updates every week? +FreeFileSync のアップデートの有無の確認を自動的に毎週行いますか> Donate with PayPal PayPal から寄付する +Download now? +ダウンロードしますか? Drag && drop ドラッグ && ドロップ -Email: -E-メール: +Email +E-メール +Enable filter to exclude files from synchronization +同期処理の際、除外ファイルフィルターを有効にする。 Error エラー Error changing modification time: @@ -302,6 +324,8 @@ Error reading file: ファイル読み込みエラー: Error resolving symbolic link: シンボリックリンクの解決に失敗: +Error retrieving full path: +フルパスの取得に失敗: Error traversing directory: ディレクトリの移動エラー: Error writing file attributes: @@ -326,12 +350,14 @@ Feedback and suggestions are welcome at: フィードバック、提案など: File Manager integration: ファイラとの統合: -File Time Tolerance: -ファイル時刻の許容範囲: +File Time tolerance (seconds): +ファイル時間の許容範囲 (秒): File already exists. Overwrite? ファイルは存在します、上書きしますか? File content ファイルの内容 +File does not exist: +ファイルが存在しません: File list exported! ファイル一覧のエクスポートが完了! File size and date @@ -362,6 +388,8 @@ Files/folders that exist on left side only 左側のみに存在するファイル/フォルダ Files/folders that exist on right side only 右側のみに存在するファイル/フォルダ +Filter +フィルター Filter active: Press again to deactivate フィルター有効化: 再度押すと無効化 Filter files @@ -382,6 +410,8 @@ FreeFileSync batch file FreeFileSync バッチファイル FreeFileSync configuration FreeFileSync 構成設定 +FreeFileSync is up to date! +FreeFileSync は最新です! Full name 完全な名前 Generating file list... @@ -412,8 +442,8 @@ Hides error messages during synchronization:\nThey are collected and shown as a 同期処理中のエラーメッセージを非表示にする:\n非表示にしたメッセージは、処理の終了後に一覧表示されます。 Hints: ヒント: -Homepage: -ホームページ: +Homepage +ホームページ If you like FFS FFS が気に入った場合 Ignore errors @@ -438,8 +468,8 @@ Initialization of Recycle Bin failed! ゴミ箱の初期化に失敗しました! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) ゴミ箱を初期化することが出来ませんでした!\n\n使用している OS が、Windows 以外の OS です。\nこの機能を使用したい場合は、作者までご連絡ください :) -Left folder: -左側フォルダ: +Left: +左側: Legend 履歴 Load configuration from file @@ -448,16 +478,22 @@ Load configuration history (press DEL to delete items) 構成設定履歴の読み込み(DELキーでアイテムを削除) Log-messages: ログメッセージ: +Logging +ログ Mirror ->> ミラー >> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. 左側フォルダをミラーリングバックアップ: 同期完了後は、左側フォルダに合わせて右側フォルダは上書きされます。 +More than 50% of the total number of files will be copied or deleted! +ファイル合計総数の 50% 以上が削除、またはコピーされます Move column down 列を下に移動 Move column up 列を上に移動 Not all items were synchronized! Have a look at the list. すべてのアイテムは同期されていません! 詳細はリストをご覧ください。 +Not enough free disk space available in: +利用可能なディスク空き容量が足りません: Nothing to synchronize according to configuration! 構成設定に対応する同期がみつかりません! Number of files and directories that will be created @@ -478,8 +514,12 @@ Operation aborted! 操作の中断! Operation: 操作: +Overview +概要 Pause 一時停止 +Paused +一時停止中 Please fill all empty directory fields. アイテムが選択されていません! Press button to activate filter @@ -502,8 +542,8 @@ Resets all warning messages すべての警告をリセット Result 結果 -Right folder: -右側フォルダ: +Right: +右側: S&ave configuration 構成設定を保存(&A) Save aborted! @@ -516,6 +556,8 @@ Scanning: スキャン: Select a folder フォルダを選択 +Select logfile directory: +ログファイルの保存先を選択: Select variant: 変数を選択: Show files that are different @@ -534,8 +576,8 @@ Show popup ポップアップ表示 Show popup on errors or warnings エラー/警告をポップアップ表示 -Significant difference detected: More than 50% of files will be overwritten/deleted! -大幅な差異の検出: 50%以上のファイルが上書き、または削除されています! +Significant difference detected: +重大な差異が検出されました: Silent mode サイレントモード Size @@ -544,6 +586,8 @@ Sorting file list... 一覧のソート中... Source code written completely in C++ utilizing: ソースコードは C++ で書かれ、コンパイルされています: +Speed: +速度: Start 開始 Start synchronization @@ -576,24 +620,26 @@ Target file already existing! 対象ファイルは既に存在します! The file does not contain a valid configuration: このファイルには有効な構成が含まれていません: -File does not exist: -ファイルが存在しません: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. ファイル名をダブルクリックする度に、このコマンドが実行されます。%name は選択ファイルのプレースフォルダとして機能します。 -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -ファイル名をダブルクリックする度に、このコマンドが実行されます。%x は選択ファイルのプレースフォルダとして機能します。 This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. この変数では、ふたつの同名ファイルが存在した場合、 それぞれのファイルサイズと最終更新日付/時間を比較します。 Time 時間 Time elapsed: 経過時間: +Time remaining: +残り時間: Total amount of data that will be transferred 転送されたデータの総量 +Total required free disk space: +必要な合計ディスク空き容量: Total time: 合計時間: Two way <-> 両方向 <-> +Unable to connect to sourceforge.net! +Sourceforge.net に接続できません! Unable to create logfile! ログファイルを作成出来ません! Unable to initialize Recycle Bin! diff --git a/Languages/polish.lng b/Languages/polish.lng index a0abffb3..c46cf4b0 100644 --- a/Languages/polish.lng +++ b/Languages/polish.lng @@ -1,4 +1,4 @@ - MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE + MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE Byte Bajt @@ -10,14 +10,24 @@ PB TB TB + day(s) + dni + hour(s) + godziny kB kB + min + minuty + sec + sekundy != files are different\n != pliki są różne\n %x directories %x katalogi %x files, %x pliki, +%x is not a valid FreeFileSync batch file! +%x nie jest poprawnym plikiem batch programu FreeFileSync! %x of %y rows in view %x z %y wierszy w widoku %x of 1 row in view @@ -32,6 +42,8 @@ &Zastosuj &Cancel &Anuluj +&Check for new version +&Sprawdź aktualizacje &Compare &Porównaj &Create batch job @@ -43,7 +55,7 @@ &File &Plik &Global settings -&Ustawienia globalne +&Ustawienia programu &Help &Pomoc &Ignore @@ -94,6 +106,8 @@ -synchronizacja plików Open-Source- . , +/sec +/sekundę 1 directory 1 katalog 1 file, @@ -107,9 +121,9 @@ 2. Use wildcard characters '*' and '?'. 2. Użyj wieloznacznika (wildcard) '*' i '?'. 3. Exclude files directly on main grid via context menu. -3. Wyklucz pliki i foldery urzywając prawego przycisku myszki. -4. Keep the number of entries small for best performance. -4. Przechowuj małą listę wpisów dla lepszej wydajności. +3. Wyklucz pliki i foldery używając prawego przycisku myszki. +4. Keep the number of (different) entries small for best performance. +4. Przechowuj małą liczbę wpisów (różnych) dla lepszej wydajności. << left file is newer\n << lewy plik jest nowszy\n <Directory> @@ -124,8 +138,8 @@ == pliki są równe\n\n >> right file is newer\n >> prawy plik jest nowszy\n -Abort -Przerwij +A newer version of FreeFileSync is available: +Dostępna jest nowa wersja FreeFileSync: Abort requested: Waiting for current operation to finish... Żądanie przerwania: Czekaj na koniec aktualnie wykonywanego zadania... Aborted @@ -153,7 +167,7 @@ Plik Batch utworzony pomyślnie! Batch job Zadanie batch Big thanks for localizing FreeFileSync goes out to: -Podziękowania za tłumaczenia FreeFileSync: +Podziękowania za tłumaczenie FreeFileSync: Build: Buduj: Cancel @@ -212,10 +226,10 @@ Copy new or updated files to right folder. Kopiuj nowe lub aktualniejsze pliki na prawą stronę. Copy to clipboard\tCTRL+C Kopiuj do pamięci\tCTRL+C -Copying file %x overwriting %y -Kopiowanie pliku %x nadpisywanie %y Copying file %x to %y Kopiowanie pliku %x do %y +Copying file %x to %y overwriting target +Nadpisywanie pliku %y plikiem %x Could not set working directory: Nie można ustawić poprawnego folderu: Create a batch job @@ -223,7 +237,7 @@ Twórz zadanie Batch Create: Utwórz: Creating folder %x -Tworzenie foldery %x +Tworzenie folderu %x Current operation: Aktualna operacja: Custom @@ -256,10 +270,12 @@ Deleting folder %x Usuwanie folderu %x Directories are dependent! Be careful when setting up synchronization rules: Katalogi są zależne! Pamiętaj o tym podczas ustawiania zasad synchronizacji: +Directory +Katalog Directory does not exist: Katalog nie istnieje: -Do not show graphical status and error messages but write to a logfile instead -Nie pokazuj statusu graficznego oraz informacji o błędach ale zapisz je w logach +Do not display visual status information but write to a logfile instead +Nie wyświetla informacji o statusie, ale tworzy plik z logami Do not show this warning again Nie pokazuj tego ostrzeżenia ponownie Do nothing @@ -268,12 +284,18 @@ Do you really want to delete the following objects(s)? Czy na pewno chcesz usunąć wybrane obiekty? Do you really want to move the following objects(s) to the Recycle Bin? Czy na pewno chcesz przenieść wybrane pliki do kosza? +Do you want FreeFileSync to automatically check for updates every week? +Czy chcesz aby FreeFileSync sprawdzał uaktualnienia co tydzień? Donate with PayPal Wesprzyj z PayPal +Download now? +Pobrać teraz? Drag && drop Drag && Drop -Email: -Email: +Email +Poczta +Enable filter to exclude files from synchronization +Włącz filtr aby wykluczyć pliki z procesu synchronizacji Error Błąd Error changing modification time: @@ -302,6 +324,8 @@ Error reading file: Błąd odczytu pliku: Error resolving symbolic link: Błąd odczytu dowiązania symbolicznego: +Error retrieving full path: +Błąd odzyskiwania pełnej ścieżki: Error traversing directory: Błąd podczas odczytywania katalogu: Error writing file attributes: @@ -326,12 +350,14 @@ Feedback and suggestions are welcome at: Komentarze i sugestie mile widziane na: File Manager integration: Menadżer plików: -File Time Tolerance: -Tolerancja czasu: +File Time tolerance (seconds): +Tolerancja czasu plików (sekundy): File already exists. Overwrite? Nadpisać istniejący już plik? File content Zawartość pliku +File does not exist: +Plik nie istnieje: File list exported! Lista plików wyeksportowana! File size and date @@ -362,6 +388,8 @@ Files/folders that exist on left side only Pliki/katalogi istniejące tylko po lewej stronie Files/folders that exist on right side only Pliki/katalogi istniejące tylko po prawej stronie +Filter +Filtr Filter active: Press again to deactivate Filtr aktywny: Kliknij ponownie aby wyłączyć Filter files @@ -382,12 +410,14 @@ FreeFileSync batch file FreeFileSync plik batch FreeFileSync configuration Konfiguracja FreeFileSync +FreeFileSync is up to date! +FreeFileSync jest już uaktualniony! Full name Pełna nazwa Generating file list... Generowanie listy plików... Global settings -Ustawienia globalne +Ustawienia programu Help Pomoc Hide all error and warning messages @@ -412,8 +442,8 @@ Hides error messages during synchronization:\nThey are collected and shown as a Ukryj informacje o błędach podczas synchronizacji:\nZostaną wyświetlone pod koniec procesu Hints: Wskazówki: -Homepage: -Strona domowa: +Homepage +Strona domowa If you like FFS Jeżeli Ci się podoba Ignore errors @@ -438,8 +468,8 @@ Initialization of Recycle Bin failed! Niepowodzenie inicjalizacji Kosza! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Inicjalizacja Kosza nie była możliwa!\n\nPrawdopodobnie nie używasz Windowsa.\nJeżeli chcesz dołączyć tą cechę, skontaktuj się z autorem. :) -Left folder: -Lewy folder: +Left: +Lewy: Legend Legenda Load configuration from file @@ -448,16 +478,22 @@ Load configuration history (press DEL to delete items) Wczytaj historie konfiguracji (naciśnij Del aby usunąć element) Log-messages: Logi: +Logging +Tworzenie logów Mirror ->> Lustrzana ->> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. Kopia lustrzana lewego folderu: Prawy folder będzie nadpisany zawartością lewego folderu. +More than 50% of the total number of files will be copied or deleted! +Ponad 50% plików zostanie skopiowanych lub usuniętych! Move column down Przesuń kolumnę w dół Move column up Przesuń kolumnę do góry Not all items were synchronized! Have a look at the list. Nie wszystkie elementy zostały zsynchronizowane! Lista pominiętych plików. +Not enough free disk space available in: +Brak wystarczającej przestrzeni dyskowej na: Nothing to synchronize according to configuration! Brak elementów do synchronizacji! Number of files and directories that will be created @@ -478,8 +514,12 @@ Operation aborted! Operacja przerwana! Operation: Operacja: +Overview +Przegląd Pause Pauza +Paused +Pauza Please fill all empty directory fields. Podaj foldery do synchronizacji. Press button to activate filter @@ -502,8 +542,8 @@ Resets all warning messages Zresetuj wszystkie ostrzeżenia Result Rezultat -Right folder: -Prawy folder: +Right: +Prawy: S&ave configuration Z&apisz konfigurację Save aborted! @@ -516,6 +556,8 @@ Scanning: Skanowanie: Select a folder Wybierz folder +Select logfile directory: +Wybierz katalog z logami: Select variant: Wybierz wariant: Show files that are different @@ -534,8 +576,8 @@ Show popup Pokaż okno popup Show popup on errors or warnings Pokaż okno popup dla błędów i ostrzeżeń -Significant difference detected: More than 50% of files will be overwritten/deleted! -Wykryto znaczące zmiany: Ponad 50% plików zostanie nadpisane/usunięte! +Significant difference detected: +Wykryto znaczące zmiany: Silent mode Tryb Cichy Size @@ -544,6 +586,8 @@ Sorting file list... Sortowanie listy plików... Source code written completely in C++ utilizing: Kod źródłowy napisany całkowicie w C++ z wykorzystaniem: +Speed: +Prędkość: Start Rozpocznij Start synchronization @@ -576,24 +620,26 @@ Target file already existing! Plik docelowy już istnieje! The file does not contain a valid configuration: Nieprawidłowy format pliku: -File does not exist: -Plik nie istnieje: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Ta komenda będzie wykonywana za każdym razem jak klikniesz dwa razy na dany plik. %name jest wskaźnikiem na katalog zaznaczonego pliku. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Ta komenda będzie wykonywana za każdym razem jak klikniesz dwa razy na dany plik. %x jest wskaźnikiem na katalog zaznaczonego pliku. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Ten wariant traktuje dwa pliki jako równe w przypadku gdy mają jednakowy rozmiar oraz tą samą datę i czas ostatniej modyfikacji. Time Czas Time elapsed: Czas: +Time remaining: +Pozostały czas: Total amount of data that will be transferred Liczba danych do przekopiowania +Total required free disk space: +Całkowita wymagana przestrzeń dyskowa: Total time: Całkowity czas: Two way <-> Obustronna <-> +Unable to connect to sourceforge.net! +Nie można się połączyć z sourceforge.net! Unable to create logfile! Nie można utworzyć pliku z logami! Unable to initialize Recycle Bin! diff --git a/Languages/portuguese.lng b/Languages/portuguese.lng index 4088dcec..8fdef162 100644 --- a/Languages/portuguese.lng +++ b/Languages/portuguese.lng @@ -10,14 +10,24 @@ PB TB TB + day(s) + dia(s) + hour(s) + hora(s) kB kB + min + min + sec + seg != files are different\n != ficheiros diferentes\n %x directories %x pastas %x files, %x ficheiros, +%x is not a valid FreeFileSync batch file! +%x não é um batch válido do FreeFileSync! %x of %y rows in view %x de %y ficheiros %x of 1 row in view @@ -32,6 +42,8 @@ &Aplicar &Cancel &Cancelar +&Check for new version +&Procurar actualizações &Compare &Comparar &Create batch job @@ -94,6 +106,8 @@ -Sincronização de ficheiros Open-Source- . , +/sec +/seg 1 directory 1 pastas 1 file, @@ -108,8 +122,8 @@ 2. Usar '*' e '?' como caracteres de procura. 3. Exclude files directly on main grid via context menu. 3. Excluir ficheiros directamente da grelha através do menu de contexto. -4. Keep the number of entries small for best performance. -4. Manter um número baixo de entradas para melhor performance. +4. Keep the number of (different) entries small for best performance. +4. Manter o número de entradas (diferentes) pequeno para melhor performance. << left file is newer\n << ficheiro à esquerda mais recente\n <Directory> @@ -124,8 +138,8 @@ == ficheiros iguais\n\n >> right file is newer\n >> ficheiro à direita mais recente\n -Abort -Abortar +A newer version of FreeFileSync is available: +Mais recente versão do FreeFileSync disponível: Abort requested: Waiting for current operation to finish... Abortar pedido: À espera do fim da operação... Aborted @@ -212,10 +226,10 @@ Copy new or updated files to right folder. Copiar ficheiros novos ou actualizados para a direita Copy to clipboard\tCTRL+C Copiar para a Área de transferência\tCTRL+C -Copying file %x overwriting %y -Copiar ficheiro %x substituindo %y Copying file %x to %y Copiar ficheiro %x para %y +Copying file %x to %y overwriting target +Copiar ficheiro %x para %y substituindo Could not set working directory: Não pode definir pasta de trabalho: Create a batch job @@ -256,10 +270,12 @@ Deleting folder %x Apagar pasta %x Directories are dependent! Be careful when setting up synchronization rules: Directórios são dependentes! Cuidado ao definir as regras de sincronização: +Directory +Directório Directory does not exist: A pasta não existe: -Do not show graphical status and error messages but write to a logfile instead -Escrever para um ficheiro log em vez de mostrar status ou mensagens de erro +Do not display visual status information but write to a logfile instead +Não apresentar informação de estado, mas escrever num ficheiro log Do not show this warning again Não voltar a mostrar este aviso Do nothing @@ -268,12 +284,18 @@ Do you really want to delete the following objects(s)? Quer mesmo eliminar o(s) seguinte(s) item(s) ? Do you really want to move the following objects(s) to the Recycle Bin? Quer mesmo mover o(s) seguinte(s) objecto(s) para a Reciclagem? +Do you want FreeFileSync to automatically check for updates every week? +Deseja que o FreeFileSync procure automaticamente actualizações todas as semanas? Donate with PayPal Doar usando PayPal +Download now? +Fazer download agora? Drag && drop Pegar && largar -Email: -Email: +Email +Email +Enable filter to exclude files from synchronization +Ligar filtro para excluir ficheiros da sincronização Error Erro Error changing modification time: @@ -302,6 +324,8 @@ Error reading file: Erro de leitura de ficheiro: Error resolving symbolic link: Erro na resolução do link simbólico: +Error retrieving full path: +Erro no retorno do caminho: Error traversing directory: Erro ao percorrer a pasta: Error writing file attributes: @@ -326,12 +350,14 @@ Feedback and suggestions are welcome at: Comentários e sugestões são benvindos em: File Manager integration: Integração c/ Gestor de Ficheiros: -File Time Tolerance: -Tolerância de tempo do ficheiro: +File Time tolerance (seconds): +Tempo de tolerância do ficheiro (segundos): File already exists. Overwrite? O ficheiro já existe. Deseja substituir? File content Conteúdo do ficheiro +File does not exist: +O ficheiro não existe: File list exported! Lista dos ficheiros exportada! File size and date @@ -362,6 +388,8 @@ Files/folders that exist on left side only Ficheiros/pastas existentes somente à esquerda Files/folders that exist on right side only Ficheiros/pastas existentes somente à direita +Filter +Filtro Filter active: Press again to deactivate Filtro activo: Clique aqui para desactivar Filter files @@ -382,6 +410,8 @@ FreeFileSync batch file FreeFileSync Ficheiro batch FreeFileSync configuration FreeFileSync configuração +FreeFileSync is up to date! +FreeFileSync está actualizado! Full name Nome completo Generating file list... @@ -412,10 +442,10 @@ Hides error messages during synchronization:\nThey are collected and shown as a Ocultar mensagens de erro durante a sincronização:\nSerão coleccionadas e mostradas numa lista no fim do processo Hints: Dicas: -Homepage: -Homepage: +Homepage +Site If you like FFS -SE gosta de FFS +Se gosta do FreeFileSync Ignore errors Ignorar erros Ignore subsequent errors @@ -438,8 +468,8 @@ Initialization of Recycle Bin failed! Início da Reciclagem falhou! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Não é possível aceder à Reciclagem!\n\nÉ possível que não esteja a utilizar Windows.\nSe desejar esta opção incluída, é favor contactar o autor. :) -Left folder: -Pasta esquerda: +Left: +Esquerda: Legend Legenda Load configuration from file @@ -448,16 +478,22 @@ Load configuration history (press DEL to delete items) Carregar histórico de configuração (pressione DEL para apagar itens) Log-messages: Log de mensagens: +Logging +A escrever em log Mirror ->> Espelhar ->> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. Espelhar pasta da esquerda: A pasta da direita vai ser sobreposta e uma cópia exacta da pasta esquerda após sincronização. +More than 50% of the total number of files will be copied or deleted! +Mais de 50% dos ficheiros vai ser copiado ou apagado! Move column down Mover coluna para baixo Move column up Mover coluna para cima Not all items were synchronized! Have a look at the list. Nem todos os itens foram sincronizados! Por favor, verifique a lista. +Not enough free disk space available in: +Não há espaço livre suficiente em: Nothing to synchronize according to configuration! Nada a sincronizar de acordo com a configuração! Number of files and directories that will be created @@ -478,8 +514,12 @@ Operation aborted! Operação abortada! Operation: Operação: +Overview +Vista Pause Pausa +Paused +Em pausa Please fill all empty directory fields. Por favor, preencha todos os campos vazios. Press button to activate filter @@ -502,8 +542,8 @@ Resets all warning messages Reinicia mensagens de aviso Result Resultado -Right folder: -Pasta da direita: +Right: +Direita: S&ave configuration G&uardar a configuração Save aborted! @@ -516,8 +556,10 @@ Scanning: A pesquisar: Select a folder Seleccione uma pasta +Select logfile directory: +Seleccione directório para ficheiro log: Select variant: -Sleccione uma variante: +Seleccione uma variante: Show files that are different Mostrar ficheiros diferentes Show files that are equal @@ -534,8 +576,8 @@ Show popup Mostrar popups Show popup on errors or warnings Mostrar popup c/ erros ou avisos -Significant difference detected: More than 50% of files will be overwritten/deleted! -Diferença significativa detectada: Mais de 50% dos ficheiros serão substituídos/apagados! +Significant difference detected: +Diferença significativa detectada: Silent mode Modo silencioso Size @@ -544,6 +586,8 @@ Sorting file list... Ordenar lista de ficheiros... Source code written completely in C++ utilizing: Código fonte todo escrito em C++ utilizando: +Speed: +Velocidade: Start Iniciar Start synchronization @@ -576,24 +620,26 @@ Target file already existing! Ficheiro destino já existe! The file does not contain a valid configuration: O ficheiro não contém uma configuração válida: -File does not exist: -O ficheiro não existe: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Esta linha de comandos será executada cada vez que fizer duplo click num ficheiro. %name serve para reservar o lugar do ficheiro seleccionado. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Esta linha de comandos será executada cada vez que fizer duplo click num ficheiro. %x serve para reservar o lugar do ficheiro seleccionado. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Esta variante avalia dois ficheiros de nome igual como iguais quando têm o mesmo tamanho e a mesma data e hora de modificação. Time Hora Time elapsed: Tempo passado: +Time remaining: +Tempo restante: Total amount of data that will be transferred Volume de dados a ser transferido +Total required free disk space: +Espaço livre necessário: Total time: Tempo total: Two way <-> 2 sentidos <-> +Unable to connect to sourceforge.net! +Não é possível ligar a sourceforge.net! Unable to create logfile! Não é possível criar ficheiro log! Unable to initialize Recycle Bin! diff --git a/Languages/portuguese_br.lng b/Languages/portuguese_br.lng new file mode 100644 index 00000000..0e7f43f6 --- /dev/null +++ b/Languages/portuguese_br.lng @@ -0,0 +1,674 @@ + MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE + MinGW \t- Windows port of GNU Compiler Collection\n wxWidgets \t- Open-Source GUI framework\n wxFormBuilder\t- wxWidgets GUI-builder\n CodeBlocks \t- Open-Source IDE + Byte + Byte + GB + GB + MB + MB + PB + PB + TB + TB + day(s) + dia(s) + hour(s) + hora(s) + kB + kB + min + min + sec + seg +!= files are different\n +!= arquivos são diferentes\n +%x directories +%x diretórios +%x files, +%x arquivos, +%x is not a valid FreeFileSync batch file! +%x não é um arquivo batch válido do FreeFileSync! +%x of %y rows in view +%x de %y arquivos +%x of 1 row in view +%x de 1 linha +&Abort +&Abortar +&About... +&Sobre... +&Advanced +&Avançado +&Apply +&Aplicar +&Cancel +&Cancelar +&Check for new version +&Procurar novas versões +&Compare +&Comparar +&Create batch job +&Criar um arquivo batch +&Default +&Config. Padrão +&Export file list +&Exportar lista de arquivos +&File +&Arquivo +&Global settings +&Configurações +&Help +&Ajuda +&Ignore +&Ignorar +&Language +&Idioma +&Load +&Carregar +&Load configuration +&Carregar configuração +&OK +&OK +&Pause +&Pausa +&Quit +&Sair +&Retry +&Tentar de Novo +&Save +&Salvar +&Start +&Iniciar +&Synchronize... +&Sincronizar... +, +. +- different +- diferente +- different (same date, different size) +- diferente (mesma data, tamanho diferente) +- equal +- igual +- exists left only +- existe apenas à esquerda +- exists right only +- existe apenas à direita +- left +- esquerda +- left newer +- mais recente à esquerda +- right +- direita +- right newer +- mais recente à direita +- same date (different size) +- mesma data (tamanho diferente) +-Open-Source file synchronization- +-Sincronização de Arquivos Open-Source- +. +, +/sec +/seg +1 directory +1 diretório +1 file, +1 arquivo, +1. &Compare +1. &Comparar +1. Enter full file or directory names separated by ';' or a new line. +1. Introduzir nome completo dos arquivos ou pastas separados por ';' ou uma nova linha. +2. &Synchronize... +2. &Sincronizar... +2. Use wildcard characters '*' and '?'. +2. Usar '*' e '?' como caracteres de procura. +3. Exclude files directly on main grid via context menu. +3. Excluir arquivos diretamente do grid principal através do menu de contexto. +4. Keep the number of (different) entries small for best performance. +4. Manter o número de entradas (diferentes) pequeno para melhor desempenho. +<< left file is newer\n +<< arquivo à esquerda é mais recente\n +<Directory> +<Diretório> +<Last session> +<Última Sessão> +<multiple selection> +<seleção múltipla> +<| file on left side only\n +<| arquivo apenas à esquerda\n +== files are equal\n\n +== arquivos são iguais\n\n +>> right file is newer\n +>> arquivo à direita é mais recente\n +A newer version of FreeFileSync is available: +Uma nova versão do FreeFileSync foi encontrada: +Abort requested: Waiting for current operation to finish... +Cancelar pedido: Esperando fim da operação... +Aborted +Cancelado +About +Sobre +Action +Ação +Add folder pair +Adicionar par de pastas +All items have been synchronized! +Todos os itens foram sincronizados! +An exception occured! +Ocorreu uma exceção! +As a result the files are separated into the following categories: +Como resultado, os arquivos são separados nas seguintes categorias: +As the name suggests, two files which share the same name are marked as equal if and only if they have the same content. This option is useful for consistency checks rather than backup operations. Therefore the file times are not taken into account at all.\n\nWith this option enabled the decision tree is smaller: +Como o nome sugere, dois arquivos com o mesmo nome são assinalados como iguais se e somente se o seu conteúdo for idêntico. Esta opção é útil para controles de consistência mais do que para efeitos de backup. Portanto, a data dos arquivos não é levada em consideração.\n\nCom esta opção, a árvore de decisão é menor: +Assemble a batch file for automated synchronization. To start in batch mode simply pass the name of the file to the FreeFileSync executable: FreeFileSync.exe <batchfile>. This can also be scheduled in your operating system's task planner. +Criar um arquivo batch para sincronização automatizada. Para iniciar o modo batch, passar o nome do arquivo para o executável do FreeFileSync: FreeFileSync.exe <arquivo batch>. Também pode ser programado no Agendador de Tarefas do sistema operacional. +Batch execution +Execução do batch +Batch file created successfully! +Arquivo batch criado com sucesso! +Batch job +Arquivo Batch +Big thanks for localizing FreeFileSync goes out to: +Pela tradução do FreeFileSync, um agradecimento a: +Build: +Criado: +Cancel +Cancelar +Check all +Verificar tudo +Choose to hide filtered files/directories from list +Ocultar arquivos/diretórios filtrados da lista +Comma separated list +Lista de itens separada por vírgula +Compare both sides +Comparar os dois lados +Compare by \"File content\" +Comparar por \"Conteúdo dos arquivos\" +Compare by \"File size and date\" +Comparar por \"Data e tamanho dos arquivos\" +Compare by... +Comparar por... +Comparing content +Comparando conteúdo +Comparing content of files %x +Comparando conteúdo do arquivo %x +Comparing... +Comparando... +Completed +Finalizado +Configuration +Configuração +Configuration loaded! +Configuração carregada! +Configuration overview: +Parâmetros de configuração: +Configuration saved! +Configuração salva! +Configure filter +Configurar filtros +Configure filter... +Configurar filtros... +Configure your own synchronization rules. +Configure as suas próprias regras de sincronização. +Confirm +Confirmar +Continue +Continuar +Conversion error: +Erro de conversão: +Copy from left to right +Copiar da esquerda para a direita +Copy from left to right overwriting +Copiar da esquerda para a direita com sobreposição +Copy from right to left +Copiar da direita para a esquerda +Copy from right to left overwriting +Copiar da direita para a esquerda com sobreposição +Copy new or updated files to right folder. +Copiar arquivos novos ou atualizados para a pasta da direita +Copy to clipboard\tCTRL+C +Copiar para a Área de transferência\tCTRL+C +Copying file %x to %y +Copiando arquivo %x para %y +Copying file %x to %y overwriting target +Copiando arquivo %x para %y substituindo destino +Could not set working directory: +Não se pode definir pasta de trabalho: +Create a batch job +Criar arquivo batch +Create: +Criar: +Creating folder %x +Criando pasta %x +Current operation: +Operação atual: +Custom +Personalizado +Customize columns +Personalizar colunas +DECISION TREE +ÁRVORE DE DECISÃO +Data remaining: +Dados faltantes: +Data: +Dados: +Date +Data +Delete files/folders existing on left side only +Apagar arquivos/pastas existentes apenas no lado esquerdo +Delete files/folders existing on right side only +Apagar arquivos/pastas existentes apenas no lado direito +Delete files\tDEL +Apagar arquivos\tDEL +Delete on both sides +Apagar em ambos os lados +Delete on both sides even if the file is selected on one side only +Apagar em ambos os lados mesmo se o arquivo está selecionado só em um lado +Delete: +Apagar: +Deleting file %x +Apagando arquivo %x +Deleting folder %x +Apagando pasta %x +Directories are dependent! Be careful when setting up synchronization rules: +Diretórios são dependentes! Cuidado ao definir as regras de sincronização: +Directory +Diretório +Directory does not exist: +Diretório não existe: +Do not display visual status information but write to a logfile instead +Ao invés de mostrar informação do estado visualmente, escrever para um arquivo log +Do not show this warning again +Não mostrar novamente este aviso +Do nothing +Não fazer nada +Do you really want to delete the following objects(s)? +Quer mesmo apagar o(s) seguinte(s) item(s) ? +Do you really want to move the following objects(s) to the Recycle Bin? +Quer mesmo mover o(s) seguinte(s) item(s) para a Lixeira? +Do you want FreeFileSync to automatically check for updates every week? +Deseja que o FreeFileSync procure automaticamente novas versões toda semana? +Donate with PayPal +Doar usando PayPal +Download now? +Baixar agora? +Drag && drop +Arrastar && Soltar +Email +Email +Enable filter to exclude files from synchronization +Habilitar filtro para excluir arquivos da sincronização +Error +Erro +Error changing modification time: +Erro ao mudar a hora de modificação: +Error copying file: +Erro ao copiar arquivo: +Error creating directory: +Erro ao criar diretório: +Error deleting directory: +Erro ao apagar diretório: +Error deleting file: +Erro ao apagar arquivo: +Error handling +Controlador de erros +Error loading library function: +Erro ao carregar a biblioteca de funções: +Error moving to Recycle Bin: +Erro ao mover para a Lixeira: +Error opening file: +Erro ao abrir arquivo: +Error parsing configuration file: +Erro de leitura do arquivo de configuração: +Error reading file attributes: +Erro ao ler atributos do arquivo: +Error reading file: +Erro ao ler arquivo: +Error resolving symbolic link: +Erro na resolução de link simbólico: +Error retrieving full path: +Erro ao obter caminho completo: +Error traversing directory: +Erro ao percorrer diretório: +Error writing file attributes: +Erro ao escrever atributos do arquivo: +Error writing file: +Erro ao escrever arquivo: +Error: Source directory does not exist anymore: +Erro: Diretório de origem não existe mais: +Example +Exemplo +Exclude +Excluir +Exclude temporarily +Excluir temporariamente +Exclude via filter: +Excluir por filtro: +Exit immediately and set returncode < 0 +Sair imediatamente e setar\nreturncode < 0 +Exit with RC < 0 +Sair com RC < 0 +Feedback and suggestions are welcome at: +Comentários e sugestões são bem-vindos em: +File Manager integration: +Integração com Gerenciador de Arquivos: +File Time tolerance (seconds): +Tolerância de Tempo do Arquivo (segundos): +File already exists. Overwrite? +O arquivo já existe. Deseja substituir? +File content +Conteúdo do arquivo +File does not exist: +O arquivo não existe: +File list exported! +Lista de arquivos exportada! +File size and date +Data e tamanho do arquivo +File times that differ by up to the specified number of seconds are still handled as having same time. +Arquivos com diferença de horário até o número especificado de segundos são tratados como tendo o mesmo horário. +Filename +Nome do arquivo +Files are found equal if\n - file content\nis the same. +Os arquivos são considerados iguais se\n - o conteúdo do arquivo\né o mesmo. +Files are found equal if\n - filesize\n - last write time and date\nare the same. +Os arquivos são considerados iguais se\n - o tamanho\n - e a data e hora da última modificação\nsão iguais. +Files remaining: +Arquivos restantes: +Files that exist on both sides and have different content +Arquivos que existem nos dois lados e têm conteúdo diferente +Files that exist on both sides, have same date but different filesizes +Arquivos que existem nos dois lados, têm a mesma data mas tamanhos diferentes +Files that exist on both sides, left one is newer +Arquivos que existem nos dois lados, sendo à esquerda mais recentes +Files that exist on both sides, right one is newer +Arquivos que existem nos dois lados, sendo à direita mais recentes +Files/folders remaining: +Arquivos/pastas restantes: +Files/folders scanned: +Arquivos/pastas analisados: +Files/folders that exist on left side only +Arquivos/pastas que existem somente à esquerda +Files/folders that exist on right side only +Arquivos/pastas que existem somente à direita +Filter +Filtro +Filter active: Press again to deactivate +Filtro ativo: Clique novamente para desativar +Filter files +Filtrar arquivos +Filter view +Filtrar vista +Folder Comparison and Synchronization +Comparação e Sincronização de Pastas +Folder pair +Par de pastas +FreeFileSync - Folder Comparison and Synchronization +FreeFileSync - Comparação e Sincronização de Pastas +FreeFileSync Batch Job +FreeFileSync Arquivo batch +FreeFileSync at Sourceforge +FreeFileSync na Sourceforge +FreeFileSync batch file +FreeFileSync Arquivo batch +FreeFileSync configuration +FreeFileSync configuração +FreeFileSync is up to date! +FreeFileSync está atualizado! +Full name +Nome completo +Generating file list... +Gerando lista de arquivos... +Global settings +Configurações +Help +Ajuda +Hide all error and warning messages +Ocultar todas as mensagens de erro ou aviso +Hide files that are different +Ocultar arquivos que são diferentes +Hide files that are equal +Ocultar arquivos que são iguais +Hide files that are newer on left +Ocultar arquivos que são mais recentes à esquerda +Hide files that are newer on right +Ocultar arquivos que são mais recentes à direita +Hide files that exist on left side only +Ocultar arquivos que existem somente à esquerda +Hide files that exist on right side only +Ocultar arquivos que existem somente à direita +Hide filtered items +Ocultar itens filtrados +Hide further error messages during the current process +Ocultar próximas mensagens de erro durante este processo +Hides error messages during synchronization:\nThey are collected and shown as a list at the end of the process +Ocultar mensagens de erro durante a sincronização:\nElas serão armazenadas e mostradas em uma lista ao final do processo +Hints: +Dicas: +Homepage +Homepage +If you like FFS +Se gosta do FFS +Ignore errors +Ignorar erros +Ignore subsequent errors +Ignorar erros subsequentes +Ignore this error, retry or abort synchronization? +Ignorar este erro, tentar novamente ou abortar a sincronização? +Ignore this error, retry or abort? +Ignorar este erro, tentar novamente ou abortar? +Include +Incluir +Include temporarily +Incluir temporariamente +Include: *.doc;*.zip;*.exe\nExclude: *\\temp\\* +Incluir: *.doc;*.zip;*.exe\nExcluir: *\\temp\\* +Info +Info +Information +Informação +Initialization of Recycle Bin failed! +Inicialização da Lixeira falhou! +It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) +Não foi possível acessar a Lixeira!\n\nÉ possível que não esteja utilizando Windows.\nSe desejar esta opção incluída, favor contatar o autor. :) +Left: +Esquerda: +Legend +Legenda +Load configuration from file +Carregar configuração do arquivo +Load configuration history (press DEL to delete items) +Carregar histórico de configuração (pressione DEL para apagar itens) +Log-messages: +Log de mensagens: +Logging +Gravando log +Mirror ->> +Espelhar ->> +Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. +Espelhar pasta da esquerda: A pasta da direita vai ser sobreposta e será feita uma cópia exata da pasta da esquerda após a sincronização. +More than 50% of the total number of files will be copied or deleted! +Mais de 50% do número total de arquivos será copiado ou apagado! +Move column down +Mover coluna para baixo +Move column up +Mover coluna para cima +Not all items were synchronized! Have a look at the list. +Nem todos os itens foram sincronizados! Verifique a lista. +Not enough free disk space available in: +Espaço em disco insuficiente em: +Nothing to synchronize according to configuration! +Nada para sincronizar de acordo com a configuração! +Number of files and directories that will be created +Número de arquivos e diretórios que serão criados +Number of files and directories that will be deleted +Número de arquivos e diretórios que serão apagados +Number of files that will be overwritten +Número de arquivos que serão substituídos +OK +OK +Only files/directories that pass filtering will be selected for synchronization. The filter will be applied to the full name including path prefix. +Apenas arquivos/diretórios que passem a filtragem serão selecionados para sincronização. O filtro será aplicado ao nome completo, incluindo o prefixo do caminho. +Open synchronization dialog +Abrir opções de sincronização +Open with File Manager\tD-Click +Abrir com Gerenciador de Arquivos\tD-Click +Operation aborted! +Operação cancelada! +Operation: +Operação: +Overview +Parâmetros +Pause +Pausa +Paused +Pausado +Please fill all empty directory fields. +Por favor, preencha todos os campos de diretórios vazios. +Press button to activate filter +Pressione para ativar o filtro +Preview +Pré-visualização +Published under the GNU General Public License: +Publicado sobre a GNU General Public License: +Quit +Sair +Relative path +Caminho relativo +Remove folder pair +Remover par de pastas +Reset +Reiniciar +Reset all warning messages? +Reiniciar todas as mensagens de aviso? +Resets all warning messages +Reinicia todas as mensagens de aviso +Result +Resultado +Right: +Direita: +S&ave configuration +S&alvar configuração +Save aborted! +Salvar cancelado! +Save current configuration to file +Salvar configuração atual para arquivo +Scanning... +Pesquisando... +Scanning: +Pesquisando: +Select a folder +Selecione uma pasta +Select logfile directory: +Escolha um diretório para salvar o arquivo log: +Select variant: +Selecione uma variante: +Show files that are different +Mostrar arquivos que são diferentes +Show files that are equal +Mostrar arquivos que são iguais +Show files that are newer on left +Mostrar arquivos que são mais recentes à esquerda +Show files that are newer on right +Mostrar arquivos que são mais recentes à direita +Show files that exist on left side only +Mostrar arquivos que existem somente à esquerda +Show files that exist on right side only +Mostrar arquivos que existem somente à direita +Show popup +Mostrar popup +Show popup on errors or warnings +Mostrar popup com erros ou avisos +Significant difference detected: +Diferença significativa detectada: +Silent mode +Modo silencioso +Size +Tamanho +Sorting file list... +Ordenando lista de arquivos... +Source code written completely in C++ utilizing: +Código fonte todo escrito em C++ utilizando: +Speed: +Velocidade: +Start +Iniciar +Start synchronization +Iniciar sincronização +Stop +Parar +Swap sides +Inverter lados +Synchronization aborted! +Sincronização cancelada! +Synchronization completed successfully! +Sincronização finalizada com sucesso! +Synchronization completed with errors! +Sincronização finalizada com erros! +Synchronization filter +Filtro de sincronização +Synchronization settings +Parâmetros de sincronização +Synchronization status +Estado da sincronização +Synchronize all .doc, .zip and .exe files except everything from folder \"temp\". +Sincronizar todos os arquivos .doc, .zip and .exe exceto os da pasta \"temp\". +Synchronize both sides simultaneously: Copy new or updated files in both directions. +Sincronizar ambos os lados simultaneamente: Copiar os arquivos novos ou mais recentes em ambas as direções. +Synchronizing... +Sincronizando... +System out of memory! +Sistema sem memória! +Target file already existing! +Arquivo de destino já existe! +The file does not contain a valid configuration: +O arquivo não contém uma configuração válida: +This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. +Esta linha de comandos será executada cada vez que fizer duplo click em um arquivo. %name serve para reservar o lugar do arquivo selecionado. +This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. +Esta variante avalia dois arquivos de nomes equivalentes como sendo iguais quando têm o mesmo tamanho E a mesma data e hora de modificação. +Time +Hora +Time elapsed: +Tempo passado: +Time remaining: +Tempo restante: +Total amount of data that will be transferred +Volume de dados que será transferido +Total required free disk space: +Espaço livre total requerido: +Total time: +Tempo total: +Two way <-> +Dois sentidos <-> +Unable to connect to sourceforge.net! +Não foi possível conectar a sourceforge.net! +Unable to create logfile! +Não foi possível criar arquivo log! +Unable to initialize Recycle Bin! +Não foi possível inicializar a Lixeira! +Uncheck all +Deselecionar todos +Update -> +Atualizar -> +Update: +Atualizar: +Use Recycle Bin +Utilizar Lixeira +Use Recycle Bin when deleting or overwriting files during synchronization +Utilizar a Lixeira ao apagar ou substituir arquivos durante a sincronização +Warning +Atenção +Warning: Synchronization failed for %x item(s): +Atenção: A sincronização falhou para %x item(s): +Warnings: +Avisos: +When the comparison is started with this option set the following decision tree is processed: +Quando a comparação é iniciada com esta opção, a seguinte árvore de decisão é processada: +You may try to synchronize remaining items again (WITHOUT having to re-compare)! +Pode tentar sincronizar os elementos restantes outra vez (SEM ter que comparar novamente)! +different +diferente +file exists on both sides +arquivo existe em ambos os lados +on one side only +existente apenas em um lado +|> file on right side only\n +|> arquivo apenas do lado direito\n diff --git a/Languages/slovenian.lng b/Languages/slovenian.lng index 4bb4984d..1eec886b 100644 --- a/Languages/slovenian.lng +++ b/Languages/slovenian.lng @@ -10,14 +10,24 @@ PB TB TB + day(s) + dni + hour(s) + ur kB kB + min + min + sec + sek != files are different\n -!= datoteke so različne\n +!= datoteki sta različni\n %x directories %x imeniki %x files, %x datoteke, +%x is not a valid FreeFileSync batch file! +%x ni veljavna FreeFileSync batch datoteka! %x of %y rows in view %x od %y vrstic prikazanih %x of 1 row in view @@ -32,6 +42,8 @@ &Uveljavi &Cancel &Prekliči +&Check for new version +&Preveri za novo različico &Compare &Primerjaj &Create batch job @@ -83,21 +95,23 @@ Na&loži konfiguracijo - left - levo - left newer -- na levi novejša +- leva novejša - right - desno - right newer -- na desni novejša +- desna novejša - same date (different size) - enak datum (različna velikost) -Open-Source file synchronization- -Odprto-kodna sinhronizacija datotek- . , +/sec +/sek 1 directory 1 imenik 1 file, -1 datoeka, +1 datoteka, 1. &Compare 1. &Primerjaj 1. Enter full file or directory names separated by ';' or a new line. @@ -108,8 +122,8 @@ Na&loži konfiguracijo 2. Uporabite lahko tudi znake '*' in '?'. 3. Exclude files directly on main grid via context menu. 3. Izključite datoteke neposredno na glavni mreži s kontekstnim menujem. -4. Keep the number of entries small for best performance. -4. Za boljše delovanje vzdržujte majhno število vnosov. +4. Keep the number of (different) entries small for best performance. +4. Za boljšo učinkovitost ohranjajte število (različnih) vnosov majhno. << left file is newer\n << leva datoteka je novejša\n <Directory> @@ -121,11 +135,11 @@ Na&loži konfiguracijo <| file on left side only\n <| datoteka obstaja samo na levi\n == files are equal\n\n -== datoteke so enake\n\n +== datoteki sta enaki\n\n >> right file is newer\n >> desna datoteka je novejša\n -Abort -Prekini +A newer version of FreeFileSync is available: +Na voljo je nova različica FreeFileSync: Abort requested: Waiting for current operation to finish... Zahtevana je bila prekinitev: čakam, da se zaključi trenutna operacija... Aborted @@ -195,7 +209,7 @@ Konfiguriraj filter... Configure your own synchronization rules. Konfiguriraj vaša lastna sinhronizacijska pravila. Confirm -Potrdite +Potrdi Continue Nadaljuj Conversion error: @@ -212,14 +226,14 @@ Copy new or updated files to right folder. Kopiraj nove ali posodobljene datoteke v desno mapo. Copy to clipboard\tCTRL+C Kopiraj v odložišče\tCTRL+C -Copying file %x overwriting %y -Kopiram datoteko %x s prepisovanjem %y Copying file %x to %y Kopiram datoteko %x v %y +Copying file %x to %y overwriting target +Kopiram datoteko %x v %y s prepisovanjem cilja Could not set working directory: Ne morem nastaviti delovnega imenika: Create a batch job -Ustvarite batch opravilo +Ustvari batch opravilo Create: Ustvari: Creating folder %x @@ -256,10 +270,12 @@ Deleting folder %x Brisanje mape %x Directories are dependent! Be careful when setting up synchronization rules: Imeniki so v odvisnosti! Bodite pozorni, ko nastavljate sinhronizacijska pravila: +Directory +Imenik Directory does not exist: Imenik ne obstaja: -Do not show graphical status and error messages but write to a logfile instead -Ne prikazuj grafičnega statusa in obvestil o napakah, ampak zabeleži samo v dnevniško datoteko +Do not display visual status information but write to a logfile instead +Ne prikazuj vidnih statusnih informacij, ampak piši v dnevniško datoteko Do not show this warning again Ne prikazuj več tega obvestila Do nothing @@ -268,12 +284,18 @@ Do you really want to delete the following objects(s)? Ali resnično želite izbrisati naslednje objekte? Do you really want to move the following objects(s) to the Recycle Bin? Ali resnično želite premakniti naslednje objekte v Koš? +Do you want FreeFileSync to automatically check for updates every week? +Ali želite, da FreeFileSync samodejno preverja za posodobitve vsak teden? Donate with PayPal Doniraj s PayPal +Download now? +Prenesem sedaj? Drag && drop Povleci && spusti -Email: -Email: +Email +Email +Enable filter to exclude files from synchronization +Omogoči filter, da se datoteke izključi iz sinhronizacije Error Napaka Error changing modification time: @@ -302,6 +324,8 @@ Error reading file: Napaka pri branju datoteke: Error resolving symbolic link: Napaka pri razreševanju simbolične povezave: +Error retrieving full path: +Napaka pri pridobivanju polne poti: Error traversing directory: Napaka pri prehajanju imenika: Error writing file attributes: @@ -326,12 +350,14 @@ Feedback and suggestions are welcome at: Povratne informacije in predlogi so dobrodošli na: File Manager integration: Integracija z urejevalnikom datotek: -File Time Tolerance: -Toleranca časa spremembe datoteke: +File Time tolerance (seconds): +Toleranca v času zadnje spremembe datoteke (sekunde): File already exists. Overwrite? Datoteka že obstaja. Prepišem? File content Vsebini datoteke +File does not exist: +Datoteka ne obstaja: File list exported! Seznam datotek je bil izvožen! File size and date @@ -362,6 +388,8 @@ Files/folders that exist on left side only Datoteke/mape, ki obstajajo samo na levi strani Files/folders that exist on right side only Datoteke/mape, ki obstajajo samo na desni strani +Filter +Filter Filter active: Press again to deactivate Filter aktiven: Ponovno kliknite za deaktivacijo Filter files @@ -382,6 +410,8 @@ FreeFileSync batch file FreeFileSync batch datoteka FreeFileSync configuration FreeFileSync konfiguracija +FreeFileSync is up to date! +FreeFileSync je posodobljen! Full name Polno ime Generating file list... @@ -412,8 +442,8 @@ Hides error messages during synchronization:\nThey are collected and shown as a Skrij obvestila o napakah med sinhronizacijo:\nObvestila se zbirajo in se prikažejo kot seznam na koncu procesa Hints: Namigi: -Homepage: -Domača stran: +Homepage +Domača stran If you like FFS Če vam je FFS všeč Ignore errors @@ -438,8 +468,8 @@ Initialization of Recycle Bin failed! Inicializacija Koša ni uspela! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) Ni bilo mogoče inicializirati Koša!\n\nZgleda, da ne uporabljate Windowsov.\nČe želite imeti vključeno to lastnost, prosimo kontaktirajte avtorja. :) -Left folder: -Leva mapa: +Left: +Levo: Legend Legenda Load configuration from file @@ -448,16 +478,22 @@ Load configuration history (press DEL to delete items) Naloži zgodovino konfiguracije (pritisnite DEL za brisanje predmetov) Log-messages: Sporočila beleženja: +Logging +Beležim Mirror ->> Zrcalno ->> Mirror backup of left folder: Right folder will be overwritten and exactly match left folder after synchronization. Zrcalna kopija leve mape: Desna mapa bo prepisana in se bo natančno ujemala z levo mapo po sinhronizaciji. +More than 50% of the total number of files will be copied or deleted! +Več kot 50% od celotnega števila datotek bo kopiranih ali izbrisanih! Move column down Premakni stolpec dol Move column up Premakni stolpec gor Not all items were synchronized! Have a look at the list. Vsi predmeti niso bili sinhronizirani! Poglejte seznam. +Not enough free disk space available in: +Na voljo ni dovolj prostega prostora na disku v: Nothing to synchronize according to configuration! Nič ni za sinhronizirati po trenutni konfiguraciji! Number of files and directories that will be created @@ -478,8 +514,12 @@ Operation aborted! Operacija prekinjena! Operation: Operacija: +Overview +Pregled Pause Pavza +Paused +Na pavzi Please fill all empty directory fields. Prosim izpolnite vse imenike s praznimi polji. Press button to activate filter @@ -502,8 +542,8 @@ Resets all warning messages Ponastavi vsa obvestila z opozorili Result Rezultat -Right folder: -Desna mapa: +Right: +Desno: S&ave configuration Shr&ani konfiguracijo Save aborted! @@ -516,6 +556,8 @@ Scanning: Pregledujem: Select a folder Izberite mapo +Select logfile directory: +Izberite imenik datoteke za beleženje: Select variant: Izberite varianto: Show files that are different @@ -534,8 +576,8 @@ Show popup Prikaži pojavno okno Show popup on errors or warnings Prikaži pojavno okno pri napakah in opozorilih -Significant difference detected: More than 50% of files will be overwritten/deleted! -Pomembna razlika zaznana: Več kot 50% datotek bo prepisanih/izbrisanih! +Significant difference detected: +Zaznana je važna razlika: Silent mode Tihi način Size @@ -544,6 +586,8 @@ Sorting file list... Sortiram seznam datotek... Source code written completely in C++ utilizing: Izvorna koda napisana celotno v C++ z uporabo: +Speed: +Hitrost: Start Začni Start synchronization @@ -573,27 +617,29 @@ Sinhroniziram... System out of memory! Sistemu je zmanjkalo pomnilnika! Target file already existing! -Tarčna datoteka že obstaja! +Ciljna datoteka že obstaja! The file does not contain a valid configuration: Datoteka ne vsebuje veljavne konfiguracije: -File does not exist: -Datoteka ne obstaja: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Ta ukaz bo izveden vsakič, ko boste dvokliknili na ime datoteke. %name služi kot rezerviran prostor za izbrano datoteko. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Ta ukaz bo izveden vsakič, ko boste dvokliknili na ime datoteke. %x služi kot rezerviran prostor za izbrano datoteko. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Ta varianta oceni dve datoteki z enakim imenom kot enaki, ko imata enako velikost IN enak datum ter čas zadnjega spreminjanja. Time Čas Time elapsed: Pretečen čas: +Time remaining: +Preostali čas: Total amount of data that will be transferred Količina podatkov, ki bo prenešena +Total required free disk space: +Skupno zahtevani prosti prostor na disku: Total time: Celoten čas: Two way <-> Obojesmerno <-> +Unable to connect to sourceforge.net! +Ne morem se povezati na sourceforge.net! Unable to create logfile! Ne morem ustvariti datoteko za beleženje! Unable to initialize Recycle Bin! @@ -611,7 +657,7 @@ Uporabi Koš ko se briše ali prepisuje datoteke med sinhronizacijo Warning Pozor Warning: Synchronization failed for %x item(s): -Pozore: Sinhronizacija ni uspela za %x predmetov: +Pozor: Sinhronizacija ni uspela za %x predmetov: Warnings: Opozorila: When the comparison is started with this option set the following decision tree is processed: @@ -623,6 +669,6 @@ različni file exists on both sides datoteka obstaja na obeh straneh on one side only -datoteka obstaja samo na eni strani +samo na eni strani |> file on right side only\n |> datoteka obstaja samo na desni\n diff --git a/Languages/spanish.lng b/Languages/spanish.lng index f00da89a..8b771aa7 100644 --- a/Languages/spanish.lng +++ b/Languages/spanish.lng @@ -110,8 +110,6 @@ 2. Usar '*' y '?' como caracteres comodín. 3. Exclude files directly on main grid via context menu. 3. Excluir directamente ficheros sobre la rejilla a través del menu de contexto. -4. Keep the number of entries small for best performance. -4. Mantener pequeño el número de entradas para conseguir un mejor funcionamiento. << left file is newer\n << el fichero de la izquierda es el más nuevo\n <Directory> @@ -126,10 +124,6 @@ == los ficheros son iguales\n\n >> right file is newer\n >> el fichero de la derecha es el más nuevo\n -A newer version is available: -Hay una nueva versión disponible: -Abort -Abortar Abort requested: Waiting for current operation to finish... Abortar pedido: Esperar a que la actual operación finalice... Aborted @@ -216,8 +210,6 @@ Copy new or updated files to right folder. Copiar ficheros nuevos o actualizados a la carpeta de la derecha. Copy to clipboard\tCTRL+C Copiar al Portapapeles\tCTRL+C -Copying file %x overwriting %y -Copiar fichero %x sustituyendo %y Copying file %x to %y Copiar fichero %x a %y Could not set working directory: @@ -262,8 +254,6 @@ Directories are dependent! Be careful when setting up synchronization rules: ¡Los directorios son dependientes! Cuidado al establecer las reglas de sincronización: Directory does not exist: El directorio no existe: -Do not show graphical status and error messages but write to a logfile instead -No mostrar estado ni errores en formato gráfico sino escribir un fichero "log" en su lugar. Do not show this warning again No mostrar este aviso otra vez Do nothing @@ -308,8 +298,6 @@ Error reading file: Error al leer el fichero: Error resolving symbolic link: Error al resolver enlace simbólico: -Error retrieving full name: -Error al recuperar el nombre completo: Error traversing directory: Error al trasladar directorio: Error writing file attributes: @@ -334,12 +322,12 @@ Feedback and suggestions are welcome at: Los comentarios y sugerencias será bienvenidos en: File Manager integration: Integración con el Explorador: -File Time Tolerance: -Tolerancia de Hora del Fichero: File already exists. Overwrite? El fichero ya existe. ¿Sustituir? File content Contenido del fichero +File does not exist: +El fichero no existe: File list exported! Lista de ficheros exportada! File size and date @@ -448,8 +436,6 @@ Initialization of Recycle Bin failed! ¡El inicio de la Papelera falló! It was not possible to initialize the Recycle Bin!\n\nIt's likely that you are not using Windows.\nIf you want this feature included, please contact the author. :) ¡No fue posible iniciar la Papelera!\n\nEs probable que no esté usando Windows.\nSi quiere que este hecho sea considerado, por favor, contactate con el autor :) -Left folder: -Carpeta de la izquierda: Legend Leyenda Load configuration from file @@ -512,8 +498,6 @@ Resets all warning messages Reinicia todos los mensajes de aviso Result Resultado -Right folder: -Carpeta de la derecha: S&ave configuration G&uardar configuración Save aborted! @@ -544,8 +528,6 @@ Show popup Mostrar "popups" Show popup on errors or warnings Mostrar "popup" de errores o avisos -Significant difference detected: More than 50% of files will be overwritten/deleted! -¡Detectada diferencia significativa!: Más del 50% de los ficheros serán sustituídos/borrados! Silent mode Modo silencioso Size @@ -586,12 +568,8 @@ Target file already existing! ¡El fichero objetivo existe ya! The file does not contain a valid configuration: El fichero no contiene una configuración válida: -File does not exist: -El fichero no existe: This commandline will be executed each time you doubleclick on a filename. %name serves as a placeholder for the selected file. Esta línea de comandos será ejecutada cada vez que haga doble click sobre un nombre de fichero. %name es el espacio reservado del fichero seleccionado. -This commandline will be executed each time you doubleclick on a filename. %x serves as a placeholder for the selected file. -Esta línea de comandos será ejecutada cada vez que haga doble click sobre un nombre de fichero. %x es el espacio reservado para el fichero seleccionado. This variant evaluates two equally named files as being equal when they have the same file size AND the same last write date and time. Esta variante evalúa dos ficheros con el mismo nombre como iguales cuando tienen el mismo tamaño Y la misma fecha de modificación. Time @@ -637,4 +615,4 @@ el fichero existe en ambos lados on one side only fichero sólo en un lado |> file on right side only\n -|> ficheiro en lado derecho sólo\n +|> fichero en lado derecho sólo\n @@ -1,82 +1,103 @@ -CPPFLAGS=-Wall -pipe -DNDEBUG `wx-config --cppflags` -DFFS_LINUX -DTIXML_USE_STL -O3 -pthread -c +CPPFLAGS=-Wall -pipe -DNDEBUG `wx-config --cppflags` `pkg-config --cflags gtk+-2.0` -DFFS_LINUX -DTIXML_USE_STL -O3 -pthread -c ENDFLAGS=`wx-config --libs` -O3 -pthread all: FreeFileSync init: if [ ! -d obj ]; then mkdir obj; fi - -obj/algorithm.o: algorithm.cpp + +structures.o: structures.cpp + g++ $(CPPFLAGS) structures.cpp -o obj/structures.o + +algorithm.o: algorithm.cpp g++ $(CPPFLAGS) algorithm.cpp -o obj/algorithm.o -obj/comparison.o: comparison.cpp +comparison.o: comparison.cpp g++ $(CPPFLAGS) comparison.cpp -o obj/comparison.o -obj/synchronization.o: synchronization.cpp +synchronization.o: synchronization.cpp g++ $(CPPFLAGS) synchronization.cpp -o obj/synchronization.o -obj/application.o: application.cpp +application.o: application.cpp g++ $(CPPFLAGS) application.cpp -o obj/application.o -obj/globalFunctions.o: library/globalFunctions.cpp +globalFunctions.o: library/globalFunctions.cpp g++ $(CPPFLAGS) library/globalFunctions.cpp -o obj/globalFunctions.o -obj/guiGenerated.o: ui/guiGenerated.cpp +guiGenerated.o: ui/guiGenerated.cpp g++ $(CPPFLAGS) ui/guiGenerated.cpp -o obj/guiGenerated.o -obj/mainDialog.o: ui/mainDialog.cpp +gridView.o: ui/gridView.cpp + g++ $(CPPFLAGS) ui/gridView.cpp -o obj/gridView.o + +mainDialog.o: ui/mainDialog.cpp g++ $(CPPFLAGS) ui/mainDialog.cpp -o obj/mainDialog.o -obj/syncDialog.o: ui/syncDialog.cpp +syncDialog.o: ui/syncDialog.cpp g++ $(CPPFLAGS) ui/syncDialog.cpp -o obj/syncDialog.o -obj/checkVersion.o: ui/checkVersion.cpp +checkVersion.o: ui/checkVersion.cpp g++ $(CPPFLAGS) ui/checkVersion.cpp -o obj/checkVersion.o -obj/customGrid.o: library/customGrid.cpp +batchStatusHandler.o: ui/batchStatusHandler.cpp + g++ $(CPPFLAGS) ui/batchStatusHandler.cpp -o obj/batchStatusHandler.o + +guiStatusHandler.o: ui/guiStatusHandler.cpp + g++ $(CPPFLAGS) ui/guiStatusHandler.cpp -o obj/guiStatusHandler.o + +customGrid.o: library/customGrid.cpp g++ $(CPPFLAGS) library/customGrid.cpp -o obj/customGrid.o -obj/fileHandling.o: library/fileHandling.cpp +fileHandling.o: library/fileHandling.cpp g++ $(CPPFLAGS) library/fileHandling.cpp -o obj/fileHandling.o -obj/multithreading.o: library/multithreading.cpp +multithreading.o: library/multithreading.cpp g++ $(CPPFLAGS) library/multithreading.cpp -o obj/multithreading.o -obj/statusHandler.o: library/statusHandler.cpp +statusHandler.o: library/statusHandler.cpp g++ $(CPPFLAGS) library/statusHandler.cpp -o obj/statusHandler.o -obj/resources.o: library/resources.cpp +resources.o: library/resources.cpp g++ $(CPPFLAGS) library/resources.cpp -o obj/resources.o -obj/smallDialogs.o: ui/smallDialogs.cpp +smallDialogs.o: ui/smallDialogs.cpp g++ $(CPPFLAGS) ui/smallDialogs.cpp -o obj/smallDialogs.o -obj/misc.o: library/misc.cpp - g++ $(CPPFLAGS) library/misc.cpp -o obj/misc.o +dragAndDrop.o: ui/dragAndDrop.cpp + g++ $(CPPFLAGS) ui/dragAndDrop.cpp -o obj/dragAndDrop.o -obj/tinyxml.o: library/tinyxml/tinyxml.cpp +localization.o: library/localization.cpp + g++ $(CPPFLAGS) library/localization.cpp -o obj/localization.o + +tinyxml.o: library/tinyxml/tinyxml.cpp g++ $(CPPFLAGS) library/tinyxml/tinyxml.cpp -o obj/tinyxml.o -obj/tinystr.o: library/tinyxml/tinystr.cpp +tinystr.o: library/tinyxml/tinystr.cpp g++ $(CPPFLAGS) library/tinyxml/tinystr.cpp -o obj/tinystr.o -obj/tinyxmlerror.o: library/tinyxml/tinyxmlerror.cpp +tinyxmlerror.o: library/tinyxml/tinyxmlerror.cpp g++ $(CPPFLAGS) library/tinyxml/tinyxmlerror.cpp -o obj/tinyxmlerror.o -obj/tinyxmlparser.o: library/tinyxml/tinyxmlparser.cpp +tinyxmlparser.o: library/tinyxml/tinyxmlparser.cpp g++ $(CPPFLAGS) library/tinyxml/tinyxmlparser.cpp -o obj/tinyxmlparser.o -obj/processXml.o: library/processXml.cpp +processXml.o: library/processXml.cpp g++ $(CPPFLAGS) library/processXml.cpp -o obj/processXml.o - -obj/zstring.o: library/zstring.cpp + +statistics.o: library/statistics.cpp + g++ $(CPPFLAGS) library/statistics.cpp -o obj/statistics.o + +zstring.o: library/zstring.cpp g++ $(CPPFLAGS) library/zstring.cpp -o obj/zstring.o - -obj/customButton.o: library/customButton.cpp + +customButton.o: library/customButton.cpp g++ $(CPPFLAGS) library/customButton.cpp -o obj/customButton.o -FreeFileSync: init obj/application.o obj/algorithm.o obj/comparison.o obj/customButton.o obj/checkVersion.o obj/synchronization.o obj/globalFunctions.o obj/guiGenerated.o obj/mainDialog.o obj/syncDialog.o obj/customGrid.o obj/fileHandling.o obj/resources.o obj/smallDialogs.o obj/multithreading.o obj/statusHandler.o obj/misc.o obj/tinyxml.o obj/tinystr.o obj/tinyxmlerror.o obj/tinyxmlparser.o obj/processXml.o obj/zstring.o - g++ $(ENDFLAGS) -o FreeFileSync obj/application.o obj/algorithm.o obj/comparison.o obj/customButton.o obj/checkVersion.o obj/synchronization.o obj/globalFunctions.o obj/guiGenerated.o obj/mainDialog.o obj/syncDialog.o obj/customGrid.o obj/fileHandling.o obj/resources.o obj/smallDialogs.o obj/multithreading.o obj/statusHandler.o obj/misc.o obj/tinyxml.o obj/tinystr.o obj/tinyxmlerror.o obj/tinyxmlparser.o obj/processXml.o obj/zstring.o +filter.o: library/filter.cpp + g++ $(CPPFLAGS) library/filter.cpp -o obj/filter.o + +FreeFileSync: init application.o structures.o algorithm.o comparison.o customButton.o filter.o checkVersion.o batchStatusHandler.o guiStatusHandler.o synchronization.o globalFunctions.o guiGenerated.o gridView.o mainDialog.o syncDialog.o customGrid.o fileHandling.o resources.o smallDialogs.o dragAndDrop.o multithreading.o statusHandler.o localization.o tinyxml.o tinystr.o tinyxmlerror.o tinyxmlparser.o processXml.o statistics.o zstring.o + g++ $(ENDFLAGS) -o FreeFileSync obj/application.o obj/structures.o obj/algorithm.o obj/comparison.o obj/customButton.o obj/filter.o obj/batchStatusHandler.o obj/guiStatusHandler.o obj/checkVersion.o obj/synchronization.o obj/globalFunctions.o obj/guiGenerated.o obj/gridView.o obj/mainDialog.o obj/syncDialog.o obj/customGrid.o obj/fileHandling.o obj/resources.o obj/smallDialogs.o obj/dragAndDrop.o obj/multithreading.o obj/statusHandler.o obj/localization.o obj/tinyxml.o obj/tinystr.o obj/tinyxmlerror.o obj/tinyxmlparser.o obj/processXml.o obj/statistics.o obj/zstring.o clean: find obj -type f -exec rm {} \; diff --git a/Makefile_Win.cmd b/Makefile_Win.cmd index 965143f4..2296d1f6 100644 --- a/Makefile_Win.cmd +++ b/Makefile_Win.cmd @@ -1,12 +1,45 @@ +::------------------------------------------------------- ::set these variables to the appropriate directories set widgets=C:\Programme\C++\wxWidgets set widgetslib=C:\Programme\C++\wxWidgets\lib\gcc_lib\mswu set sources=. set mingw=C:\Programme\C++\MinGW\bin +::------------------------------------------------------- set parameters=-Wall -pipe -mthreads -D__GNUWIN32__ -DwxUSE_UNICODE -D__WXMSW__ -DFFS_WIN -O3 -DNDEBUG -DTIXML_USE_STL path=%path%;%mingw% if not exist obj md obj +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\structures.cpp -o obj\structures.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\application.cpp -o obj\application.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\algorithm.cpp -o obj\algorithm.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\comparison.cpp -o obj\comparison.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\synchronization.cpp -o obj\synchronization.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\globalFunctions.cpp -o obj\globalFunctions.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\multithreading.cpp -o obj\multithreading.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\statusHandler.cpp -o obj\statusHandler.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\fileHandling.cpp -o obj\fileHandling.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\guiGenerated.cpp -o obj\GUI_Generated.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\gridView.cpp -o obj\gridView.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\mainDialog.cpp -o obj\MainDialog.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\syncDialog.cpp -o obj\SyncDialog.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\checkVersion.cpp -o obj\checkVersion.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\batchStatusHandler.cpp -o obj\batchStatusHandler.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\guiStatusHandler.cpp -o obj\guiStatusHandler.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\customGrid.cpp -o obj\CustomGrid.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\resources.cpp -o obj\Resources.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\localization.cpp -o obj\localization.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\statistics.cpp -o obj\statistics.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\smallDialogs.cpp -o obj\SmallDialogs.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\ui\dragAndDrop.cpp -o obj\dragAndDrop.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\tinyxml\tinyxml.cpp -o obj\tinyxml.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\tinyxml\tinystr.cpp -o obj\tinystr.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\tinyxml\tinyxmlerror.cpp -o obj\tinyxmlerror.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\tinyxml\tinyxmlparser.cpp -o obj\tinyxmlparser.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\processXml.cpp -o obj\processXml.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\zstring.cpp -o obj\zstring.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\customButton.cpp -o obj\customButton.o +mingw32-g++.exe %parameters% -I%widgets%\include -I%widgets%\contrib\include -I%widgetslib% -c %sources%\library\filter.cpp -o obj\filter.o + windres.exe -i %sources%\resource.rc -J rc -o obj\resource.res -O coff -I%widgets%\include -I%widgetslib% -mingw32-g++.exe -L%widgets%\lib\gcc_lib -o FreeFileSync.exe obj\application.o obj\algorithm.o obj\comparison.o obj\synchronization.o obj\globalFunctions.o obj\checkVersion.o obj\multithreading.o obj\statusHandler.o obj\fileHandling.o obj\customButton.o obj\misc.o obj\GUI_Generated.o obj\MainDialog.o obj\SyncDialog.o obj\CustomGrid.o obj\Resources.o obj\SmallDialogs.o obj\resource.res obj\tinyxml.o obj\tinystr.o obj\tinyxmlerror.o obj\tinyxmlparser.o obj\processXml.o obj\zstring.o -s -mthreads -lwxmsw28u_adv -lwxmsw28u_core -lwxbase28u_net -lwxbase28u -lwxpng -lwxzlib -lkernel32 -lws2_32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lcomctl32 -lwsock32 -lodbc32 -mwindows +mingw32-g++.exe -L%widgets%\lib\gcc_lib -o FreeFileSync.exe obj\structures.o obj\application.o obj\algorithm.o obj\dragAndDrop.o obj\comparison.o obj\batchStatusHandler.o obj\statistics.o obj\guiStatusHandler.o obj\synchronization.o obj\globalFunctions.o obj\checkVersion.o obj\multithreading.o obj\statusHandler.o obj\fileHandling.o obj\customButton.o obj\filter.o obj\localization.o obj\GUI_Generated.o obj\gridView.o obj\MainDialog.o obj\SyncDialog.o obj\CustomGrid.o obj\Resources.o obj\SmallDialogs.o obj\resource.res obj\tinyxml.o obj\tinystr.o obj\tinyxmlerror.o obj\tinyxmlparser.o obj\processXml.o obj\zstring.o -s -mthreads -lwxmsw28u_adv -lwxmsw28u_core -lwxbase28u_net -lwxbase28u -lwxpng -lwxzlib -lkernel32 -lws2_32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lcomctl32 -lwsock32 -lodbc32 -mwindows pause
\ No newline at end of file @@ -1,4 +1,4 @@ -FreeFileSync v1.17 +FreeFileSync v1.18 ------------------ Usage @@ -45,7 +45,7 @@ Key Features 23. Portable version (.zip) available. 24. No Windows registry entries for portable version. 25. Support for \\?\ path prefix for unrestricted path length. (windows only) -26. Check for updates from within FreeFileSync. +26. Check for updates from within FreeFileSync automatically. Advanced topics @@ -78,7 +78,7 @@ FreeFileSync has a big focus on usability. Therefore drag & drop is supported in You can: - drag & drop any directory onto the main window to set the directory for comparison - drag & drop any file onto the main window to set the directory for comparison - - drag & drop *.ffs_gui files onto the main window to load the configuration within + - drag & drop *.ffs_gui files onto the main window to load the configuration contained - drag & drop *.ffs_batch files onto the main window to display and edit the batch configuration - drag & drop *.ffs_batch files onto the batch dialog to display and edit the batch configuration diff --git a/Resources.dat b/Resources.dat Binary files differindex cd5fa6e0..86bd81fa 100644 --- a/Resources.dat +++ b/Resources.dat diff --git a/algorithm.cpp b/algorithm.cpp index 00357e9d..c595bdd3 100644 --- a/algorithm.cpp +++ b/algorithm.cpp @@ -3,6 +3,10 @@ #include <cmath> #include <wx/log.h> #include "library/resources.h" +#include "library/globalFunctions.h" +#include "library/statusHandler.h" +#include "library/fileHandling.h" +#include <wx/msgdlg.h> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -15,36 +19,47 @@ using namespace FreeFileSync; -wxString FreeFileSync::formatFilesizeToShortString(const wxULongLong& filesize) +wxString FreeFileSync::formatFilesizeToShortString(const wxLongLong& filesize) { - return formatFilesizeToShortString(filesize.ToDouble()); + return FreeFileSync::formatFilesizeToShortString(filesize.ToDouble()); } +wxString FreeFileSync::formatFilesizeToShortString(const wxULongLong& filesize) +{ + return FreeFileSync::formatFilesizeToShortString(filesize.ToDouble()); +}; + + wxString FreeFileSync::formatFilesizeToShortString(const double filesize) { + if (filesize < 0) + return _("Error"); + double nrOfBytes = filesize; + bool unitByte = true; wxString unit = _(" Byte"); if (nrOfBytes > 999) { - nrOfBytes/= 1024; + unitByte = false; + nrOfBytes /= 1024; unit = _(" kB"); if (nrOfBytes > 999) { - nrOfBytes/= 1024; + nrOfBytes /= 1024; unit = _(" MB"); if (nrOfBytes > 999) { - nrOfBytes/= 1024; + nrOfBytes /= 1024; unit = _(" GB"); if (nrOfBytes > 999) { - nrOfBytes/= 1024; + nrOfBytes /= 1024; unit = _(" TB"); if (nrOfBytes > 999) { - nrOfBytes/= 1024; + nrOfBytes /= 1024; unit = _(" PB"); } } @@ -53,7 +68,7 @@ wxString FreeFileSync::formatFilesizeToShortString(const double filesize) } wxString temp; - if (unit == _(" Byte")) //no decimal places in case of bytes + if (unitByte) //no decimal places in case of bytes { double integer = 0; modf(nrOfBytes, &integer); //get integer part of nrOfBytes @@ -67,9 +82,7 @@ wxString FreeFileSync::formatFilesizeToShortString(const double filesize) temp = globalFunctions::numberToWxString(int(integer)); - int length = temp.Len(); - - switch (length) + switch (temp.length()) { case 0: return _("Error"); @@ -116,22 +129,28 @@ Zstring FreeFileSync::getFormattedDirectoryName(const Zstring& dirname) } -void FreeFileSync::swapGrids(FileCompareResult& grid) +void FreeFileSync::swapGrids(FolderComparison& folderCmp) { - for (FileCompareResult::iterator i = grid.begin(); i != grid.end(); ++i) + for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) { - //swap compare result - if (i->cmpResult == FILE_LEFT_SIDE_ONLY) - i->cmpResult = FILE_RIGHT_SIDE_ONLY; - else if (i->cmpResult == FILE_RIGHT_SIDE_ONLY) - i->cmpResult = FILE_LEFT_SIDE_ONLY; - else if (i->cmpResult == FILE_RIGHT_NEWER) - i->cmpResult = FILE_LEFT_NEWER; - else if (i->cmpResult == FILE_LEFT_NEWER) - i->cmpResult = FILE_RIGHT_NEWER; - - //swap file descriptors - std::swap(i->fileDescrLeft, i->fileDescrRight); + std::swap(j->syncPair.leftDirectory, j->syncPair.rightDirectory); + + FileComparison& fileCmp = j->fileCmp; + for (FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) + { + //swap compare result + if (i->cmpResult == FILE_LEFT_SIDE_ONLY) + i->cmpResult = FILE_RIGHT_SIDE_ONLY; + else if (i->cmpResult == FILE_RIGHT_SIDE_ONLY) + i->cmpResult = FILE_LEFT_SIDE_ONLY; + else if (i->cmpResult == FILE_RIGHT_NEWER) + i->cmpResult = FILE_LEFT_NEWER; + else if (i->cmpResult == FILE_LEFT_NEWER) + i->cmpResult = FILE_RIGHT_NEWER; + + //swap file descriptors + std::swap(i->fileDescrLeft, i->fileDescrRight); + } } } @@ -201,232 +220,40 @@ void FreeFileSync::adjustModificationTimes(const Zstring& parentDirectory, const } */ -void compoundStringToTable(const Zstring& compoundInput, const DefaultChar* delimiter, std::vector<Zstring>& output) -{ - output.clear(); - Zstring input(compoundInput); - - //make sure input ends with delimiter - no problem with empty strings here - if (!input.EndsWith(delimiter)) - input += delimiter; - - unsigned int indexStart = 0; - unsigned int indexEnd = 0; - while ((indexEnd = input.find(delimiter, indexStart)) != Zstring::npos) - { - if (indexStart != indexEnd) //do not add empty strings - { - Zstring newEntry = input.substr(indexStart, indexEnd - indexStart); - - newEntry.Trim(true); //remove whitespace characters from right - newEntry.Trim(false); //remove whitespace characters from left - - if (!newEntry.empty()) - output.push_back(newEntry); - } - indexStart = indexEnd + 1; - } -} - - -inline -void mergeVectors(std::vector<Zstring>& changing, const std::vector<Zstring>& input) -{ - for (std::vector<Zstring>::const_iterator i = input.begin(); i != input.end(); ++i) - changing.push_back(*i); -} - - -std::vector<Zstring> compoundStringToFilter(const Zstring& filterString) -{ - //delimiters may be ';' or '\n' - std::vector<Zstring> filterList; - std::vector<Zstring> filterPreProcessing; - compoundStringToTable(filterString, wxT(";"), filterPreProcessing); - for (std::vector<Zstring>::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i) - { - std::vector<Zstring> newEntries; - compoundStringToTable(*i, wxT("\n"), newEntries); - mergeVectors(filterList, newEntries); - } - - return filterList; -} - - -inline -void formatFilterString(Zstring& filter) -{ -#ifdef FFS_WIN - //Windows does NOT distinguish between upper/lower-case - filter.MakeLower(); -#elif defined FFS_LINUX - //Linux DOES distinguish between upper/lower-case -//nothing to do here -#else - adapt; -#endif -} - - -void FreeFileSync::filterGridData(FileCompareResult& currentGridData, const wxString& includeFilter, const wxString& excludeFilter) +//add(!) all files and subfolder gridlines that are dependent from the directory +void FreeFileSync::addSubElements(const FileComparison& fileCmp, const FileCompareLine& relevantRow, std::set<int>& subElements) { - //no need for regular expressions! In tests wxRegex was by factor of 10 slower than wxString::Matches()!! - - //load filter into vectors of strings - //delimiters may be ';' or '\n' - std::vector<Zstring> includeList = compoundStringToFilter(includeFilter.c_str()); - std::vector<Zstring> excludeList = compoundStringToFilter(excludeFilter.c_str()); - - //format entries - for (std::vector<Zstring>::iterator i = includeList.begin(); i != includeList.end(); ++i) - formatFilterString(*i); - for (std::vector<Zstring>::iterator i = excludeList.begin(); i != excludeList.end(); ++i) - formatFilterString(*i); + const FileDescrLine& relFileDescrLeft = relevantRow.fileDescrLeft; + const FileDescrLine& relFileDescrRight = relevantRow.fileDescrRight; + + Zstring relevantDirectory; + if (relFileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + relevantDirectory = Zstring(relFileDescrLeft.relativeName.c_str(), relFileDescrLeft.relativeName.length()); + else if (relFileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + relevantDirectory = Zstring(relFileDescrRight.relativeName.c_str(), relFileDescrRight.relativeName.length()); + else + return; -//############################################################## + relevantDirectory += GlobalResources::FILE_NAME_SEPARATOR; //FILE_NAME_SEPARATOR needed to exclude subfile/dirs only - //filter currentGridData - for (FileCompareResult::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) + for (FileComparison::const_iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) { - Zstring filenameLeft = i->fileDescrLeft.fullName; - Zstring filenameRight = i->fileDescrRight.fullName; - - formatFilterString(filenameLeft); - formatFilterString(filenameRight); - - //process include filters if (i->fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING) { - bool includedLeft = false; - for (std::vector<Zstring>::const_iterator j = includeList.begin(); j != includeList.end(); ++j) - if (filenameLeft.Matches(*j)) - { - includedLeft = true; - break; - } - - if (!includedLeft) - { - i->selectedForSynchronization = false; - continue; - } + if (i->fileDescrLeft.relativeName.StartsWith(relevantDirectory)) + subElements.insert(i - fileCmp.begin()); } - - if (i->fileDescrRight.objType != FileDescrLine::TYPE_NOTHING) + //"else if": no need to do a redundant check on both sides: relative names should be the same! + else if (i->fileDescrRight.objType != FileDescrLine::TYPE_NOTHING) { - bool includedRight = false; - for (std::vector<Zstring>::const_iterator j = includeList.begin(); j != includeList.end(); ++j) - if (filenameRight.Matches(*j)) - { - includedRight = true; - break; - } - - if (!includedRight) - { - i->selectedForSynchronization = false; - continue; - } - } - - //process exclude filters - if (i->fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING) - { - bool excluded = false; - for (std::vector<Zstring>::const_iterator j = excludeList.begin(); j != excludeList.end(); ++j) - if (filenameLeft.Matches(*j)) - { - excluded = true; - break; - } - - if (excluded) - { - i->selectedForSynchronization = false; - continue; - } - } - - if (i->fileDescrRight.objType != FileDescrLine::TYPE_NOTHING) - { - bool excluded = false; - for (std::vector<Zstring>::const_iterator j = excludeList.begin(); j != excludeList.end(); ++j) - if (filenameRight.Matches(*j)) - { - excluded = true; - break; - } - - if (excluded) - { - i->selectedForSynchronization = false; - continue; - } - } - - i->selectedForSynchronization = true; - } -} - - -void FreeFileSync::includeAllRowsOnGrid(FileCompareResult& currentGridData) -{ - //remove all filters on currentGridData - for (FileCompareResult::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) - i->selectedForSynchronization = true; -} - - -void FreeFileSync::excludeAllRowsOnGrid(FileCompareResult& currentGridData) -{ - //exclude all rows on currentGridData - for (FileCompareResult::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) - i->selectedForSynchronization = false; -} - - -//add(!) all files and subfolder gridlines that are dependent from the directory -template <bool searchLeftSide> -void addSubElementsOneSide(const FileCompareResult& grid, const FileCompareLine& relevantRow, std::set<int>& subElements) -{ - const FileDescrLine* fileDescr = NULL; //get descriptor for file to be deleted; evaluated at compile time - if (searchLeftSide) - fileDescr = &relevantRow.fileDescrLeft; - else - fileDescr = &relevantRow.fileDescrRight; - - if (fileDescr->objType == FileDescrLine::TYPE_DIRECTORY) - { - Zstring relevantDirectory(fileDescr->relativeName.c_str(), fileDescr->relativeName.length()); - relevantDirectory += GlobalResources::FILE_NAME_SEPARATOR; //FILE_NAME_SEPARATOR needed to exclude subfile/dirs only - - for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i) - { - if (searchLeftSide) //evaluated at compile time - { - if (i->fileDescrLeft.relativeName.StartsWith(relevantDirectory)) - subElements.insert(i - grid.begin()); - } - else - { - if (i->fileDescrRight.relativeName.StartsWith(relevantDirectory)) - subElements.insert(i - grid.begin()); - } + if (i->fileDescrRight.relativeName.StartsWith(relevantDirectory)) + subElements.insert(i - fileCmp.begin()); } } } -//add(!) all files and subfolder gridlines that are dependent from the directory -void FreeFileSync::addSubElements(const FileCompareResult& grid, const FileCompareLine& relevantRow, std::set<int>& subElements) -{ - addSubElementsOneSide<true>(grid, relevantRow, subElements); - addSubElementsOneSide<false>(grid, relevantRow, subElements); -} - - //############################################################################################################ struct SortedFileName { @@ -441,7 +268,7 @@ struct SortedFileName //assemble message containing all files to be deleted -wxString FreeFileSync::deleteFromGridAndHDPreview(const FileCompareResult& grid, +wxString FreeFileSync::deleteFromGridAndHDPreview(const FileComparison& fileCmp, const std::set<int>& rowsToDeleteOnLeft, const std::set<int>& rowsToDeleteOnRight, const bool deleteOnBothSides) @@ -456,7 +283,7 @@ wxString FreeFileSync::deleteFromGridAndHDPreview(const FileCompareResult& grid, wxString filesToDelete; for (std::set<int>::const_iterator i = rowsToDelete.begin(); i != rowsToDelete.end(); ++i) { - const FileCompareLine& currentCmpLine = grid[*i]; + const FileCompareLine& currentCmpLine = fileCmp[*i]; if (currentCmpLine.fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING) filesToDelete += (currentCmpLine.fileDescrLeft.fullName + wxT("\n")).c_str(); @@ -476,7 +303,7 @@ wxString FreeFileSync::deleteFromGridAndHDPreview(const FileCompareResult& grid, SortedFileName newEntry; for (std::set<int>::const_iterator i = rowsToDeleteOnLeft.begin(); i != rowsToDeleteOnLeft.end(); ++i) { - const FileCompareLine& currentCmpLine = grid[*i]; + const FileCompareLine& currentCmpLine = fileCmp[*i]; if (currentCmpLine.fileDescrLeft.objType != FileDescrLine::TYPE_NOTHING) { @@ -488,7 +315,7 @@ wxString FreeFileSync::deleteFromGridAndHDPreview(const FileCompareResult& grid, for (std::set<int>::const_iterator i = rowsToDeleteOnRight.begin(); i != rowsToDeleteOnRight.end(); ++i) { - const FileCompareLine& currentCmpLine = grid[*i]; + const FileCompareLine& currentCmpLine = fileCmp[*i]; if (currentCmpLine.fileDescrRight.objType != FileDescrLine::TYPE_NOTHING) { @@ -510,8 +337,8 @@ wxString FreeFileSync::deleteFromGridAndHDPreview(const FileCompareResult& grid, class RemoveAtExit //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown! { public: - RemoveAtExit(FileCompareResult& grid) : - gridToWrite(grid) {} + RemoveAtExit(FileComparison& fileCmp) : + gridToWrite(fileCmp) {} ~RemoveAtExit() { @@ -524,7 +351,7 @@ public: } private: - FileCompareResult& gridToWrite; + FileComparison& gridToWrite; std::set<int> rowsProcessed; }; @@ -533,8 +360,8 @@ template <bool leftSide> //update compareGrid row information after deletion fro inline void updateCmpLineAfterDeletion(FileCompareLine& relevantRow, const int rowNr, RemoveAtExit& markForRemoval) { - FileDescrLine* fileDescr = NULL; //get descriptor for file to be deleted; evaluated at compile time - FileDescrLine* fileDescrPartner = NULL; //file descriptor for "other side" + FileDescrLine* fileDescr; //get descriptor for file to be deleted; evaluated at compile time + FileDescrLine* fileDescrPartner; //file descriptor for "other side" if (leftSide) { fileDescr = &relevantRow.fileDescrLeft; @@ -553,9 +380,7 @@ void updateCmpLineAfterDeletion(FileCompareLine& relevantRow, const int rowNr, R else { //initialize fileDescr for deleted file/folder - const Zstring saveDir = fileDescr->directory; *fileDescr = FileDescrLine(); - fileDescr->directory = saveDir; //adapt the compare result if (leftSide) //again evaluated at compile time @@ -567,7 +392,7 @@ void updateCmpLineAfterDeletion(FileCompareLine& relevantRow, const int rowNr, R template <bool leftSide> -void deleteFromGridAndHDOneSide(FileCompareResult& grid, +void deleteFromGridAndHDOneSide(FileComparison& fileCmp, const std::set<int>& rowsToDeleteOneSide, const bool useRecycleBin, RemoveAtExit& markForRemoval, @@ -575,7 +400,7 @@ void deleteFromGridAndHDOneSide(FileCompareResult& grid, { for (std::set<int>::iterator i = rowsToDeleteOneSide.begin(); i != rowsToDeleteOneSide.end(); ++i) { - FileCompareLine& currentCmpLine = grid[*i]; + FileCompareLine& currentCmpLine = fileCmp[*i]; FileDescrLine* fileDescr = NULL; //get descriptor for file to be deleted; evaluated at compile time if (leftSide) @@ -602,17 +427,16 @@ void deleteFromGridAndHDOneSide(FileCompareResult& grid, //retrieve all files and subfolder gridlines that are dependent from this deleted entry std::set<int> rowsToDelete; rowsToDelete.insert(*i); - addSubElementsOneSide<leftSide>(grid, grid[*i], rowsToDelete); + FreeFileSync::addSubElements(fileCmp, currentCmpLine, rowsToDelete); - //remove deleted entries from grid (or adapt it if deleted from one side only) + //remove deleted entries from fileCmp (or adapt it if deleted from one side only) for (std::set<int>::iterator j = rowsToDelete.begin(); j != rowsToDelete.end(); ++j) - updateCmpLineAfterDeletion<leftSide>(grid[*j], *j, markForRemoval); + updateCmpLineAfterDeletion<leftSide>(fileCmp[*j], *j, markForRemoval); break; } catch (const FileError& error) { - //if (updateClass) -> is mandatory ErrorHandler::Response rv = errorHandler->reportError(error.show()); if (rv == ErrorHandler::IGNORE_ERROR) @@ -628,15 +452,15 @@ void deleteFromGridAndHDOneSide(FileCompareResult& grid, } -void FreeFileSync::deleteFromGridAndHD(FileCompareResult& grid, +void FreeFileSync::deleteFromGridAndHD(FileComparison& fileCmp, const std::set<int>& rowsToDeleteOnLeft, const std::set<int>& rowsToDeleteOnRight, const bool deleteOnBothSides, const bool useRecycleBin, ErrorHandler* errorHandler) { - //remove deleted rows from grid (AFTER all rows to be deleted are known: consider row references! - RemoveAtExit markForRemoval(grid); //ensure that grid is always written to, even if method is exitted via exceptions + //remove deleted rows from fileCmp (AFTER all rows to be deleted are known: consider row references! + RemoveAtExit markForRemoval(fileCmp); //ensure that fileCmp is always written to, even if method is exitted via exceptions if (deleteOnBothSides) { @@ -645,13 +469,13 @@ void FreeFileSync::deleteFromGridAndHD(FileCompareResult& grid, for (std::set<int>::const_iterator i = rowsToDeleteOnRight.begin(); i != rowsToDeleteOnRight.end(); ++i) rowsToDeleteBothSides.insert(*i); - deleteFromGridAndHDOneSide<true>(grid, + deleteFromGridAndHDOneSide<true>(fileCmp, rowsToDeleteBothSides, useRecycleBin, markForRemoval, errorHandler); - deleteFromGridAndHDOneSide<false>(grid, + deleteFromGridAndHDOneSide<false>(fileCmp, rowsToDeleteBothSides, useRecycleBin, markForRemoval, @@ -659,13 +483,13 @@ void FreeFileSync::deleteFromGridAndHD(FileCompareResult& grid, } else { - deleteFromGridAndHDOneSide<true>(grid, + deleteFromGridAndHDOneSide<true>(fileCmp, rowsToDeleteOnLeft, useRecycleBin, markForRemoval, errorHandler); - deleteFromGridAndHDOneSide<false>(grid, + deleteFromGridAndHDOneSide<false>(fileCmp, rowsToDeleteOnRight, useRecycleBin, markForRemoval, diff --git a/algorithm.h b/algorithm.h index e198906c..9cf1e8ad 100644 --- a/algorithm.h +++ b/algorithm.h @@ -1,44 +1,45 @@ #ifndef ALGORITHM_H_INCLUDED #define ALGORITHM_H_INCLUDED -#include "FreeFileSync.h" -#include "library/statusHandler.h" +#include "structures.h" #include "library/resources.h" +class ErrorHandler; + namespace FreeFileSync { + wxString formatFilesizeToShortString(const wxLongLong& filesize); wxString formatFilesizeToShortString(const wxULongLong& filesize); wxString formatFilesizeToShortString(const double filesize); + Zstring getFormattedDirectoryName(const Zstring& dirname); bool endsWithPathSeparator(const Zstring& name); - void swapGrids(FileCompareResult& grid); + void swapGrids(FolderComparison& folderCmp); - void addSubElements(const FileCompareResult& grid, const FileCompareLine& relevantRow, std::set<int>& subElements); + void addSubElements(const FileComparison& fileCmp, const FileCompareLine& relevantRow, std::set<int>& subElements); - //manual deletion of files on main grid - wxString deleteFromGridAndHDPreview(const FileCompareResult& grid, + //manual deletion of files on main grid: runs at individual directory pair level + wxString deleteFromGridAndHDPreview(const FileComparison& fileCmp, const std::set<int>& rowsToDeleteOnLeft, const std::set<int>& rowsToDeleteOnRight, const bool deleteOnBothSides); - void deleteFromGridAndHD(FileCompareResult& grid, + void deleteFromGridAndHD(FileComparison& fileCmp, const std::set<int>& rowsToDeleteOnLeft, const std::set<int>& rowsToDeleteOnRight, const bool deleteOnBothSides, const bool useRecycleBin, ErrorHandler* errorHandler); - void filterGridData(FileCompareResult& currentGridData, const wxString& includeFilter, const wxString& excludeFilter); - void includeAllRowsOnGrid(FileCompareResult& currentGridData); - void excludeAllRowsOnGrid(FileCompareResult& currentGridData); wxString utcTimeToLocalString(const wxLongLong& utcTime); //enhanced binary search template: returns an iterator template <class ForwardIterator, class T> + inline ForwardIterator custom_binary_search (ForwardIterator first, ForwardIterator last, const T& value) { first = lower_bound(first, last, value); diff --git a/comparison.cpp b/comparison.cpp index 43191d38..8a092935 100644 --- a/comparison.cpp +++ b/comparison.cpp @@ -5,10 +5,11 @@ #include <wx/ffile.h> #include <wx/msgdlg.h> #include <wx/log.h> -#include "library/multithreading.h" #include "algorithm.h" #include <wx/thread.h> #include <memory> +#include "library/statusHandler.h" +#include "library/fileHandling.h" #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -20,13 +21,13 @@ using namespace FreeFileSync; class GetAllFilesFull : public FullDetailFileTraverser { public: - GetAllFilesFull(DirectoryDescrType& output, Zstring dirThatIsSearched, StatusHandler* handler) : + GetAllFilesFull(DirectoryDescrType& output, const Zstring& dirThatIsSearched, StatusHandler* handler) : m_output(output), directory(dirThatIsSearched), + textScanning(Zstring(_("Scanning:")) + wxT(" \n")), statusHandler(handler) { prefixLength = directory.length(); - textScanning = Zstring(_("Scanning:")) + wxT(" \n"); //performance optimization } @@ -34,7 +35,7 @@ public: void writeText(const wxChar* text, const int length, wxChar*& currentPos) { memcpy(currentPos, text, length * sizeof(wxChar)); - currentPos+=length; + currentPos += length; } @@ -42,7 +43,6 @@ public: { FileDescrLine fileDescr; fileDescr.fullName = fullFileName; - fileDescr.directory = directory; fileDescr.relativeName = fullFileName.zsubstr(prefixLength); fileDescr.lastWriteTimeRaw = details.lastWriteTimeRaw; fileDescr.fileSize = details.fileSize; @@ -83,10 +83,9 @@ public: FileDescrLine fileDescr; fileDescr.fullName = fullDirName; - fileDescr.directory = directory; fileDescr.relativeName = fullDirName.zsubstr(prefixLength); fileDescr.lastWriteTimeRaw = 0; //irrelevant for directories - fileDescr.fileSize = wxULongLong(0); //currently used by getBytesToTransfer + fileDescr.fileSize = 0; //currently used by getBytesToTransfer fileDescr.objType = FileDescrLine::TYPE_DIRECTORY; m_output.push_back(fileDescr); @@ -137,7 +136,7 @@ private: DirectoryDescrType& m_output; Zstring directory; int prefixLength; - Zstring textScanning; + const Zstring textScanning; StatusHandler* statusHandler; }; @@ -158,7 +157,7 @@ struct DescrBufferLine }; -class FreeFileSync::DirectoryDescrBuffer //buffer multiple scans of the same directories +class DirectoryDescrBuffer //buffer multiple scans of the same directories { public: DirectoryDescrBuffer(const bool traverseDirectorySymlinks, StatusHandler* statusUpdater) : @@ -284,10 +283,9 @@ CompareProcess::CompareProcess(const bool traverseSymLinks, fileTimeTolerance(fileTimeTol), m_warningDependentFolders(warningDependentFolders), statusUpdater(handler), - txtComparingContentOfFiles(_("Comparing content of files %x")) + txtComparingContentOfFiles(Zstring(_("Comparing content of files %x")).Replace(wxT("%x"), wxT("\n\"%x\""), false)) { descriptionBuffer = new DirectoryDescrBuffer(traverseSymLinks, handler); - txtComparingContentOfFiles.Replace(wxT("%x"), wxT("\n\"%x\""), false); } @@ -321,19 +319,19 @@ struct MemoryAllocator struct CallBackData { StatusHandler* handler; - wxULongLong bytesComparedLast; + wxLongLong bytesComparedLast; }; //callback function for status updates whily comparing -typedef void (*CompareCallback)(const wxULongLong&, void*); +typedef void (*CompareCallback)(const wxLongLong&, void*); -void compareContentCallback(const wxULongLong& totalBytesTransferred, void* data) +void compareContentCallback(const wxLongLong& totalBytesTransferred, void* data) { //called every 512 kB CallBackData* sharedData = static_cast<CallBackData*>(data); //inform about the (differential) processed amount of data - sharedData->handler->updateProcessedData(0, (totalBytesTransferred - sharedData->bytesComparedLast).ToDouble()); + sharedData->handler->updateProcessedData(0, totalBytesTransferred - sharedData->bytesComparedLast); sharedData->bytesComparedLast = totalBytesTransferred; sharedData->handler->requestUiRefresh(); //exceptions may be thrown here! @@ -352,13 +350,13 @@ bool filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, Co if (!file2.IsOpened()) //NO cleanup necessary for (wxFFile) file1 throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + filename2 + wxT("\"")); - wxULongLong bytesCompared; + wxLongLong bytesCompared; do { - const size_t length1 = file1.Read(memory.buffer1, memory.bufferSize); + const size_t length1 = file1.Read(memory.buffer1, MemoryAllocator::bufferSize); if (file1.Error()) throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + filename1 + wxT("\"")); - const size_t length2 = file2.Read(memory.buffer2, memory.bufferSize); + const size_t length2 = file2.Read(memory.buffer2, MemoryAllocator::bufferSize); if (file2.Error()) throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + filename2 + wxT("\"")); if (length1 != length2 || memcmp(memory.buffer1, memory.buffer2, length1) != 0) @@ -392,13 +390,13 @@ bool filesHaveSameContentUpdating(const Zstring& filename1, const Zstring& filen catch (...) { //error situation: undo communication of processed amount of data - handler->updateProcessedData(0, sharedData.bytesComparedLast.ToDouble() * -1 ); + handler->updateProcessedData(0, sharedData.bytesComparedLast * -1); throw; } //inform about the (remaining) processed amount of data - handler->updateProcessedData(0, (totalBytesToCmp - sharedData.bytesComparedLast).ToDouble()); + handler->updateProcessedData(0, globalFunctions::convertToSigned(totalBytesToCmp) - sharedData.bytesComparedLast); return sameContent; } @@ -459,22 +457,6 @@ bool filesHaveSameContentUpdating(const Zstring& filename1, const Zstring& filen }*/ -void getBytesToCompare(int& objectsTotal, double& dataTotal, const FileCompareResult& grid, const std::set<int>& rowsToCompare) -{ - dataTotal = 0; - - for (std::set<int>::iterator i = rowsToCompare.begin(); i != rowsToCompare.end(); ++i) - { - const FileCompareLine& gridline = grid[*i]; - - dataTotal+= gridline.fileDescrLeft.fileSize.ToDouble(); - dataTotal+= gridline.fileDescrRight.fileSize.ToDouble(); - } - - objectsTotal = rowsToCompare.size() * 2; -} - - inline bool sameFileTime(const wxLongLong& a, const wxLongLong& b, const unsigned tolerance) { @@ -487,7 +469,7 @@ bool sameFileTime(const wxLongLong& a, const wxLongLong& b, const unsigned toler void CompareProcess::startCompareProcess(const std::vector<FolderPair>& directoryPairs, const CompareVariant cmpVar, - FileCompareResult& output) throw(AbortThisProcess) + FolderComparison& output) { #ifndef __WXDEBUG__ wxLogNull noWxLogs; //hide wxWidgets log messages in release build @@ -495,16 +477,12 @@ void CompareProcess::startCompareProcess(const std::vector<FolderPair>& director //PERF_START; - //format directory pairs + //format directory pairs: ensure they end with GlobalResources::FILE_NAME_SEPARATOR! std::vector<FolderPair> directoryPairsFormatted; - for (std::vector<FolderPair>::const_iterator i = directoryPairs.begin(); i != directoryPairs.end(); ++i) - { - FolderPair newEntry; - newEntry.leftDirectory = FreeFileSync::getFormattedDirectoryName(i->leftDirectory); - newEntry.rightDirectory = FreeFileSync::getFormattedDirectoryName(i->rightDirectory); - directoryPairsFormatted.push_back(newEntry); - } + directoryPairsFormatted.push_back( + FolderPair(FreeFileSync::getFormattedDirectoryName(i->leftDirectory), + FreeFileSync::getFormattedDirectoryName(i->rightDirectory))); //some basic checks: @@ -531,12 +509,16 @@ void CompareProcess::startCompareProcess(const std::vector<FolderPair>& director try { - FileCompareResult output_tmp; //write to output not before END of process! - if (cmpVar == CMP_BY_TIME_SIZE) + FolderComparison output_tmp; //write to output not before END of process! + switch (cmpVar) + { + case CMP_BY_TIME_SIZE: compareByTimeSize(directoryPairsFormatted, output_tmp); - else if (cmpVar == CMP_BY_CONTENT) + break; + case CMP_BY_CONTENT: compareByContent(directoryPairsFormatted, output_tmp); - else assert(false); + break; + } //only if everything was processed correctly output is written to! output mustn't change to be in sync with GUI grid view!!! output_tmp.swap(output); @@ -554,21 +536,25 @@ void CompareProcess::startCompareProcess(const std::vector<FolderPair>& director } -void CompareProcess::compareByTimeSize(const std::vector<FolderPair>& directoryPairsFormatted, FileCompareResult& output) +void CompareProcess::compareByTimeSize(const std::vector<FolderPair>& directoryPairsFormatted, FolderComparison& output) { //inform about the total amount of data that will be processed from now on statusUpdater->initNewProcess(-1, 0, StatusHandler::PROCESS_SCANNING); //it's not known how many files will be scanned => -1 objects //process one folder pair after each other - unsigned tableSizeOld = 0; for (std::vector<FolderPair>::const_iterator pair = directoryPairsFormatted.begin(); pair != directoryPairsFormatted.end(); ++pair) { + FolderCompareLine newEntry; + newEntry.syncPair = *pair; + output.push_back(newEntry); //attention: push_back() copies by value!!! performance: append BEFORE writing values into fileCmp! + + FileComparison& fileCmp = output.back().fileCmp; + //do basis scan: only result lines of type FILE_UNDEFINED (files that exist on both sides) need to be determined after this call - this->performBaseComparison(*pair, output); + this->performBaseComparison(*pair, fileCmp); //categorize files that exist on both sides - for (FileCompareResult::iterator i = output.begin() + tableSizeOld; i != output.end(); ++i) - { + for (FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) if (i->cmpResult == FILE_UNDEFINED) { //last write time may differ by up to 2 seconds (NTFS vs FAT32) @@ -587,14 +573,55 @@ void CompareProcess::compareByTimeSize(const std::vector<FolderPair>& directoryP i->cmpResult = FILE_LEFT_NEWER; } } + } +} + + +class RemoveAtExit //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown! +{ +public: + RemoveAtExit(FileComparison& fileCmp) : + gridToWrite(fileCmp) {} + + ~RemoveAtExit() + { + removeRowsFromVector(gridToWrite, rowsToDelete); + } + + void markRow(int nr) + { + rowsToDelete.insert(nr); + } + +private: + FileComparison& gridToWrite; + std::set<int> rowsToDelete; +}; + + +void getBytesToCompare(const FolderComparison& grid, const FolderCompRef& rowsToCompare, int& objectsTotal, wxULongLong& dataTotal) +{ + objectsTotal = 0; + dataTotal = 0; + + for (FolderComparison::const_iterator j = grid.begin(); j != grid.end(); ++j) + { + const FileComparison& fileCmp = j->fileCmp; + + const std::set<int>& index = rowsToCompare[j - grid.begin()]; + for (std::set<int>::iterator i = index.begin(); i != index.end(); ++i) + { + const FileCompareLine& line = fileCmp[*i]; + dataTotal += line.fileDescrLeft.fileSize; + dataTotal += line.fileDescrRight.fileSize; } - tableSizeOld = output.size(); + objectsTotal += index.size() * 2; } } -void CompareProcess::compareByContent(const std::vector<FolderPair>& directoryPairsFormatted, FileCompareResult& output) +void CompareProcess::compareByContent(const std::vector<FolderPair>& directoryPairsFormatted, FolderComparison& output) { //PERF_START; @@ -604,78 +631,100 @@ void CompareProcess::compareByContent(const std::vector<FolderPair>& directoryPa //process one folder pair after each other for (std::vector<FolderPair>::const_iterator pair = directoryPairsFormatted.begin(); pair != directoryPairsFormatted.end(); ++pair) { + FolderCompareLine newEntry; + newEntry.syncPair = *pair; + output.push_back(newEntry); //attention: push_back() copies by value!!! performance: append BEFORE writing values into fileCmp! + + FileComparison& fileCmp = output.back().fileCmp; + //do basis scan: only result lines of type FILE_UNDEFINED (files that exist on both sides) need to be determined after this call - this->performBaseComparison(*pair, output); + this->performBaseComparison(*pair, fileCmp); } + //finish categorization... - std::set<int> rowsToCompareBytewise; //content comparison of file content happens AFTER finding corresponding files + FolderCompRef rowsToCompareBytewise; //content comparison of file content happens AFTER finding corresponding files //in order to separate into two processes (scanning and comparing) - //pre-check: files have different content if they have a different filesize - for (FileCompareResult::iterator i = output.begin(); i != output.end(); ++i) + for (FolderComparison::iterator j = output.begin(); j != output.end(); ++j) { - if (i->cmpResult == FILE_UNDEFINED) + FileComparison& fileCmp = j->fileCmp; + + std::set<int> newEntry; + + for (FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) { - if (i->fileDescrLeft.fileSize != i->fileDescrRight.fileSize) - i->cmpResult = FILE_DIFFERENT; - else - rowsToCompareBytewise.insert(i - output.begin()); + if (i->cmpResult == FILE_UNDEFINED) + { //pre-check: files have different content if they have a different filesize + if (i->fileDescrLeft.fileSize != i->fileDescrRight.fileSize) + i->cmpResult = FILE_DIFFERENT; + else + newEntry.insert(i - fileCmp.begin()); + } } + rowsToCompareBytewise.push_back(newEntry); } int objectsTotal = 0; - double dataTotal = 0; - getBytesToCompare(objectsTotal, dataTotal, output, rowsToCompareBytewise); + wxULongLong dataTotal; + getBytesToCompare(output, rowsToCompareBytewise, objectsTotal, dataTotal); - statusUpdater->initNewProcess(objectsTotal, dataTotal, StatusHandler::PROCESS_COMPARING_CONTENT); - - std::set<int> rowsToDelete; //if errors occur during file access and user skips, these rows need to be deleted from result + statusUpdater->initNewProcess(objectsTotal, + globalFunctions::convertToSigned(dataTotal), + StatusHandler::PROCESS_COMPARING_CONTENT); //compare files (that have same size) bytewise... - for (std::set<int>::iterator i = rowsToCompareBytewise.begin(); i != rowsToCompareBytewise.end(); ++i) + for (FolderComparison::iterator j = output.begin(); j != output.end(); ++j) { - FileCompareLine& gridline = output[*i]; + FileComparison& fileCmp = j->fileCmp; - Zstring statusText = txtComparingContentOfFiles; - statusText.Replace(wxT("%x"), gridline.fileDescrLeft.relativeName.c_str(), false); - statusUpdater->updateStatusText(statusText); + //mark erroneous rows for deletion from output + RemoveAtExit removeRowsAtExit(fileCmp); //note: running at individual folder pair level! - //check files that exist in left and right model but have different content - while (true) + const std::set<int>& index = rowsToCompareBytewise[j - output.begin()]; + for (std::set<int>::iterator i = index.begin(); i != index.end(); ++i) { - //trigger display refresh - statusUpdater->requestUiRefresh(); + FileCompareLine& gridline = fileCmp[*i]; - try - { - if (filesHaveSameContentUpdating(gridline.fileDescrLeft.fullName, gridline.fileDescrRight.fullName, gridline.fileDescrLeft.fileSize * 2, statusUpdater)) - gridline.cmpResult = FILE_EQUAL; - else - gridline.cmpResult = FILE_DIFFERENT; + Zstring statusText = txtComparingContentOfFiles; + statusText.Replace(wxT("%x"), gridline.fileDescrLeft.relativeName.c_str(), false); + statusUpdater->updateStatusText(statusText); - statusUpdater->updateProcessedData(2, 0); //processed data is communicated in subfunctions! - break; - } - catch (FileError& error) + //check files that exist in left and right model but have different content + while (true) { - ErrorHandler::Response rv = statusUpdater->reportError(error.show()); - if (rv == ErrorHandler::IGNORE_ERROR) + //trigger display refresh + statusUpdater->requestUiRefresh(); + + try { - rowsToDelete.insert(*i); + if (filesHaveSameContentUpdating(gridline.fileDescrLeft.fullName, + gridline.fileDescrRight.fullName, + gridline.fileDescrLeft.fileSize * 2, + statusUpdater)) + gridline.cmpResult = FILE_EQUAL; + else + gridline.cmpResult = FILE_DIFFERENT; + + statusUpdater->updateProcessedData(2, 0); //processed data is communicated in subfunctions! break; } - else if (rv == ErrorHandler::RETRY) - ; //continue with loop - else - assert (false); + catch (FileError& error) + { + ErrorHandler::Response rv = statusUpdater->reportError(error.show()); + if (rv == ErrorHandler::IGNORE_ERROR) + { + removeRowsAtExit.markRow(*i); + break; + } + else if (rv == ErrorHandler::RETRY) + ; //continue with loop + else + assert (false); + } } } } - - //delete invalid rows that have no valid cmpResult - if (rowsToDelete.size() > 0) - removeRowsFromVector(output, rowsToDelete); } @@ -704,7 +753,7 @@ private: }; -void CompareProcess::performBaseComparison(const FolderPair& pair, FileCompareResult& output) +void CompareProcess::performBaseComparison(const FolderPair& pair, FileComparison& output) { //PERF_START; //retrieve sets of files (with description data) @@ -759,7 +808,6 @@ void CompareProcess::performBaseComparison(const FolderPair& pair, FileCompareRe { newline.fileDescrLeft = *i; newline.fileDescrRight = FileDescrLine(); - newline.fileDescrRight.directory = pair.rightDirectory; newline.cmpResult = FILE_LEFT_SIDE_ONLY; output.push_back(newline); } @@ -792,12 +840,10 @@ void CompareProcess::performBaseComparison(const FolderPair& pair, FileCompareRe newline.fileDescrLeft = *i; newline.fileDescrRight = FileDescrLine(); - newline.fileDescrRight.directory = pair.rightDirectory; newline.cmpResult = FILE_LEFT_SIDE_ONLY; output.push_back(newline); newline.fileDescrLeft = FileDescrLine(); - newline.fileDescrLeft.directory = pair.leftDirectory; newline.fileDescrRight = *j; newline.cmpResult = FILE_RIGHT_SIDE_ONLY; output.push_back(newline); @@ -812,7 +858,6 @@ void CompareProcess::performBaseComparison(const FolderPair& pair, FileCompareRe if (custom_binary_search(directoryLeft->begin(), directoryLeft->end(), *j) == directoryLeft->end()) { newline.fileDescrLeft = FileDescrLine(); - newline.fileDescrLeft.directory = pair.leftDirectory; //directory info is needed when creating new directories newline.fileDescrRight = *j; newline.cmpResult = FILE_RIGHT_SIDE_ONLY; output.push_back(newline); diff --git a/comparison.h b/comparison.h index 204ff7ec..0f4681e5 100644 --- a/comparison.h +++ b/comparison.h @@ -1,13 +1,14 @@ #ifndef COMPARISON_H_INCLUDED #define COMPARISON_H_INCLUDED -#include "FreeFileSync.h" -#include "library/statusHandler.h" +#include "structures.h" + +class StatusHandler; +class DirectoryDescrBuffer; + namespace FreeFileSync { - class DirectoryDescrBuffer; - //class handling comparison process class CompareProcess { @@ -21,15 +22,15 @@ namespace FreeFileSync void startCompareProcess(const std::vector<FolderPair>& directoryPairs, const CompareVariant cmpVar, - FileCompareResult& output) throw(AbortThisProcess); + FolderComparison& output); private: - void compareByTimeSize(const std::vector<FolderPair>& directoryPairsFormatted, FileCompareResult& output); + void compareByTimeSize(const std::vector<FolderPair>& directoryPairsFormatted, FolderComparison& output); - void compareByContent(const std::vector<FolderPair>& directoryPairsFormatted, FileCompareResult& output); + void compareByContent(const std::vector<FolderPair>& directoryPairsFormatted, FolderComparison& output); //create comparison result table and fill relation except for files existing on both sides - void performBaseComparison(const FolderPair& pair, FileCompareResult& output); + void performBaseComparison(const FolderPair& pair, FileComparison& output); //buffer accesses to the same directories; useful when multiple folder pairs are used DirectoryDescrBuffer* descriptionBuffer; @@ -39,7 +40,7 @@ namespace FreeFileSync bool& m_warningDependentFolders; StatusHandler* statusUpdater; - Zstring txtComparingContentOfFiles; + const Zstring txtComparingContentOfFiles; }; } diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp index b8737343..faa75b2d 100644 --- a/library/CustomGrid.cpp +++ b/library/CustomGrid.cpp @@ -5,39 +5,58 @@ #include "../algorithm.h" #include "resources.h" #include <typeinfo> +#include "../ui/gridView.h" #ifdef FFS_WIN #include <wx/icon.h> #include <wx/msw/wrapwin.h> //includes "windows.h" -#endif // FFS_WIN + +#elif defined FFS_LINUX +#include <gtk/gtk.h> +#endif + + +using namespace FreeFileSync; const unsigned int MIN_ROW_COUNT = 15; //class containing pure grid data: basically the same as wxGridStringTable, but adds cell formatting + +/* +class hierarchy: + CustomGridTable + /|\ + ________________|________________ + | | + CustomGridTableRim | + /|\ | + __________|__________ | + | | | +CustomGridTableLeft CustomGridTableRight CustomGridTableMiddle +*/ + class CustomGridTable : public wxGridTableBase { public: - CustomGridTable() : + CustomGridTable(int initialRows = 0, int initialCols = 0) : //note: initialRows/initialCols MUST match with GetNumberRows()/GetNumberCols() at initialization!!! wxGridTableBase(), - blue(80, 110, 255), - grey(212, 208, 200), - lightRed(235, 57, 61), - lightBlue(63, 206, 233), - lightGreen(54, 218, 2), - gridRefUI(NULL), - gridData(NULL), - lastNrRows(0), - lastNrCols(0) {} + COLOR_BLUE( 80, 110, 255), + COLOR_GREY( 212, 208, 200), + COLOR_LIGHT_RED( 235, 57, 61), + COLOR_LIGHT_BLUE( 63, 206, 233), + COLOR_LIGHT_GREEN(54, 218, 2), + gridDataView(NULL), + lastNrRows(initialRows), + lastNrCols(initialCols) {} virtual ~CustomGridTable() {} - void setGridDataTable(GridView* gridRefUI, FileCompareResult* gridData) + void setGridDataTable(GridView* gridDataView) { - this->gridRefUI = gridRefUI; - this->gridData = gridData; + this->gridDataView = gridDataView; } @@ -46,57 +65,27 @@ public: virtual int GetNumberRows() { - if (gridRefUI) - return std::max(gridRefUI->size(), MIN_ROW_COUNT); + if (gridDataView) + return std::max(gridDataView->elementsOnView(), MIN_ROW_COUNT); else return 0; //grid is initialized with zero number of rows } - virtual int GetNumberCols() //virtual used by middle grid! - { - return columnPositions.size(); - } - - virtual bool IsEmptyCell( int row, int col ) { - return (GetValue(row, col) == wxEmptyString); - } - - - virtual wxString GetValue(int row, int col) = 0; - - - virtual void SetValue(int row, int col, const wxString& value) - { - assert (false); //should not be used, since values are retrieved directly from gridRefUI - } - - virtual void Clear() - { - assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} - } + return false; //avoid overlapping cells - virtual bool InsertRows(size_t pos = 0, size_t numRows = 1) - { - assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} - return true; + //return (GetValue(row, col) == wxEmptyString); } - virtual bool AppendRows(size_t numRows = 1) - { - assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} - return true; - } - virtual bool DeleteRows(size_t pos = 0, size_t numRows = 1) + virtual void SetValue(int row, int col, const wxString& value) { - assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} - return true; + assert (false); //should not be used, since values are retrieved directly from gridDataView } - //update dimensions of grid: no need for InsertRows, AppendRows, DeleteRows anymore!!! + //update dimensions of grid: no need for InsertRows(), AppendRows(), DeleteRows() anymore!!! void updateGridSizes() { const int currentNrRows = GetNumberRows(); @@ -156,12 +145,6 @@ public: //########################################################################### - virtual wxString GetColLabelValue( int col ) - { - return CustomGrid::getTypeName(getTypeAtPos(col)); - } - - virtual wxGridCellAttr* GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) { const wxColour& color = getRowColor(row); @@ -190,6 +173,46 @@ public: } + const FileCompareLine* getRawData(const unsigned int row) const + { + if (gridDataView && row < gridDataView->elementsOnView()) + { + return &(*gridDataView)[row]; + } + return NULL; + } + +protected: + const wxColour COLOR_BLUE; + const wxColour COLOR_GREY; + const wxColour COLOR_LIGHT_RED; + const wxColour COLOR_LIGHT_BLUE; + const wxColour COLOR_LIGHT_GREEN; + + const GridView* gridDataView; //(very fast) access to underlying grid data :) + +private: + virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color + + int lastNrRows; + int lastNrCols; +}; + + +class CustomGridTableRim : public CustomGridTable +{ +public: + virtual int GetNumberCols() + { + return columnPositions.size(); + } + + virtual wxString GetColLabelValue( int col ) + { + return CustomGridRim::getTypeName(getTypeAtPos(col)); + } + + void setupColumns(const std::vector<xmlAccess::ColumnTypes>& positions) { columnPositions = positions; @@ -206,269 +229,225 @@ public: } - const FileCompareLine* getRawData(const unsigned int row) - { - if (gridRefUI && row < gridRefUI->size()) - { - const FileCompareLine& cmpLine = (*gridData)[(*gridRefUI)[row]]; - return &cmpLine; - } - return NULL; - } - - -protected: - virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color - +private: std::vector<xmlAccess::ColumnTypes> columnPositions; - - wxColour blue; - wxColour grey; - wxColour lightRed; - wxColour lightBlue; - wxColour lightGreen; - GridView* gridRefUI; //(very fast) access to underlying grid data :) - FileCompareResult* gridData; - int lastNrRows; - int lastNrCols; }; -class CustomGridTableLeft : public CustomGridTable +class CustomGridTableLeft : public CustomGridTableRim { public: - CustomGridTableLeft() : CustomGridTable() {} - ~CustomGridTableLeft() {} + virtual wxString GetValue(int row, int col) + { + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) + { + if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + { + switch (getTypeAtPos(col)) + { + case xmlAccess::FULL_NAME: + return gridLine->fileDescrLeft.fullName.c_str(); + case xmlAccess::FILENAME: + return wxEmptyString; + case xmlAccess::REL_PATH: + return gridLine->fileDescrLeft.relativeName.c_str(); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).leftDirectory.c_str(); + case xmlAccess::SIZE: //file size + return _("<Directory>"); + case xmlAccess::DATE: //date + return wxEmptyString; + } + } + else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + { + switch (getTypeAtPos(col)) + { + case xmlAccess::FULL_NAME: + return gridLine->fileDescrLeft.fullName.c_str(); + case xmlAccess::FILENAME: //filename + return wxString(gridLine->fileDescrLeft.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::REL_PATH: //relative path + return wxString(gridLine->fileDescrLeft.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).leftDirectory.c_str(); + case xmlAccess::SIZE: //file size + return globalFunctions::includeNumberSeparator(gridLine->fileDescrLeft.fileSize.ToString()); + case xmlAccess::DATE: //date + return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrLeft.lastWriteTimeRaw); + } + } + } + //if data is not found: + return wxEmptyString; + } +private: virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color { - if (gridRefUI && unsigned(row) < gridRefUI->size()) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; - //mark filtered rows - if (!cmpLine.selectedForSynchronization) - return blue; + if (!gridLine->selectedForSynchronization) + return COLOR_BLUE; //mark directories - else if (cmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - return grey; + else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + return COLOR_GREY; else return *wxWHITE; } return *wxWHITE; } +}; - //virtual impl. - wxString GetValue(int row, int col) +class CustomGridTableRight : public CustomGridTableRim +{ +public: + virtual wxString GetValue(int row, int col) { - if (gridRefUI) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - if (unsigned(row) < gridRefUI->size()) + if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) { - const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; - - if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + switch (getTypeAtPos(col)) { - switch (getTypeAtPos(col)) - { - case xmlAccess::FULL_NAME: - return gridLine.fileDescrLeft.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxEmptyString; - case xmlAccess::REL_PATH: //relative path - return gridLine.fileDescrLeft.relativeName.c_str(); - case xmlAccess::SIZE: //file size - return _("<Directory>"); - case xmlAccess::DATE: //date - return wxEmptyString; - } + case xmlAccess::FULL_NAME: + return gridLine->fileDescrRight.fullName.c_str(); + case xmlAccess::FILENAME: //filename + return wxEmptyString; + case xmlAccess::REL_PATH: //relative path + return gridLine->fileDescrRight.relativeName.c_str(); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).rightDirectory.c_str(); + case xmlAccess::SIZE: //file size + return _("<Directory>"); + case xmlAccess::DATE: //date + return wxEmptyString; } - else if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + } + else if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_FILE) + { + switch (getTypeAtPos(col)) { - switch (getTypeAtPos(col)) - { - case xmlAccess::FULL_NAME: - return gridLine.fileDescrLeft.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxString(gridLine.fileDescrLeft.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::REL_PATH: //relative path - return wxString(gridLine.fileDescrLeft.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::SIZE: //file size - { - wxString fileSize = gridLine.fileDescrLeft.fileSize.ToString(); //tmp string - return globalFunctions::includeNumberSeparator(fileSize); - } - case xmlAccess::DATE: //date - return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrLeft.lastWriteTimeRaw); - } + case xmlAccess::FULL_NAME: + return gridLine->fileDescrRight.fullName.c_str(); + case xmlAccess::FILENAME: //filename + return wxString(gridLine->fileDescrRight.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::REL_PATH: //relative path + return wxString(gridLine->fileDescrRight.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).rightDirectory.c_str(); + case xmlAccess::SIZE: //file size + return globalFunctions::includeNumberSeparator(gridLine->fileDescrRight.fileSize.ToString()); + case xmlAccess::DATE: //date + return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrRight.lastWriteTimeRaw); } } } //if data is not found: return wxEmptyString; } + +private: + virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + { + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) + { + //mark filtered rows + if (!gridLine->selectedForSynchronization) + return COLOR_BLUE; + //mark directories + else if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + return COLOR_GREY; + else + return *wxWHITE; + } + return *wxWHITE; + } }; class CustomGridTableMiddle : public CustomGridTable { public: - CustomGridTableMiddle() : CustomGridTable() - { - lastNrCols = 1; //ensure CustomGridTable::updateGridSizes() is working correctly - } +//middle grid is created (first wxWidgets internal call to GetNumberCols()) with one column + CustomGridTableMiddle() : CustomGridTable(0, GetNumberCols()) {} //attention: static binding to virtual GetNumberCols() in a Constructor! - ~CustomGridTableMiddle() {} - - //virtual impl. - int GetNumberCols() + virtual int GetNumberCols() { return 1; } - - virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + virtual wxString GetColLabelValue( int col ) { - if (gridRefUI && unsigned(row) < gridRefUI->size()) - { - const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; - - //mark filtered rows - if (!cmpLine.selectedForSynchronization) - return blue; - else - switch (cmpLine.cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - case FILE_RIGHT_SIDE_ONLY: - return lightGreen; - case FILE_LEFT_NEWER: - case FILE_RIGHT_NEWER: - return lightBlue; - case FILE_DIFFERENT: - return lightRed; - default: - return *wxWHITE; - } - } - return *wxWHITE; + return wxEmptyString; } virtual wxString GetValue(int row, int col) { - if (gridRefUI) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - if (unsigned(row) < gridRefUI->size()) + switch (gridLine->cmpResult) { - const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; - - switch (gridLine.cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - return wxT("<|"); - case FILE_RIGHT_SIDE_ONLY: - return wxT("|>"); - case FILE_RIGHT_NEWER: - return wxT(">>"); - case FILE_LEFT_NEWER: - return wxT("<<"); - case FILE_DIFFERENT: - return wxT("!="); - case FILE_EQUAL: - return wxT("=="); - default: - assert (false); - return wxEmptyString; - } + case FILE_LEFT_SIDE_ONLY: + return wxT("<|"); + case FILE_RIGHT_SIDE_ONLY: + return wxT("|>"); + case FILE_RIGHT_NEWER: + return wxT(">>"); + case FILE_LEFT_NEWER: + return wxT("<<"); + case FILE_DIFFERENT: + return wxT("!="); + case FILE_EQUAL: + return wxT("=="); + default: + assert (false); + return wxEmptyString; } } //if data is not found: return wxEmptyString; } -}; - - -class CustomGridTableRight : public CustomGridTable -{ -public: - CustomGridTableRight() : CustomGridTable() {} - ~CustomGridTableRight() {} +private: virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color { - if (gridRefUI && unsigned(row) < gridRefUI->size()) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; - //mark filtered rows - if (!cmpLine.selectedForSynchronization) - return blue; - //mark directories - else if (cmpLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - return grey; + if (!gridLine->selectedForSynchronization) + return COLOR_BLUE; else - return *wxWHITE; - } - return *wxWHITE; - } - - //virtual impl. - wxString GetValue(int row, int col) - { - if (gridRefUI) - { - if (unsigned(row) < gridRefUI->size()) - { - const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; - - if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - { - switch (getTypeAtPos(col)) - { - case xmlAccess::FULL_NAME: - return gridLine.fileDescrRight.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxEmptyString; - case xmlAccess::REL_PATH: //relative path - return gridLine.fileDescrRight.relativeName.c_str(); - case xmlAccess::SIZE: //file size - return _("<Directory>"); - case xmlAccess::DATE: //date - return wxEmptyString; - } - } - else if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE) + switch (gridLine->cmpResult) { - switch (getTypeAtPos(col)) - { - case xmlAccess::FULL_NAME: - return gridLine.fileDescrRight.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxString(gridLine.fileDescrRight.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::REL_PATH: //relative path - return wxString(gridLine.fileDescrRight.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::SIZE: //file size - { - wxString fileSize = gridLine.fileDescrRight.fileSize.ToString(); //tmp string - return globalFunctions::includeNumberSeparator(fileSize); - } - case xmlAccess::DATE: //date - return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrRight.lastWriteTimeRaw); - } + case FILE_LEFT_SIDE_ONLY: + case FILE_RIGHT_SIDE_ONLY: + return COLOR_LIGHT_GREEN; + case FILE_LEFT_NEWER: + case FILE_RIGHT_NEWER: + return COLOR_LIGHT_BLUE; + case FILE_DIFFERENT: + return COLOR_LIGHT_RED; + default: + return *wxWHITE; } - } } - //if data is not found: - return wxEmptyString; + return *wxWHITE; } }; - - //######################################################################################################## + CustomGrid::CustomGrid(wxWindow *parent, wxWindowID id, const wxPoint& pos, @@ -476,10 +455,10 @@ CustomGrid::CustomGrid(wxWindow *parent, long style, const wxString& name) : wxGrid(parent, id, pos, size, style, name), - leadGrid(NULL), - scrollbarsEnabled(true), - m_gridLeft(NULL), m_gridMiddle(NULL), m_gridRight(NULL), - gridDataTable(NULL), + m_gridLeft(NULL), + m_gridMiddle(NULL), + m_gridRight(NULL), + isLeading(false), currentSortColumn(-1), sortMarker(NULL) { @@ -500,32 +479,14 @@ CustomGrid::CustomGrid(wxWindow *parent, Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxEventHandler(CustomGrid::onGridAccess), NULL, this); Connect(wxEVT_GRID_LABEL_LEFT_CLICK, wxEventHandler(CustomGrid::onGridAccess), NULL, this); GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this); + + GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this); } -void CustomGrid::initSettings(const bool enableScrollbars, - const bool showFileIcons, - CustomGrid* gridLeft, - CustomGrid* gridRight, - CustomGrid* gridMiddle, - GridView* gridRefUI, - FileCompareResult* gridData) +bool CustomGrid::isLeadGrid() const { - scrollbarsEnabled = enableScrollbars; - - //these grids will scroll together - assert(gridLeft && gridRight && gridMiddle); - m_gridLeft = gridLeft; - m_gridRight = gridRight; - m_gridMiddle = gridMiddle; - - //set underlying grid data - assert(gridDataTable); - gridDataTable->setGridDataTable(gridRefUI, gridData); - - this->initGridRenderer(showFileIcons); - - GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this); + return isLeading; } @@ -705,14 +666,17 @@ void additionalGridCommands(wxEvent& event, wxGrid* grid) void CustomGrid::onGridAccess(wxEvent& event) { - if (leadGrid != this) + if (!isLeading) { - leadGrid = this; + isLeading = true; - //notify grids of new user focus - m_gridLeft->leadGrid = this; - m_gridMiddle->leadGrid = this; - m_gridRight->leadGrid = this; + //notify other grids of new user focus + if (m_gridLeft != this) + m_gridLeft->isLeading = false; + if (m_gridMiddle != this) + m_gridMiddle->isLeading = false; + if (m_gridRight != this) + m_gridRight->isLeading = false; wxGrid::SetFocus(); } @@ -728,119 +692,194 @@ void CustomGrid::onGridAccess(wxEvent& event) } -const wxGrid* CustomGrid::getLeadGrid() +//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar +void CustomGrid::adjustGridHeights(wxEvent& event) { - return leadGrid; + if (m_gridLeft && m_gridRight && m_gridMiddle) + { + int y1 = 0; + int y2 = 0; + int y3 = 0; + int dummy = 0; + + m_gridLeft->GetViewStart(&dummy, &y1); + m_gridRight->GetViewStart(&dummy, &y2); + m_gridMiddle->GetViewStart(&dummy, &y3); + + if (y1 != y2 || y2 != y3) + { + int yMax = std::max(y1, std::max(y2, y3)); + + if (m_gridLeft->isLeadGrid()) //do not handle case (y1 == yMax) here!!! Avoid back coupling! + m_gridLeft->SetMargins(0, 0); + else if (y1 < yMax) + m_gridLeft->SetMargins(0, 30); + + if (m_gridRight->isLeadGrid()) + m_gridRight->SetMargins(0, 0); + else if (y2 < yMax) + m_gridRight->SetMargins(0, 30); + + if (m_gridMiddle->isLeadGrid()) + m_gridMiddle->SetMargins(0, 0); + else if (y3 < yMax) + m_gridMiddle->SetMargins(0, 30); + + m_gridLeft->ForceRefresh(); + m_gridRight->ForceRefresh(); + m_gridMiddle->ForceRefresh(); + } + } } -bool CustomGrid::isLeadGrid() +void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap) { - return leadGrid == static_cast<const wxGrid*>(this); + currentSortColumn = sortColumn; + sortMarker = bitmap; } -//overwrite virtual method to finally get rid of the scrollbars -void CustomGrid::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh) +void CustomGrid::DrawColLabel(wxDC& dc, int col) { - if (scrollbarsEnabled) - wxWindow::SetScrollbar(orientation, position, thumbSize, range, refresh); - else - wxWindow::SetScrollbar(orientation, 0, 0, 0, refresh); + wxGrid::DrawColLabel(dc, col); + + if (col == currentSortColumn) + dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border } +//############################################################################################ +//CustomGrid specializations -//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar -void CustomGrid::adjustGridHeights(wxEvent& event) //m_gridLeft, m_gridRight, m_gridMiddle are not NULL in this context +template <bool leftSide, bool showFileIcons> +class GridCellRenderer : public wxGridCellStringRenderer { - int y1 = 0; - int y2 = 0; - int y3 = 0; - int dummy = 0; +public: + GridCellRenderer(CustomGridTableRim* gridDataTable) : m_gridDataTable(gridDataTable) {}; - m_gridLeft->GetViewStart(&dummy, &y1); - m_gridRight->GetViewStart(&dummy, &y2); - m_gridMiddle->GetViewStart(&dummy, &y3); - if (y1 != y2 || y2 != y3) + virtual void Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rect, + int row, int col, + bool isSelected) { - int yMax = std::max(y1, std::max(y2, y3)); - - if (leadGrid == m_gridLeft) //do not handle case (y1 == yMax) here!!! Avoid back coupling! - m_gridLeft->SetMargins(0, 0); - else if (y1 < yMax) - m_gridLeft->SetMargins(0, 30); - - if (leadGrid == m_gridRight) - m_gridRight->SetMargins(0, 0); - else if (y2 < yMax) - m_gridRight->SetMargins(0, 30); - - if (leadGrid == m_gridMiddle) - m_gridMiddle->SetMargins(0, 0); - else if (y3 < yMax) - m_gridMiddle->SetMargins(0, 30); - - m_gridLeft->ForceRefresh(); - m_gridRight->ForceRefresh(); - m_gridMiddle->ForceRefresh(); - } -} +#ifdef FFS_WIN + //############## show windows explorer file icons ###################### + if (showFileIcons) //evaluate at compile time + { + const int ICON_SIZE = 16; //size in pixel -void CustomGrid::updateGridSizes() -{ - assert(gridDataTable); - gridDataTable->updateGridSizes(); -} + if ( m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME && + rect.GetWidth() >= ICON_SIZE) + { + //retrieve grid data + const FileCompareLine* rowData = m_gridDataTable->getRawData(row); + if (rowData) //valid row + { + const DefaultChar* filename; + if (leftSide) //evaluate at compile time + filename = rowData->fileDescrLeft.fullName.c_str(); + else + filename = rowData->fileDescrRight.fullName.c_str(); + if (*filename != DefaultChar(0)) //test if filename is empty + { + // Get the file icon. + SHFILEINFO fileInfo; + fileInfo.hIcon = 0; //initialize hIcon -void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap) -{ - currentSortColumn = sortColumn; - sortMarker = bitmap; -} + if (SHGetFileInfo(filename, //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! + 0, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON)) + { + //clear area where icon will be placed + wxRect rectShrinked(rect); + rectShrinked.SetWidth(ICON_SIZE + 2); //add 2 pixel border + wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + //draw icon + if (fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 + wxIcon icon; + icon.SetHICON(static_cast<WXHICON>(fileInfo.hIcon)); + icon.SetSize(ICON_SIZE, ICON_SIZE); -void CustomGrid::DrawColLabel(wxDC& dc, int col) -{ - wxGrid::DrawColLabel(dc, col); + dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY()); - if (col == currentSortColumn) - dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border + if (!DestroyIcon(fileInfo.hIcon)) + throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted()); + } + + //draw rest + rectShrinked.SetWidth(rect.GetWidth() - ICON_SIZE - 2); + rectShrinked.SetX(rect.GetX() + ICON_SIZE + 2); + wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + return; + } + } + } + } + } + //default + wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); + +#elif defined FFS_LINUX + wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); +#endif + } + +private: + const CustomGridTableRim* const m_gridDataTable; +}; + + + +void CustomGridRim::updateGridSizes() +{ + assert(gridDataTable); + gridDataTable->updateGridSizes(); } -xmlAccess::ColumnAttributes CustomGrid::getDefaultColumnAttributes() +xmlAccess::ColumnAttributes CustomGridRim::getDefaultColumnAttributes() { xmlAccess::ColumnAttributes defaultColumnSettings; xmlAccess::ColumnAttrib newEntry; - newEntry.type = xmlAccess::FULL_NAME; newEntry.visible = false; newEntry.position = 0; newEntry.width = 150; defaultColumnSettings.push_back(newEntry); - newEntry.type = xmlAccess::FILENAME; - newEntry.visible = true; + newEntry.type = xmlAccess::DIRECTORY; newEntry.position = 1; - newEntry.width = 138; + newEntry.width = 140; defaultColumnSettings.push_back(newEntry); newEntry.type = xmlAccess::REL_PATH; + newEntry.visible = true; newEntry.position = 2; newEntry.width = 118; defaultColumnSettings.push_back(newEntry); - newEntry.type = xmlAccess::SIZE; + newEntry.type = xmlAccess::FILENAME; newEntry.position = 3; - newEntry.width = 67; + newEntry.width = 138; defaultColumnSettings.push_back(newEntry); - newEntry.type = xmlAccess::DATE; + newEntry.type = xmlAccess::SIZE; newEntry.position = 4; + newEntry.width = 70; + defaultColumnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::DATE; + newEntry.position = 5; newEntry.width = 113; defaultColumnSettings.push_back(newEntry); @@ -848,7 +887,7 @@ xmlAccess::ColumnAttributes CustomGrid::getDefaultColumnAttributes() } -xmlAccess::ColumnAttributes CustomGrid::getColumnAttributes() +xmlAccess::ColumnAttributes CustomGridRim::getColumnAttributes() { std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); @@ -866,7 +905,7 @@ xmlAccess::ColumnAttributes CustomGrid::getColumnAttributes() } -void CustomGrid::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) +void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) { //remove special alignment for column "size" for (int i = 0; i < GetNumberCols(); ++i) @@ -940,14 +979,14 @@ void CustomGrid::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) } -xmlAccess::ColumnTypes CustomGrid::getTypeAtPos(unsigned pos) const +xmlAccess::ColumnTypes CustomGridRim::getTypeAtPos(unsigned pos) const { assert(gridDataTable); return gridDataTable->getTypeAtPos(pos); } -wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType) +wxString CustomGridRim::getTypeName(xmlAccess::ColumnTypes colType) { switch (colType) { @@ -957,6 +996,8 @@ wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType) return _("Filename"); case xmlAccess::REL_PATH: return _("Relative path"); + case xmlAccess::DIRECTORY: + return _("Directory"); case xmlAccess::SIZE: return _("Size"); case xmlAccess::DATE: @@ -966,139 +1007,121 @@ wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType) } } -//############################################################################################ -//CustomGrid specializations +//---------------------------------------------------------------------------------------- CustomGridLeft::CustomGridLeft(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) : - CustomGrid(parent, id, pos, size, style, name) {} + CustomGridRim(parent, id, pos, size, style, name) {} -template <bool leftSide, bool showFileIcons> -class GridCellRenderer : public wxGridCellStringRenderer +bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) { -public: - GridCellRenderer(CustomGridTable* gridDataTable) : m_gridDataTable(gridDataTable) {}; - - - virtual void Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected) - { -#ifdef FFS_WIN - //############## show windows explorer file icons ###################### - - if (showFileIcons) //evaluate at compile time - { - const int ICON_SIZE = 16; //size in pixel - - if ( m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME && - rect.GetWidth() >= ICON_SIZE) - { - //retrieve grid data - const FileCompareLine* rowData = m_gridDataTable->getRawData(row); - if (rowData) //valid row - { - const DefaultChar* filename; - if (leftSide) //evaluate at compile time - filename = rowData->fileDescrLeft.fullName.c_str(); - else - filename = rowData->fileDescrRight.fullName.c_str(); + //use custom wxGridTableBase class for management of large sets of formatted data. + //This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default. + gridDataTable = new CustomGridTableLeft; + SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor - if (*filename != 0) //test if filename is empty - { - // Get the file icon. - SHFILEINFO fileInfo; - if (SHGetFileInfo(filename, - 0, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON)) - { - wxIcon icon; - icon.SetHICON((WXHICON)fileInfo.hIcon); - icon.SetSize(ICON_SIZE, ICON_SIZE); + return true; +} - //clear area where icon will be placed - wxRect rectShrinked(rect); - rectShrinked.SetWidth(ICON_SIZE + 2); //add 2 pixel border - dc.SetPen(*wxWHITE_PEN); - dc.SetBrush(*wxWHITE_BRUSH); - dc.DrawRectangle(rectShrinked); +void CustomGridLeft::initSettings(const bool showFileIcons, + CustomGrid* gridLeft, + CustomGrid* gridRight, + CustomGrid* gridMiddle, + GridView* gridDataView) +{ + //these grids will scroll together + m_gridLeft = gridLeft; + m_gridRight = gridRight; + m_gridMiddle = gridMiddle; - //draw icon - dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY()); + //set underlying grid data + assert(gridDataTable); + gridDataTable->setGridDataTable(gridDataView); - rectShrinked.SetWidth(rect.GetWidth() - ICON_SIZE - 2); - rectShrinked.SetX(rect.GetX() + ICON_SIZE + 2); - wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + if (showFileIcons) + SetDefaultRenderer(new GridCellRenderer<true, true>(gridDataTable)); //SetDefaultRenderer takes ownership! + else + SetDefaultRenderer(new GridCellRenderer<true, false>(gridDataTable)); +} - if (!DestroyIcon(fileInfo.hIcon)) - throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted()); - return; - } - } - } - } - } - //default - wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); +//this method is called when grid view changes: useful for parallel updating of multiple grids +void CustomGridLeft::DoPrepareDC(wxDC& dc) +{ + wxScrollHelper::DoPrepareDC(dc); -#elif defined FFS_LINUX - wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); -#endif + int x, y = 0; + if (isLeadGrid()) //avoid back coupling + { + GetViewStart(&x, &y); + m_gridMiddle->Scroll(-1, y); //scroll in y-direction only + m_gridRight->Scroll(x, y); } +} -private: - CustomGridTable* m_gridDataTable; -}; +//---------------------------------------------------------------------------------------- +CustomGridRight::CustomGridRight(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) : + CustomGridRim(parent, id, pos, size, style, name) {} -bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) +bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) { - //use custom wxGridTableBase class for management of large sets of formatted data. - //This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default. - gridDataTable = new CustomGridTableLeft(); + gridDataTable = new CustomGridTableRight; SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor return true; } +void CustomGridRight::initSettings(const bool showFileIcons, + CustomGrid* gridLeft, + CustomGrid* gridRight, + CustomGrid* gridMiddle, + GridView* gridDataView) +{ + //these grids will scroll together + m_gridLeft = gridLeft; + m_gridRight = gridRight; + m_gridMiddle = gridMiddle; + + //set underlying grid data + assert(gridDataTable); + gridDataTable->setGridDataTable(gridDataView); + + if (showFileIcons) + SetDefaultRenderer(new GridCellRenderer<false, true>(gridDataTable)); //SetDefaultRenderer takes ownership! + else + SetDefaultRenderer(new GridCellRenderer<false, false>(gridDataTable)); +} + + //this method is called when grid view changes: useful for parallel updating of multiple grids -void CustomGridLeft::DoPrepareDC(wxDC& dc) +void CustomGridRight::DoPrepareDC(wxDC& dc) { wxScrollHelper::DoPrepareDC(dc); int x, y = 0; - if (this == leadGrid) //avoid back coupling + if (isLeadGrid()) //avoid back coupling { GetViewStart(&x, &y); - m_gridMiddle->Scroll(-1, y); //scroll in y-direction only - m_gridRight->Scroll(x, y); + m_gridLeft->Scroll(x, y); + m_gridMiddle->Scroll(-1, y); } } -void CustomGridLeft::initGridRenderer(const bool showFileIcons) -{ - if (showFileIcons) - SetDefaultRenderer(new GridCellRenderer<true, true>(gridDataTable)); //SetDefaultRenderer takes ownership! - else - SetDefaultRenderer(new GridCellRenderer<true, false>(gridDataTable)); -} - - //---------------------------------------------------------------------------------------- CustomGridMiddle::CustomGridMiddle(wxWindow *parent, wxWindowID id, @@ -1106,7 +1129,8 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent, const wxSize& size, long style, const wxString& name) : - CustomGrid(parent, id, pos, size, style, name) + CustomGrid(parent, id, pos, size, style, name), + gridDataTable(NULL) { const wxString header = _("Legend"); wxString toolTip = header + wxT("\n") + @@ -1121,6 +1145,42 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent, } +void CustomGridMiddle::initSettings(CustomGrid* gridLeft, + CustomGrid* gridRight, + CustomGrid* gridMiddle, + FreeFileSync::GridView* gridDataView) +{ + //these grids will scroll together + m_gridLeft = gridLeft; + m_gridRight = gridRight; + m_gridMiddle = gridMiddle; + + //set underlying grid data + assert(gridDataTable); + gridDataTable->setGridDataTable(gridDataView); + +#ifdef FFS_LINUX //get rid of scrollbars; Linux: change policy for GtkScrolledWindow + GtkWidget* gridWidget = wxWindow::m_widget; + GtkScrolledWindow* scrolledWindow = GTK_SCROLLED_WINDOW(gridWidget); + gtk_scrolled_window_set_policy(scrolledWindow, GTK_POLICY_NEVER, GTK_POLICY_NEVER); +#endif +} + + +#ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method +void CustomGridMiddle::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh) +{ + wxWindow::SetScrollbar(orientation, 0, 0, 0, refresh); +} +#endif + +void CustomGridMiddle::updateGridSizes() +{ + assert(gridDataTable); + gridDataTable->updateGridSizes(); +} + + class GridCellRendererMiddle : public wxGridCellStringRenderer { public: @@ -1148,12 +1208,10 @@ public: //clean first block of rect that will receive image of checkbox rectShrinked.SetWidth(shift); - dc.SetPen(*wxWHITE_PEN); - dc.SetBrush(*wxWHITE_BRUSH); - dc.DrawRectangle(rectShrinked); + wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); //print image into first block - rectShrinked.SetX(1); + rectShrinked.SetX(rect.GetX() + 1); if (rowData->selectedForSynchronization) dc.DrawLabel(wxEmptyString, *globalResource.bitmapCheckBoxTrue, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); else @@ -1161,18 +1219,18 @@ public: //print second block (default): display compare result rectShrinked.SetWidth(rect.GetWidth() - shift); - rectShrinked.SetX(shift); + rectShrinked.SetX(rect.GetX() + shift); wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); } private: - CustomGridTable* m_gridDataTable; + const CustomGridTable* const m_gridDataTable; }; bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) { - gridDataTable = new CustomGridTableMiddle(); + gridDataTable = new CustomGridTableMiddle; SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor //display checkboxes (representing bool values) if row is enabled for synchronization @@ -1188,7 +1246,7 @@ void CustomGridMiddle::DoPrepareDC(wxDC& dc) wxScrollHelper::DoPrepareDC(dc); int x, y = 0; - if (this == leadGrid) //avoid back coupling + if (isLeadGrid()) //avoid back coupling { GetViewStart(&x, &y); m_gridLeft->Scroll(-1, y); @@ -1196,45 +1254,3 @@ void CustomGridMiddle::DoPrepareDC(wxDC& dc) } } - -//---------------------------------------------------------------------------------------- -CustomGridRight::CustomGridRight(wxWindow *parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name) : - CustomGrid(parent, id, pos, size, style, name) {} - - -bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) -{ - gridDataTable = new CustomGridTableRight(); - SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor - - return true; -} - - -//this method is called when grid view changes: useful for parallel updating of multiple grids -void CustomGridRight::DoPrepareDC(wxDC& dc) -{ - wxScrollHelper::DoPrepareDC(dc); - - int x, y = 0; - if (this == leadGrid) //avoid back coupling - { - GetViewStart(&x, &y); - m_gridLeft->Scroll(x, y); - m_gridMiddle->Scroll(-1, y); - } -} - - -void CustomGridRight::initGridRenderer(const bool showFileIcons) -{ - if (showFileIcons) - SetDefaultRenderer(new GridCellRenderer<false, true>(gridDataTable)); //SetDefaultRenderer takes ownership! - else - SetDefaultRenderer(new GridCellRenderer<false, false>(gridDataTable)); -} diff --git a/library/CustomGrid.h b/library/CustomGrid.h index 14d62255..802db231 100644 --- a/library/CustomGrid.h +++ b/library/CustomGrid.h @@ -3,15 +3,33 @@ #include <vector> #include <wx/grid.h> -#include "../FreeFileSync.h" +#include "../structures.h" #include "processXml.h" -using namespace FreeFileSync; - class CustomGridTable; +class CustomGridTableRim; +class CustomGridTableMiddle; + +namespace FreeFileSync +{ + class GridView; +} //################################################################################## +/* +class hierarchy: + CustomGrid + /|\ + ____________|____________ + | | + CustomGridRim | + /|\ | + ________|_______ | + | | | +CustomGridLeft CustomGridRight CustomGridMiddle +*/ + class CustomGrid : public wxGrid { public: @@ -24,27 +42,46 @@ public: virtual ~CustomGrid() {} - //overwrite virtual method to finally get rid of the scrollbars - virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true); - virtual void DrawColLabel(wxDC& dc, int col); - void initSettings(const bool enableScrollbars, - const bool showFileIcons, - CustomGrid* gridLeft, - CustomGrid* gridRight, - CustomGrid* gridMiddle, - GridView* gridRefUI, - FileCompareResult* gridData); + //set sort direction indicator on UI + void setSortMarker(const int sortColumn, const wxBitmap* bitmap = &wxNullBitmap); + + bool isLeadGrid() const; - virtual void initGridRenderer(const bool showFileIcons) = 0; +protected: + CustomGrid* m_gridLeft; + CustomGrid* m_gridMiddle; + CustomGrid* m_gridRight; + +private: + void onGridAccess(wxEvent& event); + void adjustGridHeights(wxEvent& event); + + bool isLeading; //identify grid that has user focus + int currentSortColumn; + const wxBitmap* sortMarker; +}; + + +//############## SPECIALIZATIONS ################### +class CustomGridRim : public CustomGrid +{ +public: + CustomGridRim(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) : + CustomGrid(parent, id, pos, size, style, name), + gridDataTable(NULL) {} + + ~CustomGridRim() {} //notify wxGrid that underlying table size has changed void updateGridSizes(); - //set sort direction indicator on UI - void setSortMarker(const int sortColumn, const wxBitmap* bitmap = &wxNullBitmap); - //set visibility, position and width of columns static xmlAccess::ColumnAttributes getDefaultColumnAttributes(); xmlAccess::ColumnAttributes getColumnAttributes(); @@ -54,30 +91,15 @@ public: static wxString getTypeName(xmlAccess::ColumnTypes colType); - const wxGrid* getLeadGrid(); - bool isLeadGrid(); - protected: - void onGridAccess(wxEvent& event); - void adjustGridHeights(wxEvent& event); + CustomGridTableRim* gridDataTable; +private: xmlAccess::ColumnAttributes columnSettings; //set visibility, position and width of columns - - const wxGrid* leadGrid; //grid that has user focus - bool scrollbarsEnabled; - CustomGrid* m_gridLeft; - CustomGrid* m_gridMiddle; - CustomGrid* m_gridRight; - - CustomGridTable* gridDataTable; - - int currentSortColumn; - const wxBitmap* sortMarker; }; -//############## SPECIALIZATIONS ################### -class CustomGridLeft : public CustomGrid +class CustomGridLeft : public CustomGridRim { public: CustomGridLeft(wxWindow *parent, @@ -89,12 +111,41 @@ public: ~CustomGridLeft() {} + virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); + + void initSettings(const bool showFileIcons, //workaround: though this coding better belongs into a constructor + CustomGrid* gridLeft, //this is not possible due to source code generation (information not available at time of construction) + CustomGrid* gridRight, + CustomGrid* gridMiddle, + FreeFileSync::GridView* gridDataView); + //this method is called when grid view changes: useful for parallel updating of multiple grids virtual void DoPrepareDC(wxDC& dc); +}; + + +class CustomGridRight : public CustomGridRim +{ +public: + CustomGridRight(wxWindow *parent, + wxWindowID id, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxWANTS_CHARS, + const wxString& name = wxGridNameStr); + + ~CustomGridRight() {} virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - virtual void initGridRenderer(const bool showFileIcons); + void initSettings(const bool showFileIcons, //workaround: though this coding better belongs into a constructor + CustomGrid* gridLeft, //this is not possible due to source code generation (information not available at time of construction) + CustomGrid* gridRight, + CustomGrid* gridMiddle, + FreeFileSync::GridView* gridDataView); + + //this method is called when grid view changes: useful for parallel updating of multiple grids + virtual void DoPrepareDC(wxDC& dc); }; @@ -110,33 +161,25 @@ public: ~CustomGridMiddle() {} - //this method is called when grid view changes: useful for parallel updating of multiple grids - virtual void DoPrepareDC(wxDC& dc); - virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - virtual void initGridRenderer(const bool showFileIcons) {} -}; - + void initSettings(CustomGrid* gridLeft, //workaround: though this coding better belongs into a constructor + CustomGrid* gridRight, //this is not possible due to source code generation (information not available at time of construction) + CustomGrid* gridMiddle, + FreeFileSync::GridView* gridDataView); -class CustomGridRight : public CustomGrid -{ -public: - CustomGridRight(wxWindow *parent, - wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxWANTS_CHARS, - const wxString& name = wxGridNameStr); + //notify wxGrid that underlying table size has changed + void updateGridSizes(); - ~CustomGridRight() {} +#ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method + virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true); +#endif //this method is called when grid view changes: useful for parallel updating of multiple grids virtual void DoPrepareDC(wxDC& dc); - virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); - - virtual void initGridRenderer(const bool showFileIcons); +private: + CustomGridTableMiddle* gridDataTable; }; #endif // CUSTOMGRID_H_INCLUDED diff --git a/library/FreeFileSync.xpm b/library/FreeFileSync.xpm index 4165c5a1..339ccccb 100644 --- a/library/FreeFileSync.xpm +++ b/library/FreeFileSync.xpm @@ -1,5 +1,5 @@ /* XPM */ -static char * FreeFileSync_xpm[] = { +static const char * FreeFileSync_xpm[] = { "32 32 390 2", " c None", ". c #005927", diff --git a/library/fileHandling.cpp b/library/fileHandling.cpp index 0dccdec7..4f8caec6 100644 --- a/library/fileHandling.cpp +++ b/library/fileHandling.cpp @@ -3,6 +3,7 @@ #include <wx/msgdlg.h> #include "../algorithm.h" #include <wx/filename.h> +#include "globalFunctions.h" #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -67,20 +68,25 @@ private: bool recycleBinAvailable; }; -//global instance of recycle bin -RecycleBin recyclerInstance; + +inline +RecycleBin& getRecycleBin() +{ + static RecycleBin instance; //lazy creation of RecycleBin + return instance; +} bool FreeFileSync::recycleBinExists() { - return recyclerInstance.recycleBinExists(); + return getRecycleBin().recycleBinExists(); } inline bool moveToRecycleBin(const Zstring& filename) throw(RuntimeException) { - return recyclerInstance.moveToRecycleBin(filename); + return getRecycleBin().moveToRecycleBin(filename); } @@ -266,18 +272,19 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc class CloseHandleOnExit { public: - CloseHandleOnExit(HANDLE searchHandle) : m_searchHandle(searchHandle) {} + CloseHandleOnExit(HANDLE fileHandle) : fileHandle_(fileHandle) {} ~CloseHandleOnExit() { - FindClose(m_searchHandle); + CloseHandle(fileHandle_); } private: - HANDLE m_searchHandle; + HANDLE fileHandle_; }; + typedef DWORD WINAPI (*GetFinalPath)( HANDLE hFile, LPTSTR lpszFilePath, @@ -295,7 +302,7 @@ public: //get a handle to the DLL module containing required functionality hKernel = ::LoadLibrary(wxT("kernel32.dll")); if (hKernel) - getFinalPathNameByHandle = (GetFinalPath)(::GetProcAddress(hKernel, "GetFinalPathNameByHandleW")); //load unicode version! + getFinalPathNameByHandle = reinterpret_cast<GetFinalPath>(::GetProcAddress(hKernel, "GetFinalPathNameByHandleW")); //load unicode version! } ~DllHandler() @@ -309,8 +316,13 @@ private: HINSTANCE hKernel; }; -//global instance -DllHandler dynamicWinApi; + +inline +DllHandler& getDllHandler() //lazy creation of DllHandler +{ + static DllHandler instance; + return instance; +} Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target path of symbolic link to a directory @@ -328,13 +340,13 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa CloseHandleOnExit dummy(hDir); - if (dynamicWinApi.getFinalPathNameByHandle == NULL ) + if (getDllHandler().getFinalPathNameByHandle == NULL ) throw FileError(Zstring(_("Error loading library function:")) + wxT("\n\"") + wxT("GetFinalPathNameByHandleW") + wxT("\"")); const unsigned BUFFER_SIZE = 10000; TCHAR targetPath[BUFFER_SIZE]; - const DWORD rv = dynamicWinApi.getFinalPathNameByHandle( + const DWORD rv = getDllHandler().getFinalPathNameByHandle( hDir, targetPath, BUFFER_SIZE, @@ -638,6 +650,21 @@ void FreeFileSync::copyFile(const Zstring& sourceFile, #ifdef FFS_WIN +class CloseFindHandleOnExit +{ +public: + CloseFindHandleOnExit(HANDLE searchHandle) : searchHandle_(searchHandle) {} + + ~CloseFindHandleOnExit() + { + FindClose(searchHandle_); + } + +private: + HANDLE searchHandle_; +}; + + inline void setWin32FileInformation(const FILETIME& lastWriteTime, const DWORD fileSizeHigh, const DWORD fileSizeLow, FreeFileSync::FileInfo& output) { @@ -650,6 +677,7 @@ void setWin32FileInformation(const FILETIME& lastWriteTime, const DWORD fileSize output.fileSize = wxULongLong(fileSizeHigh, fileSizeLow); } + inline bool setWin32FileInformationFromSymlink(const Zstring linkName, FreeFileSync::FileInfo& output) { @@ -679,6 +707,7 @@ bool setWin32FileInformationFromSymlink(const Zstring linkName, FreeFileSync::Fi return true; } + #elif defined FFS_LINUX class CloseDirOnExit { @@ -713,14 +742,13 @@ public: } #ifdef FFS_WIN - Zstring directoryFormatted = directory; - if (!FreeFileSync::endsWithPathSeparator(directoryFormatted)) - directoryFormatted += GlobalResources::FILE_NAME_SEPARATOR; - - const Zstring filespec = directoryFormatted + DefaultChar('*'); + //ensure directoryFormatted ends with backslash + const Zstring directoryFormatted = FreeFileSync::endsWithPathSeparator(directory) ? + directory : + directory + GlobalResources::FILE_NAME_SEPARATOR; WIN32_FIND_DATA fileMetaData; - HANDLE searchHandle = FindFirstFile(filespec.c_str(), //pointer to name of file to search for + HANDLE searchHandle = FindFirstFile((directoryFormatted + DefaultChar('*')).c_str(), //pointer to name of file to search for &fileMetaData); //pointer to returned information if (searchHandle == INVALID_HANDLE_VALUE) @@ -736,11 +764,11 @@ public: else return true; } - CloseHandleOnExit dummy(searchHandle); + CloseFindHandleOnExit dummy(searchHandle); do { //don't return "." and ".." - const wxChar* name = fileMetaData.cFileName; + const wxChar* const name = fileMetaData.cFileName; if ( name[0] == wxChar('.') && ((name[1] == wxChar('.') && name[2] == wxChar('\0')) || name[1] == wxChar('\0'))) @@ -800,14 +828,10 @@ public: return true; #elif defined FFS_LINUX - Zstring directoryFormatted = directory; - if (FreeFileSync::endsWithPathSeparator(directoryFormatted)) - directoryFormatted = directoryFormatted.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); - - DIR* dirObj = opendir(directoryFormatted.c_str()); + DIR* dirObj = opendir(directory.c_str()); if (dirObj == NULL) { - Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directoryFormatted + wxT("\"") ; + Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory+ wxT("\"") ; if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()) == wxDIR_STOP) return false; else @@ -819,13 +843,15 @@ public: while (!(errno = 0) && (dirEntry = readdir(dirObj)) != NULL) //set errno to 0 as unfortunately this isn't done when readdir() returns NULL when it is finished { //don't return "." and ".." - const wxChar* name = dirEntry->d_name; + const wxChar* const name = dirEntry->d_name; if ( name[0] == wxChar('.') && ((name[1] == wxChar('.') && name[2] == wxChar('\0')) || name[1] == wxChar('\0'))) continue; - const Zstring fullName = directoryFormatted + GlobalResources::FILE_NAME_SEPARATOR + name; + const Zstring fullName = FreeFileSync::endsWithPathSeparator(directory) ? //e.g. "/" + directory + name : + directory + GlobalResources::FILE_NAME_SEPARATOR + name; struct stat fileInfo; if (lstat(fullName.c_str(), &fileInfo) != 0) //lstat() does not resolve symlinks @@ -887,7 +913,7 @@ public: return true; //everything okay //else: we have a problem... report it: - const Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directoryFormatted + wxT("\"") ; + const Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\"") ; if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()) == wxDIR_STOP) return false; else @@ -904,15 +930,21 @@ void FreeFileSync::traverseInDetail(const Zstring& directory, const bool traverseDirectorySymlinks, FullDetailFileTraverser* sink) { + Zstring directoryFormatted = directory; +#ifdef FFS_LINUX //remove trailing slash + if (directoryFormatted.size() > 1 && FreeFileSync::endsWithPathSeparator(directoryFormatted)) + directoryFormatted = directoryFormatted.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); +#endif + if (traverseDirectorySymlinks) { TraverseRecursively<true> filewalker(sink); - filewalker.traverse(directory, 0); + filewalker.traverse(directoryFormatted, 0); } else { TraverseRecursively<false> filewalker(sink); - filewalker.traverse(directory, 0); + filewalker.traverse(directoryFormatted, 0); } } diff --git a/library/fileHandling.h b/library/fileHandling.h index 6c9a0400..de068d1f 100644 --- a/library/fileHandling.h +++ b/library/fileHandling.h @@ -1,7 +1,6 @@ #ifndef RECYCLER_H_INCLUDED #define RECYCLER_H_INCLUDED -#include "globalFunctions.h" #include <wx/dir.h> #include "zstring.h" @@ -11,7 +10,7 @@ public: FileError(const Zstring& message) : errorMessage(message) {} - Zstring show() const + const Zstring& show() const { return errorMessage; } @@ -25,7 +24,7 @@ namespace FreeFileSync { struct FileInfo { - wxULongLong fileSize; //unit: bytes! + wxULongLong fileSize; //unit: bytes! wxLongLong lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC }; diff --git a/library/filter.cpp b/library/filter.cpp new file mode 100644 index 00000000..d5255367 --- /dev/null +++ b/library/filter.cpp @@ -0,0 +1,241 @@ +#include "filter.h" +#include "zstring.h" +#include <wx/string.h> +#include <set> +#include <vector> +#include "resources.h" + + +void compoundStringToTable(const Zstring& compoundInput, const DefaultChar* delimiter, std::vector<Zstring>& output) +{ + output.clear(); + Zstring input(compoundInput); + + //make sure input ends with delimiter - no problem with empty strings here + if (!input.EndsWith(delimiter)) + input += delimiter; + + unsigned int indexStart = 0; + unsigned int indexEnd = 0; + while ((indexEnd = input.find(delimiter, indexStart)) != Zstring::npos) + { + if (indexStart != indexEnd) //do not add empty strings + { + Zstring newEntry = input.substr(indexStart, indexEnd - indexStart); + + newEntry.Trim(true); //remove whitespace characters from right + newEntry.Trim(false); //remove whitespace characters from left + + if (!newEntry.empty()) + output.push_back(newEntry); + } + indexStart = indexEnd + 1; + } +} + + +inline +void mergeVectors(std::vector<Zstring>& changing, const std::vector<Zstring>& input) +{ + for (std::vector<Zstring>::const_iterator i = input.begin(); i != input.end(); ++i) + changing.push_back(*i); +} + + +inline +void formatFilterString(Zstring& filter) +{ +#ifdef FFS_WIN + //Windows does NOT distinguish between upper/lower-case + filter.MakeLower(); +#elif defined FFS_LINUX + //Linux DOES distinguish between upper/lower-case +//nothing to do here +#else + adapt; +#endif +} + + +std::vector<Zstring> compoundStringToFilter(const Zstring& filterString) +{ + //delimiters may be ';' or '\n' + std::vector<Zstring> filterList; + std::vector<Zstring> filterPreProcessing; + compoundStringToTable(filterString, wxT(";"), filterPreProcessing); + + for (std::vector<Zstring>::const_iterator i = filterPreProcessing.begin(); i != filterPreProcessing.end(); ++i) + { + std::vector<Zstring> newEntries; + compoundStringToTable(*i, wxT("\n"), newEntries); + mergeVectors(filterList, newEntries); + } + + return filterList; +} + + +inline +void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, std::set<Zstring>& directoryFilter) +{ + //Test if filtername ends with GlobalResources::FILE_NAME_SEPARATOR, ignoring '*' and '?'. + //If so, treat as filter for directory and add to directoryFilter. + if (!filtername.empty()) + { + const DefaultChar* filter = filtername.c_str(); + int i = filtername.length() - 1; + while (filter[i] == DefaultChar('*') || filter[i] == DefaultChar('?')) + { + --i; + + if (i == -1) + break; + } + + if (i >= 0 && filter[i] == GlobalResources::FILE_NAME_SEPARATOR) //last FILE_NAME_SEPARATOR found + { + if (i != int(filtername.length()) - 1) // "name\*" + { + fileFilter.insert(filtername); + directoryFilter.insert(filtername); + } + //else: "name\" -> not inserted directly + + if (i > 0) // "name\*" or "name\": add "name" to directory filter + directoryFilter.insert(Zstring(filtername.c_str(), i)); + } + else + { + fileFilter.insert(filtername); + directoryFilter.insert(filtername); + } + } +} + + +inline +bool matchesFilter(const Zstring& name, const std::set<Zstring>& filter) +{ + for (std::set<Zstring>::iterator j = filter.begin(); j != filter.end(); ++j) + if (name.Matches(*j)) + return true; + + return false; +} + + +void FreeFileSync::filterGridData(FolderComparison& folderCmp, const wxString& includeFilter, const wxString& excludeFilter) +{ + //no need for regular expressions! In tests wxRegex was by factor of 10 slower than wxString::Matches()!! + + //load filter into vectors of strings + //delimiters may be ';' or '\n' + std::vector<Zstring> includeList = compoundStringToFilter(includeFilter.c_str()); + std::vector<Zstring> excludeList = compoundStringToFilter(excludeFilter.c_str()); + +//############################################################## + //setup include/exclude filters for files and directories + std::set<Zstring> filterFileIn; + std::set<Zstring> filterFolderIn; + for (std::vector<Zstring>::iterator i = includeList.begin(); i != includeList.end(); ++i) + { + formatFilterString(*i); //format entry + addFilterEntry(*i, filterFileIn, filterFolderIn); + } + + std::set<Zstring> filterFileEx; + std::set<Zstring> filterFolderEx; + for (std::vector<Zstring>::iterator i = excludeList.begin(); i != excludeList.end(); ++i) + { + formatFilterString(*i); //format entry + addFilterEntry(*i, filterFileEx, filterFolderEx); + } + +//############################################################## + + //execute filtering... + for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + { + FileComparison& fileCmp = j->fileCmp; + + for (FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) + { + Zstring filenameLeft = i->fileDescrLeft.fullName; + Zstring filenameRight = i->fileDescrRight.fullName; + + formatFilterString(filenameLeft); + formatFilterString(filenameRight); + + + //left hand side + if (i->fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + { + if ( !matchesFilter(filenameLeft, filterFileIn) || //process include filters + matchesFilter(filenameLeft, filterFileEx)) //process exclude filters + { + i->selectedForSynchronization = false; + continue; + } + } + else if (i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + { + if ( !matchesFilter(filenameLeft, filterFolderIn) || //process include filters + matchesFilter(filenameLeft, filterFolderEx)) //process exclude filters + { + i->selectedForSynchronization = false; + continue; + } + } + + //right hand side + if (i->fileDescrRight.objType == FileDescrLine::TYPE_FILE) + { + if ( !matchesFilter(filenameRight, filterFileIn) || //process include filters + matchesFilter(filenameRight, filterFileEx)) //process exclude filters + { + i->selectedForSynchronization = false; + continue; + } + } + else if (i->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + { + if ( !matchesFilter(filenameRight, filterFolderIn) || //process include filters + matchesFilter(filenameRight, filterFolderEx)) //process exclude filters + { + i->selectedForSynchronization = false; + continue; + } + } + + i->selectedForSynchronization = true; + } + } +} + + +template <bool includeRows> +inline +void inOrExcludeAllRows(FreeFileSync::FolderComparison& folderCmp) +{ + //remove all filters on folderCmp + for (FreeFileSync::FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + { + FreeFileSync::FileComparison& fileCmp = j->fileCmp; + for (FreeFileSync::FileComparison::iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) + i->selectedForSynchronization = includeRows; + } +} + + +void FreeFileSync::includeAllRowsOnGrid(FolderComparison& folderCmp) +{ + //remove all filters on currentGridData + inOrExcludeAllRows<true>(folderCmp); +} + + +void FreeFileSync::excludeAllRowsOnGrid(FolderComparison& folderCmp) +{ + //exclude all rows on currentGridData + inOrExcludeAllRows<false>(folderCmp); +} diff --git a/library/filter.h b/library/filter.h new file mode 100644 index 00000000..3d0598e1 --- /dev/null +++ b/library/filter.h @@ -0,0 +1,15 @@ +#ifndef FFS_FILTER_H_INCLUDED +#define FFS_FILTER_H_INCLUDED + +#include "../structures.h" + + +namespace FreeFileSync +{ + void filterGridData(FolderComparison& folderCmp, const wxString& includeFilter, const wxString& excludeFilter); + void includeAllRowsOnGrid(FolderComparison& folderCmp); + void excludeAllRowsOnGrid(FolderComparison& folderCmp); +} + + +#endif // FFS_FILTER_H_INCLUDED diff --git a/library/globalFunctions.cpp b/library/globalFunctions.cpp index 4b387293..7c4a1b92 100644 --- a/library/globalFunctions.cpp +++ b/library/globalFunctions.cpp @@ -2,6 +2,7 @@ #include "resources.h" #include <wx/msgdlg.h> #include <wx/file.h> +#include <fstream> std::string globalFunctions::numberToString(const unsigned int number) @@ -20,6 +21,14 @@ std::string globalFunctions::numberToString(const int number) } +std::string globalFunctions::numberToString(const long number) +{ + char result[100]; + sprintf(result, "%ld", number); + return std::string(result); +} + + std::string globalFunctions::numberToString(const float number) { char result[100]; @@ -52,14 +61,18 @@ int globalFunctions::stringToInt(const std::string& number) } -inline +long globalFunctions::stringToLong(const std::string& number) +{ + return atol(number.c_str()); +} + + double globalFunctions::stringToDouble(const std::string& number) { return atof(number.c_str()); } -inline int globalFunctions::wxStringToInt(const wxString& number) { long result = 0; @@ -70,7 +83,6 @@ int globalFunctions::wxStringToInt(const wxString& number) } -inline double globalFunctions::wxStringToDouble(const wxString& number) { double result = 0; diff --git a/library/globalFunctions.h b/library/globalFunctions.h index 98e8cd1c..622babcb 100644 --- a/library/globalFunctions.h +++ b/library/globalFunctions.h @@ -6,10 +6,9 @@ #include <vector> #include <set> #include <wx/string.h> -#include <fstream> #include <wx/stream.h> #include <wx/stopwatch.h> - +#include <wx/longlong.h> namespace globalFunctions { @@ -28,6 +27,7 @@ namespace globalFunctions std::string numberToString(const unsigned int number); //convert number to string std::string numberToString(const int number); //convert number to string + std::string numberToString(const long number); //convert number to string std::string numberToString(const float number); //convert number to string wxString numberToWxString(const unsigned int number); //convert number to wxString @@ -35,6 +35,7 @@ namespace globalFunctions wxString numberToWxString(const float number); //convert number to wxString int stringToInt( const std::string& number); //convert String to number + long stringToLong( const std::string& number); //convert String to number double stringToDouble(const std::string& number); //convert String to number int wxStringToInt( const wxString& number); //convert wxString to number @@ -47,6 +48,12 @@ namespace globalFunctions int readInt(wxInputStream& stream); //read int from file stream void writeInt(wxOutputStream& stream, const int number); //write int to filestream + + inline + wxLongLong convertToSigned(const wxULongLong number) + { + return wxLongLong(number.GetHi(), number.GetLo()); + } } @@ -54,7 +61,7 @@ namespace globalFunctions class Performance { public: - wxDEPRECATED(Performance()); //generates compiler warnings as a reminder to remove code after measurements + wxDEPRECATED(Performance()); //generate compiler warnings as a reminder to remove code after measurements ~Performance(); void showResult(); @@ -113,26 +120,26 @@ private: template <class T> void removeRowsFromVector(std::vector<T>& grid, const std::set<int>& rowsToRemove) { - std::vector<T> temp; - int rowToSkip = -1; //keep it an INT! - - std::set<int>::iterator rowToSkipIndex = rowsToRemove.begin(); + if (rowsToRemove.size() > 0) + { + std::vector<T> temp; - if (rowToSkipIndex != rowsToRemove.end()) - rowToSkip = *rowToSkipIndex; + std::set<int>::iterator rowToSkipIndex = rowsToRemove.begin(); + int rowToSkip = *rowToSkipIndex; - for (int i = 0; i < int(grid.size()); ++i) - { - if (i != rowToSkip) - temp.push_back(grid[i]); - else + for (int i = 0; i < int(grid.size()); ++i) { - ++rowToSkipIndex; - if (rowToSkipIndex != rowsToRemove.end()) - rowToSkip = *rowToSkipIndex; + if (i != rowToSkip) + temp.push_back(grid[i]); + else + { + ++rowToSkipIndex; + if (rowToSkipIndex != rowsToRemove.end()) + rowToSkip = *rowToSkipIndex; + } } + grid.swap(temp); } - grid.swap(temp); } diff --git a/library/misc.cpp b/library/localization.cpp index 0d5761d6..328f37be 100644 --- a/library/misc.cpp +++ b/library/localization.cpp @@ -1,7 +1,39 @@ -#include "misc.h" +#include "localization.h" #include <wx/msgdlg.h> #include "resources.h" #include "globalFunctions.h" +#include <fstream> +#include <set> + +//_("Browse") <- dummy string for wxDirPickerCtrl to be recognized by automatic text extraction! + + +struct TranslationLine +{ + wxString original; + wxString translation; + + bool operator<(const TranslationLine& b) const + { + return (original < b.original); + } +}; + +class Translation : public std::set<TranslationLine> {}; + + +CustomLocale::CustomLocale() : + wxLocale(), + currentLanguage(wxLANGUAGE_ENGLISH) +{ + translationDB = new Translation; +} + + +CustomLocale::~CustomLocale() +{ + delete translationDB; +} inline @@ -49,12 +81,6 @@ void exchangeEscapeChars(wxString& data) } -CustomLocale::CustomLocale() : - wxLocale(), - currentLanguage(wxLANGUAGE_ENGLISH) -{} - - void CustomLocale::setLanguage(const int language) { currentLanguage = language; @@ -89,6 +115,9 @@ void CustomLocale::setLanguage(const int language) case wxLANGUAGE_PORTUGUESE: languageFile = "Languages/portuguese.lng"; break; + case wxLANGUAGE_PORTUGUESE_BRAZILIAN: + languageFile = "Languages/portuguese_br.lng"; + break; case wxLANGUAGE_SLOVENIAN: languageFile = "Languages/slovenian.lng"; break; @@ -100,7 +129,7 @@ void CustomLocale::setLanguage(const int language) currentLanguage = wxLANGUAGE_ENGLISH; } - static bool initialized = false; //wxLocale is a "static" too! + static bool initialized = false; //wxLocale is a global too! if (!initialized) { initialized = true; @@ -108,7 +137,7 @@ void CustomLocale::setLanguage(const int language) } //load language file into buffer - translationDB.clear(); + translationDB->clear(); const int bufferSize = 100000; char temp[bufferSize]; if (!languageFile.empty()) @@ -137,7 +166,7 @@ void CustomLocale::setLanguage(const int language) else { currentLine.translation = formattedString; - translationDB.insert(currentLine); + translationDB->insert(currentLine); } } langFile.close(); @@ -160,8 +189,8 @@ const wxChar* CustomLocale::GetString(const wxChar* szOrigString, const wxChar* currentLine.original = szOrigString; //look for translation in buffer table - Translation::iterator i; - if ((i = translationDB.find(currentLine)) != translationDB.end()) + const Translation::iterator i = translationDB->find(currentLine); + if (i != translationDB->end()) return i->translation.c_str(); //fallback diff --git a/library/localization.h b/library/localization.h new file mode 100644 index 00000000..cf29d06d --- /dev/null +++ b/library/localization.h @@ -0,0 +1,32 @@ +#ifndef MISC_H_INCLUDED +#define MISC_H_INCLUDED + +#include <wx/intl.h> + +class Translation; + + +class CustomLocale : public wxLocale +{ +public: + CustomLocale(); + ~CustomLocale(); + + void setLanguage(const int language); + + int getLanguage() const + { + return currentLanguage; + } + + const wxChar* GetString(const wxChar* szOrigString, const wxChar* szDomain = NULL) const; + + static const std::string FfsLanguageDat; + +private: + Translation* translationDB; + int currentLanguage; +}; + + +#endif // MISC_H_INCLUDED diff --git a/library/misc.h b/library/misc.h deleted file mode 100644 index c3e960ff..00000000 --- a/library/misc.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef MISC_H_INCLUDED -#define MISC_H_INCLUDED - -#include <wx/string.h> -#include <set> -#include <wx/intl.h> -#include <wx/panel.h> - - -struct TranslationLine -{ - wxString original; - wxString translation; - - bool operator>(const TranslationLine& b ) const - { - return (original > b.original); - } - bool operator<(const TranslationLine& b) const - { - return (original < b.original); - } - bool operator==(const TranslationLine& b) const - { - return (original == b.original); - } -}; -typedef std::set<TranslationLine> Translation; - - -class CustomLocale : public wxLocale -{ -public: - CustomLocale(); - ~CustomLocale() {} - - void setLanguage(const int language); - - int getLanguage() - { - return currentLanguage; - } - - const wxChar* GetString(const wxChar* szOrigString, const wxChar* szDomain = NULL) const; - - static const std::string FfsLanguageDat; - -private: - Translation translationDB; - int currentLanguage; -}; - - -#endif // MISC_H_INCLUDED diff --git a/library/multithreading.cpp b/library/multithreading.cpp index 106d1aa7..6c2612f1 100644 --- a/library/multithreading.cpp +++ b/library/multithreading.cpp @@ -1,4 +1,5 @@ #include "multithreading.h" +#include "statusHandler.h" #include <wx/utils.h> //#include <wx/msw/wrapwin.h> //includes "windows.h" diff --git a/library/multithreading.h b/library/multithreading.h index bf0da145..21c5bcf2 100644 --- a/library/multithreading.h +++ b/library/multithreading.h @@ -1,9 +1,9 @@ #ifndef MULTITHREADING_H_INCLUDED #define MULTITHREADING_H_INCLUDED -#include "statusHandler.h" #include <wx/thread.h> +class StatusHandler; class WorkerThread; diff --git a/library/processXml.cpp b/library/processXml.cpp index 00737633..c16b9d76 100644 --- a/library/processXml.cpp +++ b/library/processXml.cpp @@ -3,11 +3,18 @@ #include <wx/ffile.h> #include <wx/intl.h> #include "globalFunctions.h" +#include "tinyxml/tinyxml.h" #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" #endif +using namespace FreeFileSync; + +const wxString xmlAccess::LAST_CONFIG_FILE = wxT("LastRun.ffs_gui"); +const wxString xmlAccess::GLOBAL_CONFIG_FILE = wxT("GlobalSettings.xml"); + + //small helper functions bool readXmlElementValue(std::string& output, const TiXmlElement* parent, const std::string& name); bool readXmlElementValue(int& output, const TiXmlElement* parent, const std::string& name); @@ -151,18 +158,18 @@ xmlAccess::XmlBatchConfig xmlAccess::readBatchConfig(const wxString& filename) xmlAccess::XmlGlobalSettings xmlAccess::readGlobalSettings() { //load XML - if (!wxFileExists(FreeFileSync::GLOBAL_CONFIG_FILE)) - throw FileError(Zstring(_("File does not exist:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); + if (!wxFileExists(xmlAccess::GLOBAL_CONFIG_FILE)) + throw FileError(Zstring(_("File does not exist:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); - XmlConfigInput inputFile(FreeFileSync::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS); + XmlConfigInput inputFile(xmlAccess::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS); XmlGlobalSettings outputCfg; if (!inputFile.loadedSuccessfully()) - throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); + throw FileError(Zstring(_("Error reading file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); if (!inputFile.readXmlGlobalSettings(outputCfg)) - throw FileError(Zstring(_("Error parsing configuration file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); + throw FileError(Zstring(_("Error parsing configuration file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); return outputCfg; } @@ -194,12 +201,12 @@ void xmlAccess::writeBatchConfig(const wxString& filename, const XmlBatchConfig& void xmlAccess::writeGlobalSettings(const XmlGlobalSettings& outputCfg) { - XmlConfigOutput outputFile(FreeFileSync::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS); + XmlConfigOutput outputFile(xmlAccess::GLOBAL_CONFIG_FILE, XML_GLOBAL_SETTINGS); //populate and write XML tree if ( !outputFile.writeXmlGlobalSettings(outputCfg) || //add GUI layout configuration settings !outputFile.writeToFile()) //save XML - throw FileError(Zstring(_("Error writing file:")) + wxT(" \"") + FreeFileSync::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); + throw FileError(Zstring(_("Error writing file:")) + wxT(" \"") + xmlAccess::GLOBAL_CONFIG_FILE.c_str() + wxT("\"")); return; } @@ -273,6 +280,19 @@ bool readXmlElementValue(int& output, const TiXmlElement* parent, const std::str } +bool readXmlElementValue(long& output, const TiXmlElement* parent, const std::string& name) +{ + std::string temp; + if (readXmlElementValue(temp, parent, name)) + { + output = globalFunctions::stringToLong(temp); + return true; + } + else + return false; +} + + bool readXmlElementValue(CompareVariant& output, const TiXmlElement* parent, const std::string& name) { int dummy = 0; @@ -396,6 +416,7 @@ bool XmlConfigInput::readXmlMainConfig(MainConfiguration& mainCfg, std::vector<F newPair.rightDirectory = wxString::FromUTF8(tempString.c_str()).c_str(); directoryPairs.push_back(newPair); + folderPair = folderPair->NextSiblingElement(); } @@ -435,17 +456,11 @@ bool XmlConfigInput::readXmlGuiConfig(xmlAccess::XmlGuiConfig& outputCfg) TiXmlHandle hRoot(root); - //read GUI layout - TiXmlElement* mainWindow = hRoot.FirstChild("GuiConfig").FirstChild("Windows").FirstChild("Main").ToElement(); - if (mainWindow) - { - readXmlElementValue(outputCfg.hideFilteredElements, mainWindow, "HideFiltered"); - } - - TiXmlElement* guiConfig = hRoot.FirstChild("GuiConfig").ToElement(); if (guiConfig) { + readXmlElementValue(outputCfg.hideFilteredElements, guiConfig, "HideFiltered"); + readXmlElementValue(outputCfg.ignoreErrors, guiConfig, "IgnoreErrors"); } @@ -506,6 +521,9 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC //copy symbolic links to files readXmlElementValue(outputCfg.shared.copyFileSymlinks, global, "CopyFileSymlinks"); + + //last update check + readXmlElementValue(outputCfg.shared.lastUpdateCheck, global, "LastCheckForUpdates"); } TiXmlElement* warnings = hRoot.FirstChild("Shared").FirstChild("Warnings").ToElement(); @@ -516,6 +534,9 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC //significant difference check readXmlElementValue(outputCfg.shared.warningSignificantDifference, warnings, "CheckForSignificantDifference"); + + //check free disk space + readXmlElementValue(outputCfg.shared.warningNotEnoughDiskSpace, warnings, "CheckForFreeDiskSpace"); } //gui specific global settings (optional) @@ -532,6 +553,7 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC readXmlElementValue(outputCfg.gui.deleteOnBothSides, mainWindow, "ManualDeletionOnBothSides"); readXmlElementValue(outputCfg.gui.useRecyclerForManualDeletion, mainWindow, "ManualDeletionUseRecycler"); readXmlElementValue(outputCfg.gui.showFileIcons, mainWindow, "ShowFileIcons"); + readXmlElementValue(outputCfg.gui.popupOnConfigChange, mainWindow, "PopupOnConfigChange"); //########################################################### //read column attributes @@ -585,10 +607,28 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC //load folder history elements const TiXmlElement* historyLeft = TiXmlHandle(mainWindow).FirstChild("FolderHistoryLeft").ToElement(); + if (historyLeft) + { + //load max. history size + const char* histSizeMax = historyLeft->Attribute("MaximumSize"); + if (histSizeMax) //may be NULL! + outputCfg.gui.folderHistLeftMax = globalFunctions::stringToInt(histSizeMax); + + //load config history elements + readXmlElementTable(outputCfg.gui.folderHistoryLeft, historyLeft, "Folder"); + } + const TiXmlElement* historyRight = TiXmlHandle(mainWindow).FirstChild("FolderHistoryRight").ToElement(); + if (historyRight) + { + //load max. history size + const char* histSizeMax = historyRight->Attribute("MaximumSize"); + if (histSizeMax) //may be NULL! + outputCfg.gui.folderHistRightMax = globalFunctions::stringToInt(histSizeMax); - readXmlElementTable(outputCfg.gui.folderHistoryLeft, historyLeft, "Folder"); - readXmlElementTable(outputCfg.gui.folderHistoryRight, historyRight, "Folder"); + //load config history elements + readXmlElementTable(outputCfg.gui.folderHistoryRight, historyRight, "Folder"); + } } TiXmlElement* gui = hRoot.FirstChild("Gui").ToElement(); @@ -607,7 +647,7 @@ bool XmlConfigInput::readXmlGlobalSettings(xmlAccess::XmlGlobalSettings& outputC //load max. history size const char* histSizeMax = cfgHistory->Attribute("MaximumSize"); if (histSizeMax) //may be NULL! - outputCfg.gui.cfgHistoryMaxItems = globalFunctions::stringToInt(histSizeMax); + outputCfg.gui.cfgHistoryMax = globalFunctions::stringToInt(histSizeMax); //load config history elements readXmlElementTable(outputCfg.gui.cfgFileHistory, cfgHistory, "File"); @@ -649,7 +689,7 @@ XmlConfigOutput::XmlConfigOutput(const wxString& fileName, const xmlAccess::XmlT bool XmlConfigOutput::writeToFile() { //workaround to get a FILE* from a unicode filename - wxFFile dummyFile(m_fileName, wxT("wb")); //save in binary mode for Linux portability of config files + wxFFile dummyFile(m_fileName, wxT("w")); //no need for "binary" mode here if (!dummyFile.IsOpened()) return false; @@ -676,6 +716,12 @@ void addXmlElement(TiXmlElement* parent, const std::string& name, const int valu } +void addXmlElement(TiXmlElement* parent, const std::string& name, const long value) +{ + addXmlElement(parent, name, globalFunctions::numberToString(value)); +} + + void addXmlElement(TiXmlElement* parent, const std::string& name, const SyncConfiguration::Direction value) { if (value == SyncConfiguration::SYNC_DIR_LEFT) @@ -794,13 +840,7 @@ bool XmlConfigOutput::writeXmlGuiConfig(const xmlAccess::XmlGuiConfig& inputCfg) TiXmlElement* guiConfig = new TiXmlElement("GuiConfig"); root->LinkEndChild(guiConfig); - TiXmlElement* windows = new TiXmlElement("Windows"); - guiConfig->LinkEndChild(windows); - - TiXmlElement* mainWindow = new TiXmlElement("Main"); - windows->LinkEndChild(mainWindow); - - addXmlElement(mainWindow, "HideFiltered", inputCfg.hideFilteredElements); + addXmlElement(guiConfig, "HideFiltered", inputCfg.hideFilteredElements); addXmlElement(guiConfig, "IgnoreErrors", inputCfg.ignoreErrors); @@ -851,6 +891,9 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& //copy symbolic links to files addXmlElement(global, "CopyFileSymlinks", inputCfg.shared.copyFileSymlinks); + //last update check + addXmlElement(global, "LastCheckForUpdates", inputCfg.shared.lastUpdateCheck); + //warnings TiXmlElement* warnings = new TiXmlElement("Warnings"); global->LinkEndChild(warnings); @@ -861,6 +904,9 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& //significant difference check addXmlElement(warnings, "CheckForSignificantDifference", inputCfg.shared.warningSignificantDifference); + //check free disk space + addXmlElement(warnings, "CheckForFreeDiskSpace", inputCfg.shared.warningNotEnoughDiskSpace); + //################################################################### //write global gui settings @@ -884,7 +930,8 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& addXmlElement(mainWindow, "ManualDeletionOnBothSides", inputCfg.gui.deleteOnBothSides); addXmlElement(mainWindow, "ManualDeletionUseRecycler", inputCfg.gui.useRecyclerForManualDeletion); - addXmlElement(mainWindow, "ShowFileIcons", inputCfg.gui.showFileIcons); + addXmlElement(mainWindow, "ShowFileIcons" , inputCfg.gui.showFileIcons); + addXmlElement(mainWindow, "PopupOnConfigChange" , inputCfg.gui.popupOnConfigChange); //write column attributes TiXmlElement* leftColumn = new TiXmlElement("LeftColumns"); @@ -925,7 +972,10 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& TiXmlElement* historyRight = new TiXmlElement("FolderHistoryRight"); mainWindow->LinkEndChild(historyRight); + historyLeft->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.folderHistLeftMax)); addXmlElementTable(historyLeft, "Folder", inputCfg.gui.folderHistoryLeft); + + historyRight->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.folderHistRightMax)); addXmlElementTable(historyRight, "Folder", inputCfg.gui.folderHistoryRight); @@ -936,7 +986,7 @@ bool XmlConfigOutput::writeXmlGlobalSettings(const xmlAccess::XmlGlobalSettings& TiXmlElement* cfgHistory = new TiXmlElement("ConfigHistory"); gui->LinkEndChild(cfgHistory); - cfgHistory->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.cfgHistoryMaxItems)); + cfgHistory->SetAttribute("MaximumSize", globalFunctions::numberToString(inputCfg.gui.cfgHistoryMax)); addXmlElementTable(cfgHistory, "File", inputCfg.gui.cfgFileHistory); //################################################################### @@ -988,10 +1038,6 @@ int xmlAccess::retrieveSystemLanguage() case wxLANGUAGE_CHINESE_TAIWAN: return wxLANGUAGE_CHINESE_SIMPLIFIED; - //variants of wxLANGUAGE_PORTUGUESE - case wxLANGUAGE_PORTUGUESE_BRAZILIAN: - return wxLANGUAGE_PORTUGUESE; - //variants of wxLANGUAGE_SPANISH case wxLANGUAGE_SPANISH_ARGENTINA: case wxLANGUAGE_SPANISH_BOLIVIA: @@ -1019,6 +1065,8 @@ int xmlAccess::retrieveSystemLanguage() //case wxLANGUAGE_POLISH: //case wxLANGUAGE_SLOVENIAN: //case wxLANGUAGE_HUNGARIAN: + //case wxLANGUAGE_PORTUGUESE: + //case wxLANGUAGE_PORTUGUESE_BRAZILIAN: default: return lang; @@ -1048,4 +1096,5 @@ void xmlAccess::XmlGlobalSettings::_Shared::resetWarnings() { warningDependentFolders = true; warningSignificantDifference = true; + warningNotEnoughDiskSpace = true; } diff --git a/library/processXml.h b/library/processXml.h index c24ad0e7..763edbca 100644 --- a/library/processXml.h +++ b/library/processXml.h @@ -1,14 +1,14 @@ #ifndef PROCESSXML_H_INCLUDED #define PROCESSXML_H_INCLUDED -#include "../FreeFileSync.h" -#include "tinyxml/tinyxml.h" - -using namespace FreeFileSync; - +#include "../structures.h" +#include "fileHandling.h" namespace xmlAccess { + extern const wxString LAST_CONFIG_FILE; + extern const wxString GLOBAL_CONFIG_FILE; + enum OnError { ON_ERROR_POPUP, @@ -30,9 +30,10 @@ namespace xmlAccess REL_PATH, SIZE, DATE, - FULL_NAME + FULL_NAME, + DIRECTORY }; - const unsigned COLUMN_TYPE_COUNT = 5; + const unsigned COLUMN_TYPE_COUNT = 6; struct ColumnAttrib { @@ -48,13 +49,28 @@ namespace xmlAccess struct XmlGuiConfig { - XmlGuiConfig() : hideFilteredElements(false), ignoreErrors(false) {} //initialize values + XmlGuiConfig() : + hideFilteredElements(false), + ignoreErrors(false) {} //initialize values - MainConfiguration mainCfg; - std::vector<FolderPair> directoryPairs; + FreeFileSync::MainConfiguration mainCfg; + std::vector<FreeFileSync::FolderPair> directoryPairs; bool hideFilteredElements; bool ignoreErrors; //reaction on error situation during synchronization + + bool operator==(const XmlGuiConfig& other) const + { + return mainCfg == other.mainCfg && + directoryPairs == other.directoryPairs && + hideFilteredElements == other.hideFilteredElements && + ignoreErrors == other.ignoreErrors; + } + + bool operator!=(const XmlGuiConfig& other) const + { + return !(*this == other); + } }; @@ -62,8 +78,8 @@ namespace xmlAccess { XmlBatchConfig() : silent(false), handleError(ON_ERROR_POPUP) {} - MainConfiguration mainCfg; - std::vector<FolderPair> directoryPairs; + FreeFileSync::MainConfiguration mainCfg; + std::vector<FreeFileSync::FolderPair> directoryPairs; bool silent; OnError handleError; //reaction on error situation during synchronization @@ -83,7 +99,8 @@ namespace xmlAccess programLanguage(retrieveSystemLanguage()), fileTimeTolerance(2), //default 2s: FAT vs NTFS traverseDirectorySymlinks(false), - copyFileSymlinks(supportForSymbolicLinks()) + copyFileSymlinks(supportForSymbolicLinks()), + lastUpdateCheck(0) { resetWarnings(); } @@ -92,12 +109,14 @@ namespace xmlAccess unsigned fileTimeTolerance; //max. allowed file time deviation bool traverseDirectorySymlinks; bool copyFileSymlinks; //copy symbolic link instead of target file + long lastUpdateCheck; //time of last update check //warnings void resetWarnings(); bool warningDependentFolders; bool warningSignificantDifference; + bool warningNotEnoughDiskSpace; } shared; //--------------------------------------------------------------------- @@ -114,10 +133,13 @@ namespace xmlAccess #elif defined FFS_LINUX commandLineFileManager(wxT("konqueror \"%path\"")), #endif - cfgHistoryMaxItems(10), + cfgHistoryMax(10), + folderHistLeftMax(12), + folderHistRightMax(12), deleteOnBothSides(false), useRecyclerForManualDeletion(FreeFileSync::recycleBinExists()), //enable if OS supports it; else user will have to activate first and then get an error message - showFileIcons(true) {} + showFileIcons(true), + popupOnConfigChange(true) {} int widthNotMaximized; int heightNotMaximized; @@ -127,14 +149,22 @@ namespace xmlAccess ColumnAttributes columnAttribLeft; ColumnAttributes columnAttribRight; + wxString commandLineFileManager; + std::vector<wxString> cfgFileHistory; - unsigned cfgHistoryMaxItems; + unsigned int cfgHistoryMax; + std::vector<wxString> folderHistoryLeft; + unsigned int folderHistLeftMax; + std::vector<wxString> folderHistoryRight; + unsigned int folderHistRightMax; + bool deleteOnBothSides; bool useRecyclerForManualDeletion; bool showFileIcons; + bool popupOnConfigChange; } gui; //--------------------------------------------------------------------- diff --git a/library/resources.cpp b/library/resources.cpp index f8624ed3..23d1ac37 100644 --- a/library/resources.cpp +++ b/library/resources.cpp @@ -83,7 +83,6 @@ GlobalResources::GlobalResources() bitmapResource[wxT("statusComparing.png")] = (bitmapStatusComparing = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("statusSyncing.png")] = (bitmapStatusSyncing = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("logo.png")] = (bitmapLogo = new wxBitmap(wxNullBitmap)); - bitmapResource[wxT("finished.png")] = (bitmapFinished = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("statusEdge.png")] = (bitmapStatusEdge = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("add pair.png")] = (bitmapAddFolderPair = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("remove pair.png")] = (bitmapRemoveFolderPair = new wxBitmap(wxNullBitmap)); @@ -105,12 +104,25 @@ GlobalResources::GlobalResources() bitmapResource[wxT("settings_small.png")] = (bitmapSettingsSmall = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("recycler.png")] = (bitmapRecycler = new wxBitmap(wxNullBitmap)); bitmapResource[wxT("shift.png")] = (bitmapShift = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("question.png")] = (bitmapQuestion = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("china.png")] = (bitmapChina = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("holland.png")] = (bitmapHolland = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("england.png")] = (bitmapEngland = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("france.png")] = (bitmapFrance = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("germany.png")] = (bitmapGermany = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("hungary.png")] = (bitmapHungary = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("italy.png")] = (bitmapItaly = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("japan.png")] = (bitmapJapan = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("poland.png")] = (bitmapPoland = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("portugal.png")] = (bitmapPortugal = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("brazil.png")] = (bitmapBrazil = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("slovakia.png")] = (bitmapSlovakia = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("spain.png")] = (bitmapSpain = new wxBitmap(wxNullBitmap)); //init all the other resource files animationMoney = new wxAnimation(wxNullAnimation); animationSync = new wxAnimation(wxNullAnimation); - - programIcon = &wxNullIcon; + programIcon = new wxIcon(wxNullIcon); } @@ -159,7 +171,7 @@ void GlobalResources::load() std::map<wxString, wxBitmap*>::iterator bmp; while ((entry = resourceFile.GetNextEntry())) { - wxString name = entry->GetName(); + const wxString name = entry->GetName(); //search if entry is available in map if ((bmp = bitmapResource.find(name)) != bitmapResource.end()) @@ -172,9 +184,9 @@ void GlobalResources::load() } #ifdef FFS_WIN - programIcon = new wxIcon(wxT("ffsIcon1")); + *programIcon = wxIcon(wxT("ffsIcon1")); #else #include "FreeFileSync.xpm" - programIcon = new wxIcon(FreeFileSync_xpm); + *programIcon = wxIcon(FreeFileSync_xpm); #endif } diff --git a/library/resources.h b/library/resources.h index 6bac0d86..5de79ec2 100644 --- a/library/resources.h +++ b/library/resources.h @@ -82,7 +82,6 @@ public: wxBitmap* bitmapStatusComparing; wxBitmap* bitmapStatusSyncing; wxBitmap* bitmapLogo; - wxBitmap* bitmapFinished; wxBitmap* bitmapStatusEdge; wxBitmap* bitmapAddFolderPair; wxBitmap* bitmapRemoveFolderPair; @@ -104,6 +103,20 @@ public: wxBitmap* bitmapSettingsSmall; wxBitmap* bitmapRecycler; wxBitmap* bitmapShift; + wxBitmap* bitmapQuestion; + wxBitmap* bitmapChina; + wxBitmap* bitmapHolland; + wxBitmap* bitmapEngland; + wxBitmap* bitmapFrance; + wxBitmap* bitmapGermany; + wxBitmap* bitmapHungary; + wxBitmap* bitmapItaly; + wxBitmap* bitmapJapan; + wxBitmap* bitmapPoland; + wxBitmap* bitmapPortugal; + wxBitmap* bitmapBrazil; + wxBitmap* bitmapSlovakia; + wxBitmap* bitmapSpain; wxAnimation* animationMoney; wxAnimation* animationSync; diff --git a/library/sorting.h b/library/sorting.h deleted file mode 100644 index 171cca6d..00000000 --- a/library/sorting.h +++ /dev/null @@ -1,325 +0,0 @@ -#ifndef SORTING_H_INCLUDED -#define SORTING_H_INCLUDED - -#include "../FreeFileSync.h" -#include "resources.h" -#include "globalFunctions.h" - -using namespace FreeFileSync; - -enum SideToSort -{ - SORT_ON_LEFT, - SORT_ON_RIGHT, -}; - - -template <SideToSort side> -inline -void getDescrLine(const FileCompareLine& a, const FileCompareLine& b, const FileDescrLine*& descrLineA, const FileDescrLine*& descrLineB) -{ - if (side == SORT_ON_LEFT) - { - descrLineA = &a.fileDescrLeft; - descrLineB = &b.fileDescrLeft; - } - else if (side == SORT_ON_RIGHT) - { - descrLineA = &a.fileDescrRight; - descrLineB = &b.fileDescrRight; - } - else - assert(false); -} - - -template <bool sortAscending> -inline -bool stringSmallerThan(const wxChar* stringA, const wxChar* stringB) -{ -#ifdef FFS_WIN //case-insensitive comparison! - return sortAscending ? - FreeFileSync::compareStringsWin32(stringA, stringB) < 0 : //way faster than wxString::CmpNoCase() in windows build!!! - FreeFileSync::compareStringsWin32(stringA, stringB) > 0; -#else - while (*stringA == *stringB) - { - if (*stringA == wxChar(0)) //strings are equal - return false; - - ++stringA; - ++stringB; - } - return sortAscending ? *stringA < *stringB : *stringA > *stringB; //wxChar(0) is handled correctly -#endif -} - - -inline -int compareString(const wxChar* stringA, const wxChar* stringB, const int lengthA, const int lengthB) -{ -#ifdef FFS_WIN //case-insensitive comparison! - return FreeFileSync::compareStringsWin32(stringA, stringB, lengthA, lengthB); //way faster than wxString::CmpNoCase() in the windows build!!! -#else - int i = 0; - if (lengthA == lengthB) - { - for (i = 0; i < lengthA; ++i) - { - if (stringA[i] != stringB[i]) - break; - } - return i == lengthA ? 0 : stringA[i] < stringB[i] ? -1 : 1; - } - else if (lengthA < lengthB) - { - for (i = 0; i < lengthA; ++i) - { - if (stringA[i] != stringB[i]) - break; - } - return i == lengthA ? -1 : stringA[i] < stringB[i] ? -1 : 1; - } - else - { - for (i = 0; i < lengthB; ++i) - { - if (stringA[i] != stringB[i]) - break; - } - return i == lengthB ? 1 : stringA[i] < stringB[i] ? -1 : 1; - } -#endif -} - - -template <bool sortAscending, SideToSort side> -inline -bool sortByFileName(const FileCompareLine& a, const FileCompareLine& b) -{ - const FileDescrLine* descrLineA = NULL; - const FileDescrLine* descrLineB = NULL; - getDescrLine<side>(a, b, descrLineA, descrLineB); - - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) - return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) - return true; //empty rows always last - - - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); - else - return false; - } - else - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; - else - { - const wxChar* stringA = descrLineA->relativeName.c_str(); - const wxChar* stringB = descrLineB->relativeName.c_str(); - - size_t pos = descrLineA->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end - if (pos != std::string::npos) - stringA += pos + 1; - - pos = descrLineB->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end - if (pos != std::string::npos) - stringB += pos + 1; - - return stringSmallerThan<sortAscending>(stringA, stringB); - } - } -} - - -template <bool sortAscending, SideToSort side> -bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b) -{ - const FileDescrLine* descrLineA = NULL; - const FileDescrLine* descrLineB = NULL; - getDescrLine<side>(a, b, descrLineA, descrLineB); - - //extract relative name and filename - const wxChar* relStringA = descrLineA->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly - const wxChar* fileStringA = relStringA; - int relLengthA = 0; - int fileLengthA = 0; - - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) - relLengthA = descrLineA->relativeName.length(); - else if (descrLineA->objType == FileDescrLine::TYPE_FILE) - { - relLengthA = descrLineA->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end - if (relLengthA == wxNOT_FOUND) - { - relLengthA = 0; - fileLengthA = descrLineA->relativeName.length(); - } - else - { - fileStringA += relLengthA + 1; - fileLengthA = descrLineA->relativeName.length() - (relLengthA + 1); - } - } - else - return false; //empty rows should be on end of list - - - const wxChar* relStringB = descrLineB->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly - const wxChar* fileStringB = relStringB; - int relLengthB = 0; - int fileLengthB = 0; - - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - relLengthB = descrLineB->relativeName.length(); - else if (descrLineB->objType == FileDescrLine::TYPE_FILE) - { - relLengthB = descrLineB->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end - if (relLengthB == wxNOT_FOUND) - { - relLengthB = 0; - fileLengthB = descrLineB->relativeName.length(); - } - else - { - fileStringB += relLengthB + 1; - fileLengthB = descrLineB->relativeName.length() - (relLengthB + 1); - } - } - else - return true; //empty rows should be on end of list - - //compare relative names without filenames first - int rv = compareString(relStringA, relStringB, relLengthA, relLengthB); - if (rv != 0) - return sortAscending ? (rv < 0) : (rv > 0); - else //compare the filenames - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) //directories shall appear before files - return false; - else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) - return true; - - return sortAscending ? - compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) < 0 : - compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) > 0; - } -} - - -template <bool sortAscending, SideToSort side> -inline -bool sortByFullName(const FileCompareLine& a, const FileCompareLine& b) -{ - const FileDescrLine* descrLineA = NULL; - const FileDescrLine* descrLineB = NULL; - getDescrLine<side>(a, b, descrLineA, descrLineB); - - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) - return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) - return true; //empty rows always last - else -#ifdef FFS_WIN //case-insensitive comparison! - return sortAscending ? - FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) < 0 : //way faster than wxString::CmpNoCase() in windows build!!! - FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) > 0; -#else - return sortAscending ? - descrLineA->fullName.Cmp(descrLineB->fullName) < 0 : - descrLineA->fullName.Cmp(descrLineB->fullName) > 0; -#endif -} - - -template <bool sortAscending, SideToSort side> -inline -bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b) -{ - const FileDescrLine* descrLineA = NULL; - const FileDescrLine* descrLineB = NULL; - getDescrLine<side>(a, b, descrLineA, descrLineB); - - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) - return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) - return true; //empty rows always last - - - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); - else - return false; - } - else - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; - else - return sortAscending ? - descrLineA->fileSize > descrLineB->fileSize : //sortAscending == true shall result in list beginning with largest files first - descrLineA->fileSize < descrLineB->fileSize; - } -} - - -template <bool sortAscending, SideToSort side> -inline -bool sortByDate(const FileCompareLine& a, const FileCompareLine& b) -{ - const FileDescrLine* descrLineA = NULL; - const FileDescrLine* descrLineB = NULL; - getDescrLine<side>(a, b, descrLineA, descrLineB); - - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) - return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) - return true; //empty rows always last - - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); - else - return false; - } - else - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; - else - return sortAscending ? - descrLineA->lastWriteTimeRaw < descrLineB->lastWriteTimeRaw : - descrLineA->lastWriteTimeRaw > descrLineB->lastWriteTimeRaw; - } -} - - -template <bool sortAscending> -inline -bool sortByCmpResult(const FileCompareLine& a, const FileCompareLine& b) -{ - //presort result: equal shall appear at end of list - if (a.cmpResult == FILE_EQUAL) - return false; - if (b.cmpResult == FILE_EQUAL) - return true; - - return sortAscending ? - a.cmpResult < b.cmpResult : - a.cmpResult > b.cmpResult; -} - - -#endif // SORTING_H_INCLUDED diff --git a/library/statistics.cpp b/library/statistics.cpp new file mode 100644 index 00000000..ec03c59e --- /dev/null +++ b/library/statistics.cpp @@ -0,0 +1,320 @@ +#include "statistics.h" + +#include <wx/ffile.h> +#include "globalFunctions.h" +#include "statusHandler.h" +#include "../algorithm.h" +#include <limits> + + +RetrieveStatistics::~RetrieveStatistics() +{ //write statistics to a file + wxFFile outputFile(wxT("statistics.dat"), wxT("w")); + + outputFile.Write(wxT("Time(ms);Objects;Data\n")); + + for (std::vector<statEntry>::const_iterator i = data.begin(); i != data.end(); ++i) + { + outputFile.Write(globalFunctions::numberToWxString(int(i->time))); + outputFile.Write(wxT(";")); + outputFile.Write(globalFunctions::numberToWxString(i->objects)); + outputFile.Write(wxT(";")); + outputFile.Write(globalFunctions::numberToWxString(float(i->value))); + outputFile.Write(wxT("\n")); + } +} + + +void RetrieveStatistics::writeEntry(const double value, const int objects) +{ + statEntry newEntry; + newEntry.value = value; + newEntry.objects = objects; + newEntry.time = timer.Time(); + data.push_back(newEntry); +} + + +//######################################################################################## + +inline +bool isNull(const double number) +{ + return globalFunctions::abs(number) <= std::numeric_limits<double>::epsilon(); +} + + +inline +wxString Statistics::formatRemainingTime(const double timeInMs) const +{ + bool unitSec = true; + double remainingTime = timeInMs / 1000; + wxString unit = _(" sec"); + if (remainingTime > 55) + { + unitSec = false; + remainingTime /= 60; + unit = _(" min"); + if (remainingTime > 59) + { + remainingTime /= 60; + unit = _(" hour(s)"); + if (remainingTime > 23) + { + remainingTime /= 24; + unit = _(" day(s)"); + } + } + } + + + int formattedTime = globalFunctions::round(remainingTime); + + //reduce precision to 5 seconds + if (unitSec && formattedTime % 5 != 0) + formattedTime += 5 - formattedTime % 5; //"ceiling" + + + //avoid "jumping back and forth" when fluctuating around .5 + if (remainingTimeLast < formattedTime) + { + if (unitSec) + { + formattedTime = globalFunctions::round(remainingTime); + formattedTime -= formattedTime % 5; //"floor" + } + else + formattedTime = int(remainingTime); //"floor" + } + remainingTimeLast = formattedTime; + + return globalFunctions::numberToWxString(formattedTime) + unit; + //+ wxT("(") + globalFunctions::numberToWxString(globalFunctions::round(timeInMs / 1000)) + wxT(")"); +} + + +Statistics::Statistics(const int totalObjectCount, + const double totalDataAmount, + const unsigned windowSizeRemainingTime, + const unsigned windowSizeBytesPerSecond) : + objectsTotal(totalObjectCount), + dataTotal(totalDataAmount), + windowSizeRemTime(windowSizeRemainingTime), + windowSizeBPS(windowSizeBytesPerSecond), + windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)), + remainingTimeLast(256*256*256*100) //something "big" +{} + + +void Statistics::addMeasurement(const int objectsCurrent, const double dataCurrent) +{ + record newEntry; + newEntry.objects = objectsCurrent; + newEntry.data = dataCurrent; + newEntry.time = timer.Time(); + + //insert new record + measurements.push_back(newEntry); + + //remove all records earlier than "currentTime - windowSize" + const long newBegin = newEntry.time - windowMax; + while (measurements.size() > 0 && measurements.front().time < newBegin) + measurements.pop_front(); +} + + +wxString Statistics::getRemainingTime() const +{ + if (measurements.size() > 0) + { + //find start of records "window" + const record backElement = measurements.back(); + const long frontTime = backElement.time - windowSizeRemTime; + std::list<record>::const_iterator frontElement = measurements.end(); + do + { + --frontElement; + } + while (frontElement != measurements.begin() && frontElement->time > frontTime); + + const double timeDelta = backElement.time - frontElement->time; + const double dataDelta = backElement.data - frontElement->data; + + const double dataRemaining = dataTotal - backElement.data; + + if (!isNull(dataDelta)) + return formatRemainingTime(dataRemaining * timeDelta / dataDelta); + } + + return wxT("-"); //fallback +} + + +wxString Statistics::getBytesPerSecond() const +{ + if (measurements.size() > 0) + { + //find start of records "window" + const long frontTime = measurements.back().time - windowSizeBPS; + std::list<record>::const_iterator frontElement = measurements.end(); + do + { + --frontElement; + } + while (frontElement != measurements.begin() && frontElement->time > frontTime); + + const double timeDelta = measurements.back().time - frontElement->time; + const double dataDelta = measurements.back().data - frontElement->data; + + if (!isNull(timeDelta)) + return FreeFileSync::formatFilesizeToShortString(dataDelta * 1000 / timeDelta) + _("/sec"); + } + + return wxT("-"); //fallback +} + + +void Statistics::pauseTimer() +{ + timer.Pause(); +} + + +void Statistics::resumeTimer() +{ + timer.Resume(); +} + +/* +class for calculation of remaining time: +---------------------------------------- +"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x + +For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by + +z_1 = (r - s * q / p) / ((n + 1) - s * s / p) +z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s + +with +p := x_0^2 + ... + x_n^2 +q := f_0 x_0 + ... + f_n x_n +r := f_0 + ... + f_n +s := x_0 + ... + x_n + +=> the time to process N files with amount of data D is: N * z_1 + D * z_2 + +Problem: +-------- +Times f_0, ..., f_n can be very small so that precision of the PC clock is poor. +=> Times have to be accumulated to enhance precision: +Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i +With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...) +m * z_1 + z_2 * X = F <=> +z_1 + z_2 * X / m = F / m + +=> we obtain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above + + +Statistics::Statistics(const int totalObjectCount, const double totalDataAmount, const unsigned recordCount) : + objectsTotal(totalObjectCount), + dataTotal(totalDataAmount), + recordsMax(recordCount), + objectsLast(0), + dataLast(0), + timeLast(wxGetLocalTimeMillis()), + z1_current(0), + z2_current(0), + dummyRecordPresent(false) {} + + +wxString Statistics::getRemainingTime(const int objectsCurrent, const double dataCurrent) +{ + //add new measurement point + const int m = objectsCurrent - objectsLast; + if (m != 0) + { + objectsLast = objectsCurrent; + + const double X = dataCurrent - dataLast; + dataLast = dataCurrent; + + const wxLongLong timeCurrent = wxGetLocalTimeMillis(); + const double F = (timeCurrent - timeLast).ToDouble(); + timeLast = timeCurrent; + + record newEntry; + newEntry.x_i = X / m; + newEntry.f_i = F / m; + + //remove dummy record + if (dummyRecordPresent) + { + measurements.pop_back(); + dummyRecordPresent = false; + } + + //insert new record + measurements.push_back(newEntry); + if (measurements.size() > recordsMax) + measurements.pop_front(); + } + else //dataCurrent increased without processing new objects: + { //modify last measurement until m != 0 + const double X = dataCurrent - dataLast; //do not set dataLast, timeLast variables here, but write dummy record instead + if (!isNull(X)) + { + const wxLongLong timeCurrent = wxGetLocalTimeMillis(); + const double F = (timeCurrent - timeLast).ToDouble(); + + record modifyEntry; + modifyEntry.x_i = X; + modifyEntry.f_i = F; + + //insert dummy record + if (!dummyRecordPresent) + { + measurements.push_back(modifyEntry); + if (measurements.size() > recordsMax) + measurements.pop_front(); + dummyRecordPresent = true; + } + else //modify dummy record + measurements.back() = modifyEntry; + } + } + + //calculate remaining time based on stored measurement points + double p = 0; + double q = 0; + double r = 0; + double s = 0; + for (std::list<record>::const_iterator i = measurements.begin(); i != measurements.end(); ++i) + { + const double x_i = i->x_i; + const double f_i = i->f_i; + p += x_i * x_i; + q += f_i * x_i; + r += f_i; + s += x_i; + } + + if (!isNull(p)) + { + const double n = measurements.size(); + const double tmp = (n - s * s / p); + + if (!isNull(tmp) && !isNull(s)) + { + const double z1 = (r - s * q / p) / tmp; + const double z2 = (r - n * z1) / s; //not (n + 1) here, since n already is the number of measurements + + //refresh current values for z1, z2 + z1_current = z1; + z2_current = z2; + } + } + + return formatRemainingTime((objectsTotal - objectsCurrent) * z1_current + (dataTotal - dataCurrent) * z2_current); +} + +*/ diff --git a/library/statistics.h b/library/statistics.h new file mode 100644 index 00000000..3d4179db --- /dev/null +++ b/library/statistics.h @@ -0,0 +1,67 @@ +#ifndef STATISTICS_H_INCLUDED +#define STATISTICS_H_INCLUDED + +#include <vector> +#include <wx/stopwatch.h> +#include <list> + +class RetrieveStatistics +{ +public: + wxDEPRECATED(RetrieveStatistics() {}) //generate compiler warnings as a reminder to remove code after measurements + ~RetrieveStatistics(); + + void writeEntry(const double value, const int objects); + +private: + struct statEntry + { + long time; + int objects; + double value; + }; + + std::vector<statEntry> data; + wxStopWatch timer; +}; + + +class Statistics +{ +public: + Statistics(const int totalObjectCount, + const double totalDataAmount, + const unsigned windowSizeRemainingTime, //time in ms + const unsigned windowSizeBytesPerSecond); //time in ms + + void addMeasurement(const int objectsCurrent, const double dataCurrent); + wxString getRemainingTime() const; //returns the remaining time in milliseconds + wxString getBytesPerSecond() const; + + void pauseTimer(); + void resumeTimer(); + +private: + wxString formatRemainingTime(const double timeInMs) const; + + const int objectsTotal; + const double dataTotal; + + const unsigned windowSizeRemTime; //"window width" of statistics used for calculation of remaining time in ms + const unsigned windowSizeBPS; // + const unsigned windowMax; + + mutable int remainingTimeLast; //used for "smoothening" remaining time + + struct record + { + int objects; + double data; + long time; + }; + + std::list<record> measurements; + wxStopWatch timer; +}; + +#endif // STATISTICS_H_INCLUDED diff --git a/library/statusHandler.h b/library/statusHandler.h index 11517efb..18d9f129 100644 --- a/library/statusHandler.h +++ b/library/statusHandler.h @@ -1,7 +1,9 @@ #ifndef STATUSHANDLER_H_INCLUDED #define STATUSHANDLER_H_INCLUDED -#include "zstring.h" +#include <wx/longlong.h> + +class Zstring; const int UI_UPDATE_INTERVAL = 100; //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss @@ -45,8 +47,8 @@ public: //these methods have to be implemented in the derived classes to handle error and status information virtual void updateStatusText(const Zstring& text) = 0; - virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on - virtual void updateProcessedData(int objectsProcessed, double dataProcessed) = 0; //called periodically after data was processed + virtual void initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on + virtual void updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) = 0; //called periodically after data was processed //this method is triggered repeatedly by requestUiRefresh() and can be used to refresh the ui by dispatching pending events virtual void forceUiRefresh() = 0; diff --git a/library/zstring.cpp b/library/zstring.cpp index d704e741..abded9d0 100644 --- a/library/zstring.cpp +++ b/library/zstring.cpp @@ -2,23 +2,28 @@ #include <wx/intl.h> #include "globalFunctions.h" - #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" #endif //FFS_WIN -#ifdef __WXDEBUG__ -int allocCount = 0; //test Zstring for memory leaks -void testZstringForMemoryLeak() +#ifdef __WXDEBUG__ +AllocationCount::~AllocationCount() { - if (allocCount != 0) + if (count != 0) #ifdef FFS_WIN - MessageBox(NULL, wxT("Fatal Error! Allocation problem with Zstring! (No problem if it occures while Unit testing only!)"), wxString::Format(wxT("%i"), allocCount), 0); + MessageBox(NULL, wxT("Fatal Error! Allocation problem with Zstring! (No problem if it occurs while Unit testing only!)"), wxString::Format(wxT("%i"), count), 0); #else - throw; -#endif //FFS_WIN + std::abort(); +#endif +} + + +AllocationCount& AllocationCount::getGlobal() +{ + static AllocationCount global; + return global; } #endif @@ -45,11 +50,10 @@ int FreeFileSync::compareStringsWin32(const wchar_t* a, const wchar_t* b, const #endif -size_t Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll) +Zstring& Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll) { const size_t oldLen = defaultLength(old); const size_t replacementLen = defaultLength(replacement); - size_t uiCount = 0; //count of replacements made size_t pos = 0; while (true) @@ -61,13 +65,11 @@ size_t Zstring::Replace(const DefaultChar* old, const DefaultChar* replacement, replace(pos, oldLen, replacement, replacementLen); pos += replacementLen; //move past the string that was replaced - ++uiCount; //increase replace count - // stop now? if (!replaceAll) break; } - return uiCount; + return *this; } diff --git a/library/zstring.h b/library/zstring.h index 2a10efa9..2b6bc475 100644 --- a/library/zstring.h +++ b/library/zstring.h @@ -61,7 +61,7 @@ public: #endif int Cmp(const DefaultChar* other) const; int Cmp(const Zstring& other) const; - size_t Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll = true); + Zstring& Replace(const DefaultChar* old, const DefaultChar* replacement, bool replaceAll = true); Zstring AfterLast(DefaultChar ch) const; Zstring BeforeLast(DefaultChar ch) const; size_t Find(DefaultChar ch, bool fromEnd) const; @@ -87,10 +87,12 @@ public: Zstring& operator=(const Zstring& source); Zstring& operator=(const DefaultChar* source); - bool operator==(const Zstring& other) const; - bool operator==(const DefaultChar* other) const; - bool operator!=(const Zstring& other) const; - bool operator!=(const DefaultChar* other) const; + bool operator == (const Zstring& other) const; + bool operator == (const DefaultChar* other) const; + bool operator < (const Zstring& other) const; + bool operator < (const DefaultChar* other) const; + bool operator != (const Zstring& other) const; + bool operator != (const DefaultChar* other) const; DefaultChar operator[](const size_t pos) const; @@ -98,9 +100,9 @@ public: Zstring& operator+=(const DefaultChar* other); Zstring& operator+=(DefaultChar ch); - Zstring operator+(const Zstring& string2) const; - Zstring operator+(const DefaultChar* string2) const; - Zstring operator+(const DefaultChar ch) const; + const Zstring operator+(const Zstring& string2) const; + const Zstring operator+(const DefaultChar* string2) const; + const Zstring operator+(const DefaultChar ch) const; static const size_t npos = static_cast<size_t>(-1); @@ -235,8 +237,27 @@ wchar_t defaultToLower(const wchar_t ch) #ifdef __WXDEBUG__ -extern int allocCount; //test Zstring for memory leaks -void testZstringForMemoryLeak(); +class AllocationCount //small test for memory leaks in Zstring +{ +public: + void inc() + { + ++count; + } + + void dec() + { + --count; + } + + static AllocationCount& getGlobal(); + +private: + AllocationCount() : count(0) {} + ~AllocationCount(); + + int count; +}; #endif @@ -265,14 +286,7 @@ void Zstring::allocate(const size_t newLength, newDescr->capacity = newCapacity; #ifdef __WXDEBUG__ - ++allocCount; //test Zstring for memory leaks - - static bool isRegistered = false; - if (!isRegistered) - { - isRegistered = true; - atexit(testZstringForMemoryLeak); - } + AllocationCount::getGlobal().inc(); //test Zstring for memory leaks #endif } @@ -287,7 +301,7 @@ Zstring::Zstring() static Zstring emptyString(L""); #endif - emptyString.incRef(); //implicitly handle case "this == &source" and avoid this check + emptyString.incRef(); descr = emptyString.descr; data = emptyString.data; } @@ -347,9 +361,9 @@ void Zstring::decRef() if (--descr->refCount == 0) { free(descr); //this must NEVER be changed!! E.g. Trim() relies on descr being start of allocated memory block - descr = 0; + descr = NULL; #ifdef __WXDEBUG__ - --allocCount; //test Zstring for memory leaks + AllocationCount::getGlobal().dec(); //test Zstring for memory leaks #endif } } @@ -504,28 +518,42 @@ int Zstring::Cmp(const Zstring& other) const inline -bool Zstring::operator==(const Zstring& other) const +bool Zstring::operator == (const Zstring& other) const { return length() != other.length() ? false : defaultCompare(c_str(), other.c_str()) == 0; } inline -bool Zstring::operator==(const DefaultChar* other) const +bool Zstring::operator == (const DefaultChar* other) const { return defaultCompare(c_str(), other) == 0; //overload using strcmp(char*, char*) should be fastest! } inline -bool Zstring::operator!=(const Zstring& other) const +bool Zstring::operator < (const Zstring& other) const +{ + return defaultCompare(c_str(), other.c_str()) < 0; +} + + +inline +bool Zstring::operator < (const DefaultChar* other) const +{ + return defaultCompare(c_str(), other) < 0; //overload using strcmp(char*, char*) should be fastest! +} + + +inline +bool Zstring::operator != (const Zstring& other) const { return length() != other.length() ? true: defaultCompare(c_str(), other.c_str()) != 0; } inline -bool Zstring::operator!=(const DefaultChar* other) const +bool Zstring::operator != (const DefaultChar* other) const { return defaultCompare(c_str(), other) != 0; //overload using strcmp(char*, char*) should be fastest! } @@ -590,21 +618,21 @@ DefaultChar Zstring::operator[](const size_t pos) const inline -Zstring Zstring::operator+(const Zstring& string2) const +const Zstring Zstring::operator+(const Zstring& string2) const { return Zstring(*this)+=string2; } inline -Zstring Zstring::operator+(const DefaultChar* string2) const +const Zstring Zstring::operator+(const DefaultChar* string2) const { return Zstring(*this)+=string2; } inline -Zstring Zstring::operator+(const DefaultChar ch) const +const Zstring Zstring::operator+(const DefaultChar ch) const { return Zstring(*this)+=ch; } diff --git a/structures.cpp b/structures.cpp new file mode 100644 index 00000000..e90ffe58 --- /dev/null +++ b/structures.cpp @@ -0,0 +1,12 @@ +#include "structures.h" +#include "library/fileHandling.h" + +using namespace FreeFileSync; + +MainConfiguration::MainConfiguration() : + compareVar(CMP_BY_TIME_SIZE), + filterIsActive(false), //do not filter by default + includeFilter(wxT("*")), //include all files/folders + excludeFilter(wxEmptyString), //exclude nothing + useRecycleBin(FreeFileSync::recycleBinExists()) {} //enable if OS supports it; else user will have to activate first and then get an error message + diff --git a/FreeFileSync.h b/structures.h index a15be076..9fe6c823 100644 --- a/FreeFileSync.h +++ b/structures.h @@ -4,8 +4,9 @@ #include <wx/string.h> #include <set> #include <vector> -#include "library/fileHandling.h" #include "library/zstring.h" +#include <wx/longlong.h> + namespace FreeFileSync { @@ -37,17 +38,21 @@ namespace FreeFileSync Direction leftNewer; Direction rightNewer; Direction different; + + bool operator==(const SyncConfiguration& other) const + { + return exLeftSideOnly == other.exLeftSideOnly && + exRightSideOnly == other.exRightSideOnly && + leftNewer == other.leftNewer && + rightNewer == other.rightNewer && + different == other.different; + } }; struct MainConfiguration { - MainConfiguration() : - compareVar(CMP_BY_TIME_SIZE), - filterIsActive(false), //do not filter by default - includeFilter(wxT("*")), //include all files/folders - excludeFilter(wxEmptyString), //exclude nothing - useRecycleBin(FreeFileSync::recycleBinExists()) {} //enable if OS supports it; else user will have to activate first and then get an error message + MainConfiguration(); //Compare setting CompareVariant compareVar; @@ -62,6 +67,34 @@ namespace FreeFileSync //misc options bool useRecycleBin; //use Recycle bin when deleting or overwriting files while synchronizing + + bool operator==(const MainConfiguration& other) const + { + return compareVar == other.compareVar && + syncConfiguration == other.syncConfiguration && + filterIsActive == other.filterIsActive && + includeFilter == other.includeFilter && + excludeFilter == other.excludeFilter; + } + }; + + + struct FolderPair + { + FolderPair() {} + + FolderPair(const Zstring& leftDir, const Zstring& rightDir) : + leftDirectory(leftDir), + rightDirectory(rightDir) {} + + Zstring leftDirectory; + Zstring rightDirectory; + + bool operator==(const FolderPair& other) const + { + return leftDirectory == other.leftDirectory && + rightDirectory == other.rightDirectory; + } }; @@ -77,17 +110,14 @@ namespace FreeFileSync }; Zstring fullName; // == directory + relativeName - Zstring directory; //directory to be synced + separator Zsubstr relativeName; //fullName without directory that is being synchronized - //Note on performance: Keep redundant information "directory" and "relativeName"! + //Note on performance: Keep redundant information "relativeName"! //Extracting info from "fullName" instead would result in noticeable performance loss, with only limited memory reduction (note ref. counting strings)! wxLongLong lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) wxULongLong fileSize; ObjectType objType; //is it a file or directory or initial? - //the following operators are needed by template class "set" - //DO NOT CHANGE THESE RELATIONS!!! - bool operator < (const FileDescrLine& b) const + bool operator < (const FileDescrLine& b) const //used by template class "set" { //quick check based on string length: we are not interested in a lexicographical order! const size_t aLength = relativeName.length(); @@ -123,22 +153,22 @@ namespace FreeFileSync FileDescrLine fileDescrLeft; FileDescrLine fileDescrRight; - CompareFilesResult cmpResult; + bool selectedForSynchronization; }; - typedef std::vector<FileCompareLine> FileCompareResult; + typedef std::vector<FileCompareLine> FileComparison; - typedef int GridViewLine; - typedef std::vector<GridViewLine> GridView; //vector of references to lines in FileCompareResult - - - struct FolderPair + struct FolderCompareLine //support for multiple folder pairs { - Zstring leftDirectory; - Zstring rightDirectory; + FolderPair syncPair; //directories to be synced (ending with separator) + FileComparison fileCmp; }; + typedef std::vector<FolderCompareLine> FolderComparison; + + //References to single lines(in FileComparison) inside FolderComparison + typedef std::vector<std::set<int> > FolderCompRef; class AbortThisProcess //Exception class used to abort the "compare" and "sync" process @@ -147,9 +177,6 @@ namespace FreeFileSync AbortThisProcess() {} ~AbortThisProcess() {} }; - - const wxString LAST_CONFIG_FILE = wxT("LastRun.ffs_gui"); - const wxString GLOBAL_CONFIG_FILE = wxT("GlobalSettings.xml"); } #endif // FREEFILESYNC_H_INCLUDED diff --git a/synchronization.cpp b/synchronization.cpp index 0fbaf930..b8f2d6a6 100644 --- a/synchronization.cpp +++ b/synchronization.cpp @@ -2,9 +2,12 @@ #include <wx/intl.h> #include <wx/msgdlg.h> #include <wx/log.h> -#include "library/multithreading.h" #include "library/resources.h" #include "algorithm.h" +#include "library/globalFunctions.h" +#include "library/statusHandler.h" +#include "library/fileHandling.h" +#include <utility> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -39,20 +42,19 @@ SyncConfiguration::Direction getSyncDirection(const CompareFilesResult cmpResult break; default: - assert (false); + return SyncConfiguration::SYNC_DIR_NONE; } - return SyncConfiguration::SYNC_DIR_NONE; } inline -bool getBytesToTransfer(int& objectsToCreate, +bool getBytesToTransfer(const FileCompareLine& fileCmpLine, + const SyncConfiguration& config, + int& objectsToCreate, int& objectsToOverwrite, int& objectsToDelete, - double& dataToProcess, - const FileCompareLine& fileCmpLine, - const SyncConfiguration& config) -{ //false if nothing has to be done + wxULongLong& dataToProcess) +{ //return false if nothing has to be done objectsToCreate = 0; //always initialize variables objectsToOverwrite = 0; @@ -75,7 +77,7 @@ bool getBytesToTransfer(int& objectsToCreate, objectsToDelete = 1; break; case SyncConfiguration::SYNC_DIR_RIGHT: //copy from left to right - dataToProcess = fileCmpLine.fileDescrLeft.fileSize.ToDouble(); + dataToProcess = fileCmpLine.fileDescrLeft.fileSize; objectsToCreate = 1; break; case SyncConfiguration::SYNC_DIR_NONE: @@ -87,7 +89,7 @@ bool getBytesToTransfer(int& objectsToCreate, switch (getSyncDirection(fileCmpLine.cmpResult, config)) { case SyncConfiguration::SYNC_DIR_LEFT: //copy from right to left - dataToProcess = fileCmpLine.fileDescrRight.fileSize.ToDouble();; + dataToProcess = fileCmpLine.fileDescrRight.fileSize; objectsToCreate = 1; break; case SyncConfiguration::SYNC_DIR_RIGHT: //delete file on right @@ -106,11 +108,11 @@ bool getBytesToTransfer(int& objectsToCreate, switch (getSyncDirection(fileCmpLine.cmpResult, config)) { case SyncConfiguration::SYNC_DIR_LEFT: //copy from right to left - dataToProcess = fileCmpLine.fileDescrRight.fileSize.ToDouble(); + dataToProcess = fileCmpLine.fileDescrRight.fileSize; objectsToOverwrite = 1; break; case SyncConfiguration::SYNC_DIR_RIGHT: //copy from left to right - dataToProcess = fileCmpLine.fileDescrLeft.fileSize.ToDouble(); + dataToProcess = fileCmpLine.fileDescrLeft.fileSize; objectsToOverwrite = 1; break; case SyncConfiguration::SYNC_DIR_NONE: @@ -120,6 +122,7 @@ bool getBytesToTransfer(int& objectsToCreate, case FILE_EQUAL: return false; + default: assert(false); return false; @@ -129,12 +132,13 @@ bool getBytesToTransfer(int& objectsToCreate, } -void FreeFileSync::calcTotalBytesToSync(const FileCompareResult& fileCmpResult, - const SyncConfiguration& config, - int& objectsToCreate, - int& objectsToOverwrite, - int& objectsToDelete, - double& dataToProcess) +//runs at folder pair level +void calcBytesToSync(const FileComparison& fileCmp, + const SyncConfiguration& config, + int& objectsToCreate, + int& objectsToOverwrite, + int& objectsToDelete, + wxULongLong& dataToProcess) { objectsToCreate = 0; objectsToOverwrite = 0; @@ -144,11 +148,11 @@ void FreeFileSync::calcTotalBytesToSync(const FileCompareResult& fileCmpResult, int toCreate = 0; int toOverwrite = 0; int toDelete = 0; - double data = 0; + wxULongLong data; - for (FileCompareResult::const_iterator i = fileCmpResult.begin(); i != fileCmpResult.end(); ++i) + for (FileComparison::const_iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) { //only sum up sizes of files AND directories - if (getBytesToTransfer(toCreate, toOverwrite, toDelete, data, *i, config)) + if (getBytesToTransfer(*i, config, toCreate, toOverwrite, toDelete, data)) { objectsToCreate += toCreate; objectsToOverwrite += toOverwrite; @@ -159,39 +163,118 @@ void FreeFileSync::calcTotalBytesToSync(const FileCompareResult& fileCmpResult, } -bool FreeFileSync::synchronizationNeeded(const FileCompareResult& fileCmpResult, const SyncConfiguration& config) +//aggregate over all folder pairs +void FreeFileSync::calcTotalBytesToSync(const FolderComparison& folderCmp, + const SyncConfiguration& config, + int& objectsToCreate, + int& objectsToOverwrite, + int& objectsToDelete, + wxULongLong& dataToProcess) { - int objectsToCreate = 0; - int objectsToOverwrite = 0; - int objectsToDelete = 0; - double dataToProcess = 0; - FreeFileSync::calcTotalBytesToSync(fileCmpResult, + objectsToCreate = 0; + objectsToOverwrite = 0; + objectsToDelete = 0; + dataToProcess = 0; + + for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + { + const FileComparison& fileCmp = j->fileCmp; + + int toCreate = 0; + int toOverwrite = 0; + int toDelete = 0; + wxULongLong data; + + calcBytesToSync(fileCmp, config, toCreate, toOverwrite, toDelete, data); + + objectsToCreate += toCreate; + objectsToOverwrite += toOverwrite; + objectsToDelete += toDelete; + dataToProcess += data; + } +} + + +template <bool recyclerUsed> +std::pair<wxLongLong, wxLongLong> spaceNeededSub(const FileComparison& fileCmp, const SyncConfiguration& config) +{ + wxLongLong spaceNeededLeft; + wxLongLong spaceNeededRight; + + for (FileComparison::const_iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) + { + if (i->selectedForSynchronization) //do not add filtered entries + { + //get data to process + switch (getSyncDirection(i->cmpResult, config)) + { + case SyncConfiguration::SYNC_DIR_LEFT: //copy from right to left + if (!recyclerUsed) + spaceNeededLeft -= globalFunctions::convertToSigned(i->fileDescrLeft.fileSize); + spaceNeededLeft += globalFunctions::convertToSigned(i->fileDescrRight.fileSize); + break; + + case SyncConfiguration::SYNC_DIR_RIGHT: //copy from left to right + if (!recyclerUsed) + spaceNeededRight -= globalFunctions::convertToSigned(i->fileDescrRight.fileSize); + spaceNeededRight += globalFunctions::convertToSigned(i->fileDescrLeft.fileSize); + break; + + case SyncConfiguration::SYNC_DIR_NONE: + break; + } + } + } + + return std::pair<wxLongLong, wxLongLong>(spaceNeededLeft, spaceNeededRight); +} + + +std::pair<wxLongLong, wxLongLong> freeDiskSpaceNeeded(const FileComparison& fileCmp, const SyncConfiguration& config, const bool recyclerUsed) +{ + if (recyclerUsed) + return spaceNeededSub<true>(fileCmp, config); + else + return spaceNeededSub<false>(fileCmp, config); +} + + +bool FreeFileSync::synchronizationNeeded(const FolderComparison& folderCmp, const SyncConfiguration& config) +{ + int objectsToCreate = 0; + int objectsToOverwrite = 0; + int objectsToDelete = 0; + wxULongLong dataToProcess; + + FreeFileSync::calcTotalBytesToSync(folderCmp, config, objectsToCreate, objectsToOverwrite, objectsToDelete, dataToProcess); + return objectsToCreate + objectsToOverwrite + objectsToDelete != 0; } //test if more then 50% of files will be deleted/overwritten -bool significantDifferenceDetected(const FileCompareResult& fileCmpResult, const SyncConfiguration& config) +bool significantDifferenceDetected(const FileComparison& fileCmp, const SyncConfiguration& config) { - int objectsToCreate = 0; - int objectsToOverwrite = 0; - int objectsToDelete = 0; - double dataToProcess = 0; - FreeFileSync::calcTotalBytesToSync(fileCmpResult, - config, - objectsToCreate, - objectsToOverwrite, - objectsToDelete, - dataToProcess); + int objectsToCreate = 0; + int objectsToOverwrite = 0; + int objectsToDelete = 0; + wxULongLong dataToProcess; + + calcBytesToSync(fileCmp, + config, + objectsToCreate, + objectsToOverwrite, + objectsToDelete, + dataToProcess); - const int changedFiles = objectsToOverwrite + objectsToDelete; + const int changedFiles = objectsToCreate + objectsToOverwrite + objectsToDelete; //include objectsToCreate also! - return changedFiles >= 10 && changedFiles > .5 * fileCmpResult.size(); + return changedFiles >= 10 && changedFiles > 0.5 * fileCmp.size(); } @@ -199,30 +282,23 @@ SyncProcess::SyncProcess(const bool useRecycler, const bool copyFileSymLinks, const bool traverseDirSymLinks, bool& warningSignificantDifference, + bool& warningNotEnoughDiskSpace, StatusHandler* handler) : m_useRecycleBin(useRecycler), m_copyFileSymLinks(copyFileSymLinks), m_traverseDirSymLinks(traverseDirSymLinks), m_warningSignificantDifference(warningSignificantDifference), + m_warningNotEnoughDiskSpace(warningNotEnoughDiskSpace), statusUpdater(handler), - txtCopyingFile(_("Copying file %x to %y")), - txtOverwritingFile(_("Copying file %x overwriting %y")), - txtCreatingFolder(_("Creating folder %x")), - txtDeletingFile(_("Deleting file %x")), - txtDeletingFolder(_("Deleting folder %x")) -{ - txtCopyingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false); - txtCopyingFile.Replace( wxT("%y"), wxT("\n\"%y\""), false); - txtOverwritingFile.Replace(wxT("%x"), wxT("\n\"%x\""), false); - txtOverwritingFile.Replace(wxT("%y"), wxT("\n\"%y\""), false); - txtCreatingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false); - txtDeletingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false); - txtDeletingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false); -} + txtCopyingFile(Zstring(_("Copying file %x to %y")).Replace(wxT("%x"), wxT("\"%x\""), false).Replace(wxT("%y"), wxT("\n\"%y\""), false)), + txtOverwritingFile(Zstring(_("Copying file %x to %y overwriting target")).Replace(wxT("%x"), wxT("\"%x\""), false).Replace(wxT("%y"), wxT("\n\"%y\""), false)), + txtCreatingFolder(Zstring(_("Creating folder %x")).Replace( wxT("%x"), wxT("\n\"%x\""), false)), + txtDeletingFile(Zstring(_("Deleting file %x")).Replace(wxT("%x"), wxT("\n\"%x\""), false)), + txtDeletingFolder(Zstring(_("Deleting folder %x")).Replace( wxT("%x"), wxT("\n\"%x\""), false)) {} inline -bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConfiguration& config) +bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConfiguration& config, const FolderPair& folderPair) { //return false if nothing had to be done if (!cmpLine.selectedForSynchronization) return false; @@ -244,19 +320,17 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf removeFile(cmpLine.fileDescrLeft.fullName, m_useRecycleBin); break; case SyncConfiguration::SYNC_DIR_RIGHT: //copy files to right - target = cmpLine.fileDescrRight.directory + cmpLine.fileDescrLeft.relativeName.c_str(); + target = folderPair.rightDirectory + cmpLine.fileDescrLeft.relativeName.c_str(); statusText = txtCopyingFile; - statusText.Replace(wxT("%x"), cmpLine.fileDescrLeft.fullName, false); - statusText.Replace(wxT("%y"), target, false); + statusText.Replace(wxT("%x"), cmpLine.fileDescrLeft.fullName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR), false); + statusText.Replace(wxT("%y"), target.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR), false); statusUpdater->updateStatusText(statusText); copyFileUpdating(cmpLine.fileDescrLeft.fullName, target, cmpLine.fileDescrLeft.fileSize); break; case SyncConfiguration::SYNC_DIR_NONE: return false; - default: - assert (false); } break; @@ -264,11 +338,11 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf switch (config.exRightSideOnly) { case SyncConfiguration::SYNC_DIR_LEFT: //copy files to left - target = cmpLine.fileDescrLeft.directory + cmpLine.fileDescrRight.relativeName.c_str(); + target = folderPair.leftDirectory + cmpLine.fileDescrRight.relativeName.c_str(); statusText = txtCopyingFile; - statusText.Replace(wxT("%x"), cmpLine.fileDescrRight.fullName, false); - statusText.Replace(wxT("%y"), target, false); + statusText.Replace(wxT("%x"), cmpLine.fileDescrRight.fullName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR), false); + statusText.Replace(wxT("%y"), target.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR), false); statusUpdater->updateStatusText(statusText); copyFileUpdating(cmpLine.fileDescrRight.fullName, target, cmpLine.fileDescrRight.fileSize); @@ -282,8 +356,6 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf break; case SyncConfiguration::SYNC_DIR_NONE: return false; - default: - assert (false); } break; @@ -294,8 +366,8 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf { case SyncConfiguration::SYNC_DIR_LEFT: //copy from right to left statusText = txtOverwritingFile; - statusText.Replace(wxT("%x"), cmpLine.fileDescrRight.fullName, false); - statusText.Replace(wxT("%y"), cmpLine.fileDescrLeft.fullName, false); + statusText.Replace(wxT("%x"), cmpLine.fileDescrRight.fullName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR), false); + statusText.Replace(wxT("%y"), cmpLine.fileDescrLeft.fullName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR), false); statusUpdater->updateStatusText(statusText); removeFile(cmpLine.fileDescrLeft.fullName, m_useRecycleBin); //only used if switch activated by user, else file is simply deleted @@ -303,8 +375,8 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf break; case SyncConfiguration::SYNC_DIR_RIGHT: //copy from left to right statusText = txtOverwritingFile; - statusText.Replace(wxT("%x"), cmpLine.fileDescrLeft.fullName, false); - statusText.Replace(wxT("%y"), cmpLine.fileDescrRight.fullName, false); + statusText.Replace(wxT("%x"), cmpLine.fileDescrLeft.fullName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR), false); + statusText.Replace(wxT("%y"), cmpLine.fileDescrRight.fullName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR), false); statusUpdater->updateStatusText(statusText); removeFile(cmpLine.fileDescrRight.fullName, m_useRecycleBin); //only used if switch activated by user, else file is simply deleted @@ -312,8 +384,6 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf break; case SyncConfiguration::SYNC_DIR_NONE: return false; - default: - assert (false); } break; @@ -328,9 +398,8 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf inline -bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncConfiguration& config) -{ //false if nothing was to be done - assert (statusUpdater); +bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncConfiguration& config, const FolderPair& folderPair) +{ //return false if nothing had to be done if (!cmpLine.selectedForSynchronization) return false; @@ -351,7 +420,7 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo removeDirectory(cmpLine.fileDescrLeft.fullName, m_useRecycleBin); break; case SyncConfiguration::SYNC_DIR_RIGHT: //create folders on right - target = cmpLine.fileDescrRight.directory + cmpLine.fileDescrLeft.relativeName.c_str(); + target = folderPair.rightDirectory + cmpLine.fileDescrLeft.relativeName.c_str(); statusText = txtCreatingFolder; statusText.Replace(wxT("%x"), target, false); @@ -364,8 +433,6 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo break; case SyncConfiguration::SYNC_DIR_NONE: return false; - default: - assert (false); } break; @@ -373,7 +440,7 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo switch (config.exRightSideOnly) { case SyncConfiguration::SYNC_DIR_LEFT: //create folders on left - target = cmpLine.fileDescrLeft.directory + cmpLine.fileDescrRight.relativeName.c_str(); + target = folderPair.leftDirectory + cmpLine.fileDescrRight.relativeName.c_str(); statusText = txtCreatingFolder; statusText.Replace(wxT("%x"), target, false); @@ -393,13 +460,12 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo break; case SyncConfiguration::SYNC_DIR_NONE: return false; - default: - assert (false); } break; case FILE_EQUAL: return false; + case FILE_RIGHT_NEWER: case FILE_LEFT_NEWER: case FILE_DIFFERENT: @@ -424,33 +490,34 @@ bool deletionImminent(const FileCompareLine& line, const SyncConfiguration& conf class RemoveAtExit //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown! { public: - RemoveAtExit(FileCompareResult& grid) : - gridToWrite(grid) {} + RemoveAtExit(FileComparison& fileCmp) : + gridToWrite(fileCmp) {} ~RemoveAtExit() { - removeRowsFromVector(gridToWrite, rowsProcessed); + removeRowsFromVector(gridToWrite, rowsToDelete); } - void removeRow(int nr) + void markRow(int nr) { - rowsProcessed.insert(nr); + rowsToDelete.insert(nr); } private: - FileCompareResult& gridToWrite; - std::set<int> rowsProcessed; + FileComparison& gridToWrite; + std::set<int> rowsToDelete; }; -//synchronizes while processing rows in grid and returns all rows that have not been synced -void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const SyncConfiguration& config) throw(AbortThisProcess) +//synchronize and returns all rows that have not been synced +void SyncProcess::startSynchronizationProcess(FolderComparison& folderCmp, const SyncConfiguration& config) { #ifndef __WXDEBUG__ wxLogNull noWxLogs; //prevent wxWidgets logging #endif - //some basic checks: + //-------------------some basic checks:------------------------------------------ + //test existence of Recycle Bin if (m_useRecycleBin && !FreeFileSync::recycleBinExists()) { @@ -458,129 +525,187 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn return; //should be obsolete! } - //check if more than 50% of files/dirs are to be overwritten/deleted - if (m_warningSignificantDifference) //test if check should be executed + for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) { - if (significantDifferenceDetected(grid, config)) + const FileComparison& fileCmp = j->fileCmp; + + //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted + if (m_warningSignificantDifference) //test if check should be executed { - bool dontShowAgain = false; - statusUpdater->reportWarning(_("Significant difference detected: More than 50% of files will be overwritten/deleted!"), - dontShowAgain); - m_warningSignificantDifference = !dontShowAgain; + if (significantDifferenceDetected(fileCmp, config)) + { + bool dontShowAgain = false; + statusUpdater->reportWarning(Zstring(_("Significant difference detected:")) + wxT("\n") + + j->syncPair.leftDirectory + wxT(" <-> ") + j->syncPair.rightDirectory + wxT("\n\n") + + _("More than 50% of the total number of files will be copied or deleted!"), + dontShowAgain); + m_warningSignificantDifference = !dontShowAgain; + } } - } + //check for sufficient free diskspace in left directory + if (m_warningNotEnoughDiskSpace) + { + const std::pair<wxLongLong, wxLongLong> spaceNeeded = freeDiskSpaceNeeded(fileCmp, config, m_useRecycleBin); + + wxLongLong freeDiskSpaceLeft; + if (wxGetDiskSpace(j->syncPair.leftDirectory.c_str(), NULL, &freeDiskSpaceLeft)) + { + if (freeDiskSpaceLeft < spaceNeeded.first) + { + bool dontShowAgain = false; + statusUpdater->reportWarning(Zstring(_("Not enough free disk space available in:")) + wxT("\n") + + j->syncPair.leftDirectory + wxT("\n\n") + + _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.first).c_str(), + dontShowAgain); + m_warningNotEnoughDiskSpace = !dontShowAgain; + } + } + + //check for sufficient free diskspace in right directory + if (m_warningNotEnoughDiskSpace) + { + wxLongLong freeDiskSpaceRight; + if (wxGetDiskSpace(j->syncPair.rightDirectory.c_str(), NULL, &freeDiskSpaceRight)) + { + if (freeDiskSpaceRight < spaceNeeded.second) + { + bool dontShowAgain = false; + statusUpdater->reportWarning(Zstring(_("Not enough free disk space available in:")) + wxT("\n") + + j->syncPair.rightDirectory + wxT("\n\n") + + _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.second).c_str(), + dontShowAgain); + m_warningNotEnoughDiskSpace = !dontShowAgain; + } + } + } + } + } + //-------------------end of basic checks------------------------------------------ - RemoveAtExit markForRemoval(grid); //ensure that grid is always written to, even if method is exitted via exceptions //inform about the total amount of data that will be processed from now on - int objectsToCreate = 0; - int objectsToOverwrite = 0; - int objectsToDelete = 0; - double dataToProcess = 0; - calcTotalBytesToSync(grid, - config, - objectsToCreate, - objectsToOverwrite, - objectsToDelete, - dataToProcess); - - statusUpdater->initNewProcess(objectsToCreate + objectsToOverwrite + objectsToDelete, dataToProcess, StatusHandler::PROCESS_SYNCHRONIZING); + int objectsToCreate = 0; + int objectsToOverwrite = 0; + int objectsToDelete = 0; + wxULongLong dataToProcess; + + FreeFileSync::calcTotalBytesToSync(folderCmp, + config, + objectsToCreate, + objectsToOverwrite, + objectsToDelete, + dataToProcess); + + statusUpdater->initNewProcess(objectsToCreate + objectsToOverwrite + objectsToDelete, + globalFunctions::convertToSigned(dataToProcess), + StatusHandler::PROCESS_SYNCHRONIZING); try { - // it should never happen, that a directory on left side has same name as file on right side. startCompareProcess() should take care of this - // and split into two "exists on one side only" cases - // Note: this case is not handled by this tool as this is considered to be a bug and must be solved by the user - - //synchronize folders first; advantage: in case of deletions the whole folder is moved to recycle bin instead of single files - for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i) + //loop over folder pairs + for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) { - if ( i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY || - i->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - { - while (true) - { //trigger display refresh - statusUpdater->requestUiRefresh(); + FileComparison& fileCmp = j->fileCmp; - try - { - if (synchronizeFolder(*i, config)) - //progress indicator update - //indicator is updated only if directory is sync'ed correctly (and if some work was done)! - statusUpdater->updateProcessedData(1, 0); //each call represents one processed file/directory + //ensure that folderCmp is always written to, even if method is left via exceptions + RemoveAtExit removeRowsAtExit(fileCmp); //note: running at individual folder pair level! - markForRemoval.removeRow(i - grid.begin()); - break; - } - catch (FileError& error) - { - ErrorHandler::Response rv = statusUpdater->reportError(error.show()); + const FolderPair& currentPair = j->syncPair; + + // it should never happen, that a directory on left side has same name as file on right side. startCompareProcess() should take care of this + // and split into two "exists on one side only" cases + // Note: this case is not handled by this tool as this is considered to be a bug and must be solved by the user - if ( rv == ErrorHandler::IGNORE_ERROR) + //synchronize folders first; advantage: in case of deletions the whole folder is moved to recycle bin instead of single files + for (FileComparison::const_iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) + { + if ( i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY || + i->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + { + while (true) + { //trigger display refresh + statusUpdater->requestUiRefresh(); + + try + { + if (synchronizeFolder(*i, config, currentPair)) + //progress indicator update + //indicator is updated only if directory is sync'ed correctly (and if some work was done)! + statusUpdater->updateProcessedData(1, 0); //each call represents one processed file/directory + + removeRowsAtExit.markRow(i - fileCmp.begin()); break; - else if (rv == ErrorHandler::RETRY) - ; //continue with loop - else - assert (false); + } + catch (FileError& error) + { + ErrorHandler::Response rv = statusUpdater->reportError(error.show()); + + if ( rv == ErrorHandler::IGNORE_ERROR) + break; + else if (rv == ErrorHandler::RETRY) + ; //continue with loop + else + assert (false); + } } } } - } - //PERF_START; + //PERF_START; - //synchronize files: - bool deleteLoop = true; - for (int k = 0; k < 2; ++k) //loop over all files twice; reason: first delete, then copy (no sorting necessary: performance) - { - deleteLoop = !k; //-> first loop: delete files, second loop: copy files - - for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i) + //synchronize files: + for (int k = 0; k < 2; ++k) //loop over all files twice; reason: first delete, then copy (no sorting necessary: performance) { - if ( i->fileDescrLeft.objType == FileDescrLine::TYPE_FILE || - i->fileDescrRight.objType == FileDescrLine::TYPE_FILE) + bool deleteLoop = true; + deleteLoop = k == 0; //-> first loop: delete files, second loop: copy files + + for (FileComparison::const_iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) { - if ( (deleteLoop && deletionImminent(*i, config)) || - (!deleteLoop && !deletionImminent(*i, config))) + if ( i->fileDescrLeft.objType == FileDescrLine::TYPE_FILE || + i->fileDescrRight.objType == FileDescrLine::TYPE_FILE) { - while (true) - { //trigger display refresh - statusUpdater->requestUiRefresh(); - - try - { - if (synchronizeFile(*i, config)) + if ( (deleteLoop && deletionImminent(*i, config)) || + (!deleteLoop && !deletionImminent(*i, config))) + { + while (true) + { //trigger display refresh + statusUpdater->requestUiRefresh(); + + try { - //progress indicator update - //indicator is updated only if file is sync'ed correctly (and if some sync was done)! - int objectsToCreate = 0; - int objectsToOverwrite = 0; - int objectsToDelete = 0; - double dataToProcess = 0; - - if (getBytesToTransfer(objectsToCreate, - objectsToOverwrite, - objectsToDelete, - dataToProcess, - *i, - config)) //update status if some work was done (answer is always "yes" in this context) - statusUpdater->updateProcessedData(objectsToCreate + objectsToOverwrite + objectsToDelete, 0); //processed data is communicated in subfunctions! - } - - markForRemoval.removeRow(i - grid.begin()); - break; - } - catch (FileError& error) - { - ErrorHandler::Response rv = statusUpdater->reportError(error.show()); - - if ( rv == ErrorHandler::IGNORE_ERROR) + if (synchronizeFile(*i, config, currentPair)) + { + //progress indicator update + //indicator is updated only if file is sync'ed correctly (and if some sync was done)! + int objectsToCreate = 0; + int objectsToOverwrite = 0; + int objectsToDelete = 0; + wxULongLong dataToProcess; + + if (getBytesToTransfer(*i, + config, + objectsToCreate, + objectsToOverwrite, + objectsToDelete, + dataToProcess)) //update status if some work was done (answer is always "yes" in this context) + statusUpdater->updateProcessedData(objectsToCreate + objectsToOverwrite + objectsToDelete, 0); //processed data is communicated in subfunctions! + } + + removeRowsAtExit.markRow(i - fileCmp.begin()); break; - else if (rv == ErrorHandler::RETRY) - ; //continue with loop - else - assert (false); + } + catch (FileError& error) + { + ErrorHandler::Response rv = statusUpdater->reportError(error.show()); + + if ( rv == ErrorHandler::IGNORE_ERROR) + break; + else if (rv == ErrorHandler::RETRY) + ; //continue with loop + else + assert (false); + } } } } @@ -602,7 +727,7 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn struct CallBackData { StatusHandler* handler; - wxULongLong bytesTransferredLast; + wxLongLong bytesTransferredLast; }; #ifdef FFS_WIN @@ -625,20 +750,26 @@ DWORD CALLBACK copyProcessCallback( { CallBackData* sharedData = static_cast<CallBackData*>(lpData); + assert(totalBytesTransferred.HighPart >= 0); + + if (totalBytesTransferred.HighPart < 0) //let's see if someone answers the call... + wxMessageBox(wxT("You've just discovered a bug in WIN32 API function \"CopyFileEx\"! \n\n\ + Please write a mail to the author of FreeFileSync at zhnmju123@gmx.de and simply state that\n\ + \"totalBytesTransferred.HighPart can be below zero\"!\n\n\ + This will then be handled in future versions of FreeFileSync.\n\nThanks -ZenJu"), wxT("Warning")); + //inform about the (differential) processed amount of data - const wxULongLong currentBytesTransferred = wxULongLong(totalBytesTransferred.HighPart, totalBytesTransferred.LowPart); - sharedData->handler->updateProcessedData(0, (currentBytesTransferred - sharedData->bytesTransferredLast).ToDouble()); + const wxLongLong currentBytesTransferred(totalBytesTransferred.HighPart, totalBytesTransferred.LowPart); + sharedData->handler->updateProcessedData(0, currentBytesTransferred - sharedData->bytesTransferredLast); sharedData->bytesTransferredLast = currentBytesTransferred; sharedData->handler->requestUiRefresh(false); //don't allow throwing exception within this call if (sharedData->handler->abortIsRequested()) return PROGRESS_CANCEL; - else - return PROGRESS_CONTINUE; } - else - return PROGRESS_CONTINUE; + + return PROGRESS_CONTINUE; } #elif defined FFS_LINUX @@ -647,9 +778,12 @@ void copyProcessCallback(const wxULongLong& totalBytesTransferred, void* data) CallBackData* sharedData = static_cast<CallBackData*>(data); + //convert to unsigned + const wxLongLong totalBytes = globalFunctions::convertToSigned(totalBytesTransferred); + //inform about the (differential) processed amount of data - sharedData->handler->updateProcessedData(0, (totalBytesTransferred - sharedData->bytesTransferredLast).ToDouble()); - sharedData->bytesTransferredLast = totalBytesTransferred; + sharedData->handler->updateProcessedData(0, totalBytes - sharedData->bytesTransferredLast); + sharedData->bytesTransferredLast = totalBytes; sharedData->handler->requestUiRefresh(); //exceptions may be thrown here! } @@ -689,7 +823,7 @@ void SyncProcess::copyFileUpdating(const Zstring& source, const Zstring& target, copyFlags)) { //error situation: undo communication of processed amount of data - statusUpdater->updateProcessedData(0, sharedData.bytesTransferredLast.ToDouble() * -1 ); + statusUpdater->updateProcessedData(0, sharedData.bytesTransferredLast * -1 ); statusUpdater->requestUiRefresh(); //abort if requested, don't show error message if cancelled by user! @@ -700,7 +834,7 @@ void SyncProcess::copyFileUpdating(const Zstring& source, const Zstring& target, } //inform about the (remaining) processed amount of data - statusUpdater->updateProcessedData(0, (totalBytesToCpy - sharedData.bytesTransferredLast).ToDouble()); + statusUpdater->updateProcessedData(0, globalFunctions::convertToSigned(totalBytesToCpy) - sharedData.bytesTransferredLast); #elif defined FFS_LINUX //update via call-back function @@ -720,13 +854,13 @@ void SyncProcess::copyFileUpdating(const Zstring& source, const Zstring& target, catch (...) { //error situation: undo communication of processed amount of data - statusUpdater->updateProcessedData(0, sharedData.bytesTransferredLast.ToDouble() * -1 ); + statusUpdater->updateProcessedData(0, sharedData.bytesTransferredLast * -1 ); throw; } //inform about the (remaining) processed amount of data - statusUpdater->updateProcessedData(0, (totalBytesToCpy - sharedData.bytesTransferredLast).ToDouble()); + statusUpdater->updateProcessedData(0, globalFunctions::convertToSigned(totalBytesToCpy) - sharedData.bytesTransferredLast); #endif } diff --git a/synchronization.h b/synchronization.h index 82241f05..c8ce10cd 100644 --- a/synchronization.h +++ b/synchronization.h @@ -1,20 +1,21 @@ #ifndef SYNCHRONIZATION_H_INCLUDED #define SYNCHRONIZATION_H_INCLUDED -#include "FreeFileSync.h" -#include "library/statusHandler.h" +#include "structures.h" + +class StatusHandler; namespace FreeFileSync { - void calcTotalBytesToSync(const FileCompareResult& fileCmpResult, + void calcTotalBytesToSync(const FolderComparison& folderCmp, const SyncConfiguration& config, int& objectsToCreate, int& objectsToOverwrite, int& objectsToDelete, - double& dataToProcess); + wxULongLong& dataToProcess); - bool synchronizationNeeded(const FileCompareResult& fileCmpResult, const SyncConfiguration& config); + bool synchronizationNeeded(const FolderComparison& folderCmp, const SyncConfiguration& config); //class handling synchronization process class SyncProcess @@ -24,13 +25,14 @@ namespace FreeFileSync const bool copyFileSymLinks, const bool traverseDirSymLinks, bool& warningSignificantDifference, + bool& warningNotEnoughDiskSpace, StatusHandler* handler); - void startSynchronizationProcess(FileCompareResult& grid, const SyncConfiguration& config) throw(AbortThisProcess); + void startSynchronizationProcess(FolderComparison& folderCmp, const SyncConfiguration& config); private: - bool synchronizeFile(const FileCompareLine& cmpLine, const SyncConfiguration& config); //false if nothing had to be done - bool synchronizeFolder(const FileCompareLine& cmpLine, const SyncConfiguration& config); //false if nothing had to be done + bool synchronizeFile(const FileCompareLine& cmpLine, const SyncConfiguration& config, const FolderPair& folderPair); //false if nothing was done + bool synchronizeFolder(const FileCompareLine& cmpLine, const SyncConfiguration& config, const FolderPair& folderPair); //false if nothing was done void copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& sourceFileSize); @@ -38,14 +40,15 @@ namespace FreeFileSync const bool m_copyFileSymLinks; const bool m_traverseDirSymLinks; bool& m_warningSignificantDifference; + bool& m_warningNotEnoughDiskSpace; StatusHandler* statusUpdater; //preload status texts - Zstring txtCopyingFile; - Zstring txtOverwritingFile; - Zstring txtCreatingFolder; - Zstring txtDeletingFile; - Zstring txtDeletingFolder; + const Zstring txtCopyingFile; + const Zstring txtOverwritingFile; + const Zstring txtCreatingFolder; + const Zstring txtDeletingFile; + const Zstring txtDeletingFolder; }; } diff --git a/ui/MainDialog.cpp b/ui/MainDialog.cpp index d25fe224..e8006600 100644 --- a/ui/MainDialog.cpp +++ b/ui/MainDialog.cpp @@ -1,29 +1,136 @@ -/*************************************************************** - * Purpose: Code for main dialog - * Author: ZenJu (zhnmju123@gmx.de) - * Created: 2008-07-16 - **************************************************************/ - #include "mainDialog.h" #include <wx/filename.h> #include "../library/globalFunctions.h" -#include <wx/wfstream.h> #include <wx/clipbrd.h> +#include <wx/dataobj.h> #include <wx/ffile.h> #include "../library/customGrid.h" #include "../library/customButton.h" #include <algorithm> -#include "../library/sorting.h" #include <wx/msgdlg.h> #include "../comparison.h" #include "../synchronization.h" #include "../algorithm.h" #include "checkVersion.h" +#include "guiStatusHandler.h" +#include "syncDialog.h" +#include "../library/localization.h" +#include "smallDialogs.h" +#include "dragAndDrop.h" +#include "../library/filter.h" +#include "../structures.h" + +using namespace FreeFileSync; + + +class FolderPairPanel : public FolderPairGenerated +{ +public: + FolderPairPanel(wxWindow* parent) : + FolderPairGenerated(parent), + dragDropOnLeft(new DragDropOnDlg(m_panelLeft, m_dirPickerLeft, m_directoryLeft)), + dragDropOnRight(new DragDropOnDlg(m_panelRight, m_dirPickerRight, m_directoryRight)) {} + +private: + //support for drag and drop + std::auto_ptr<DragDropOnDlg> dragDropOnLeft; + std::auto_ptr<DragDropOnDlg> dragDropOnRight; +}; + + +class MainFolderDragDrop : public DragDropOnMainDlg +{ +public: + MainFolderDragDrop(MainDialog* mainDlg, + wxWindow* dropWindow1, + wxWindow* dropWindow2, + wxDirPickerCtrl* dirPicker, + wxComboBox* dirName) : + + DragDropOnMainDlg(dropWindow1, dropWindow2, dirPicker, dirName), + mainDlg_(mainDlg) {} + + virtual bool AcceptDrop(const wxString& dropName) + { + const xmlAccess::XmlType fileType = xmlAccess::getXmlType(dropName); + + //test if ffs config file has been dropped + if (fileType == xmlAccess::XML_GUI_CONFIG) + { + if (mainDlg_->readConfigurationFromXml(dropName)) + mainDlg_->pushStatusInformation(_("Configuration loaded!")); + return false; + } + //...or a ffs batch file + else if (fileType == xmlAccess::XML_BATCH_CONFIG) + { + BatchDialog* batchDlg = new BatchDialog(mainDlg_, dropName); + if (batchDlg->ShowModal() == BatchDialog::BATCH_FILE_SAVED) + mainDlg_->pushStatusInformation(_("Batch file created successfully!")); + return false; + } + + //disable the sync button + mainDlg_->enableSynchronization(false); + + //clear grids + mainDlg_->currentGridData.clear(); + mainDlg_->updateGuiGrid(); + + return true; + } + +private: + MainDialog* mainDlg_; +}; + + +//workaround for wxWidgets: small hack to update menu items: actually this is a wxWidgets bug (affects Windows- and Linux-build) +class MenuItemUpdater +{ +public: + MenuItemUpdater(wxMenu* menuToUpdate) : menuToUpdate_(menuToUpdate) {} + + ~MenuItemUpdater() + { + //start updating menu icons + const wxMenuItemList& allItems = menuToUpdate_->GetMenuItems(); + + //retrieve menu item positions: unfortunately wxMenu doesn't offer a better way + MenuItemMap::iterator j; + int index = 0; + for (wxMenuItemList::const_iterator i = allItems.begin(); i != allItems.end(); ++i, ++index) + if ((j = menuItems.find(*i)) != menuItems.end()) + j->second = index; + + //finally update items + for (MenuItemMap::const_iterator i = menuItems.begin(); i != menuItems.end(); ++i) + if (i->second >= 0) + { + menuToUpdate_->Remove(i->first); //actual workaround + menuToUpdate_->Insert(i->second, i->first); // + } + } + + void addForUpdate(wxMenuItem* newEntry, const wxBitmap& newBitmap) + { + newEntry->SetBitmap(newBitmap); + menuItems.insert(std::pair<wxMenuItem*, int>(newEntry, -1)); + } + +private: + typedef std::map<wxMenuItem*, int> MenuItemMap; + wxMenu* menuToUpdate_; + MenuItemMap menuItems; +}; + +//################################################################################################################################## MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale* language, xmlAccess::XmlGlobalSettings& settings) : MainDialogGenerated(frame), globalSettings(settings), + gridDataView(currentGridData), contextMenu(new wxMenu), //initialize right-click context menu; will be dynamically re-created on each R-mouse-click programLanguage(language), filteringInitialized(false), @@ -52,35 +159,48 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale bSizer6->Layout(); //wxButtonWithImage size might have changed - //menu icons - m_menuItem10->SetBitmap(*globalResource.bitmapCompareSmall); - m_menuItem11->SetBitmap(*globalResource.bitmapSyncSmall); - m_menuItem7->SetBitmap(*globalResource.bitmapBatchSmall); - m_menuItemGlobSett->SetBitmap(*globalResource.bitmapSettingsSmall); - m_menuItemAbout->SetBitmap(*globalResource.bitmapAboutSmall); - - //workaround for wxWidgets: small hack to update menu items: actually this is a wxWidgets bug (affects Windows- and Linux-build) - m_menu1->Remove(m_menuItem10); - m_menu1->Remove(m_menuItem11); - m_menu1->Insert(0, m_menuItem10); - m_menu1->Insert(1, m_menuItem11); - m_menu3->Remove(m_menuItemGlobSett); - m_menu3->Remove(m_menuItem7); - m_menu3->Insert(2, m_menuItemGlobSett); - m_menu3->Insert(3, m_menuItem7); - - m_menu33->Remove(m_menuItemAbout); - m_menu33->Insert(2, m_menuItemAbout); + //menu icons: workaround for wxWidgets: small hack to update menu items: actually this is a wxWidgets bug (affects Windows- and Linux-build) + MenuItemUpdater updateMenuFile(m_menuFile); + updateMenuFile.addForUpdate(m_menuItem10, *globalResource.bitmapCompareSmall); + updateMenuFile.addForUpdate(m_menuItem11, *globalResource.bitmapSyncSmall); + + MenuItemUpdater updateMenuAdv(m_menuAdvanced); + updateMenuAdv.addForUpdate(m_menuItemGlobSett, *globalResource.bitmapSettingsSmall); + updateMenuAdv.addForUpdate(m_menuItem7, *globalResource.bitmapBatchSmall); + + MenuItemUpdater updateMenuHelp(m_menuHelp); + updateMenuHelp.addForUpdate(m_menuItemAbout, *globalResource.bitmapAboutSmall); + + MenuItemUpdater updateMenuAdvLang(m_menuLanguages); + updateMenuAdvLang.addForUpdate(m_menuItemGerman, *globalResource.bitmapGermany); + updateMenuAdvLang.addForUpdate(m_menuItemEnglish, *globalResource.bitmapEngland); + updateMenuAdvLang.addForUpdate(m_menuItemSpanish, *globalResource.bitmapSpain); + updateMenuAdvLang.addForUpdate(m_menuItemFrench, *globalResource.bitmapFrance); + updateMenuAdvLang.addForUpdate(m_menuItemItalian, *globalResource.bitmapItaly); + updateMenuAdvLang.addForUpdate(m_menuItemHungarian, *globalResource.bitmapHungary); + updateMenuAdvLang.addForUpdate(m_menuItemDutch, *globalResource.bitmapHolland); + updateMenuAdvLang.addForUpdate(m_menuItemPolish, *globalResource.bitmapPoland); + updateMenuAdvLang.addForUpdate(m_menuItemPortuguese, *globalResource.bitmapPortugal); + updateMenuAdvLang.addForUpdate(m_menuItemPortugueseBrazil, *globalResource.bitmapBrazil); + updateMenuAdvLang.addForUpdate(m_menuItemSlovenian, *globalResource.bitmapSlovakia); + updateMenuAdvLang.addForUpdate(m_menuItemJapanese, *globalResource.bitmapJapan); + updateMenuAdvLang.addForUpdate(m_menuItemChineseSimple, *globalResource.bitmapChina); - //prepare drag & drop - m_panel1->SetDropTarget(new MainWindowDropTarget(this, m_panel1)); - m_panel2->SetDropTarget(new MainWindowDropTarget(this, m_panel2)); - m_panel11->SetDropTarget(new MainWindowDropTarget(this, m_panel1)); - m_panel12->SetDropTarget(new MainWindowDropTarget(this, m_panel2)); - - //redirect drag & drop event back to main window - Connect(FFS_DROP_FILE_EVENT, FfsFileDropEventHandler(MainDialog::OnFilesDropped), NULL, this); + //prepare drag & drop + dragDropOnLeft.reset(new MainFolderDragDrop( + this, + m_panelLeft, + m_panelTopLeft, + m_dirPickerLeft, + m_directoryLeft)); + + dragDropOnRight.reset(new MainFolderDragDrop( + this, + m_panelRight, + m_panelTopRight, + m_dirPickerRight, + m_directoryRight)); //support for CTRL + C and DEL m_gridLeft->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridLeftButtonEvent), NULL, this); @@ -95,58 +215,31 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale Connect(wxEVT_MOVE, wxEventHandler(MainDialog::onResizeMainWindow), NULL, this); //init grid settings - m_gridLeft->initSettings( true, globalSettings.gui.showFileIcons, m_gridLeft, m_gridRight, m_gridMiddle, &gridRefUI, ¤tGridData); - m_gridMiddle->initSettings(false, false, m_gridLeft, m_gridRight, m_gridMiddle, &gridRefUI, ¤tGridData); - m_gridRight->initSettings( true, globalSettings.gui.showFileIcons, m_gridLeft, m_gridRight, m_gridMiddle, &gridRefUI, ¤tGridData); + m_gridLeft->initSettings( globalSettings.gui.showFileIcons, + m_gridLeft, + m_gridRight, + m_gridMiddle, + &gridDataView); + + m_gridMiddle->initSettings(m_gridLeft, + m_gridRight, + m_gridMiddle, + &gridDataView); + + m_gridRight->initSettings( globalSettings.gui.showFileIcons, + m_gridLeft, + m_gridRight, + m_gridMiddle, + &gridDataView); //disable sync button as long as "compare" hasn't been triggered. enableSynchronization(false); //mainly to update row label sizes... - writeGrid(currentGridData); + updateGuiGrid(); enableSynchronization(false); - //initialize language selection - switch (programLanguage->getLanguage()) - { - case wxLANGUAGE_CHINESE_SIMPLIFIED: - m_menuItemChineseSimple->Check(); - break; - case wxLANGUAGE_DUTCH: - m_menuItemDutch->Check(); - break; - case wxLANGUAGE_FRENCH: - m_menuItemFrench->Check(); - break; - case wxLANGUAGE_GERMAN: - m_menuItemGerman->Check(); - break; - case wxLANGUAGE_HUNGARIAN: - m_menuItemHungarian->Check(); - break; - case wxLANGUAGE_ITALIAN: - m_menuItemItalian->Check(); - break; - case wxLANGUAGE_JAPANESE: - m_menuItemJapanese->Check(); - break; - case wxLANGUAGE_POLISH: - m_menuItemPolish->Check(); - break; - case wxLANGUAGE_PORTUGUESE: - m_menuItemPortuguese->Check(); - break; - case wxLANGUAGE_SLOVENIAN: - m_menuItemSlovenian->Check(); - break; - case wxLANGUAGE_SPANISH: - m_menuItemSpanish->Check(); - break; - default: - m_menuItemEnglish->Check(); - } - //create the compare status panel in hidden state compareStatus = new CompareStatus(this); bSizer1->Insert(1, compareStatus, 0, wxEXPAND | wxBOTTOM, 5 ); @@ -154,11 +247,14 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale compareStatus->Hide(); //correct width of swap button above middle grid - wxSize source = m_gridMiddle->GetSize(); - wxSize target = bSizerMiddle->GetSize(); - int spaceToAdd = source.GetX() - target.GetX(); + const wxSize source = m_gridMiddle->GetSize(); + const wxSize target = bSizerMiddle->GetSize(); + const int spaceToAdd = source.GetX() - target.GetX(); bSizerMiddle->Insert(1, spaceToAdd / 2, 0, 0); bSizerMiddle->Insert(0, spaceToAdd - (spaceToAdd / 2), 0, 0); + + //register regular check for update on next idle event + Connect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), NULL, this); } @@ -177,7 +273,7 @@ void MainDialog::cleanUp() //no need for event Disconnect() here; done automatically //save configuration - writeConfigurationToXml(FreeFileSync::LAST_CONFIG_FILE); //don't trow exceptions in destructors + writeConfigurationToXml(xmlAccess::LAST_CONFIG_FILE); //don't trow exceptions in destructors writeGlobalSettings(); } } @@ -240,12 +336,12 @@ void MainDialog::writeGlobalSettings() //write list of last used folders globalSettings.gui.folderHistoryLeft.clear(); - const wxArrayString leftFolderHistory = m_comboBoxDirLeft->GetStrings(); + const wxArrayString leftFolderHistory = m_directoryLeft->GetStrings(); for (unsigned i = 0; i < leftFolderHistory.GetCount(); ++i) globalSettings.gui.folderHistoryLeft.push_back(leftFolderHistory[i]); globalSettings.gui.folderHistoryRight.clear(); - const wxArrayString rightFolderHistory = m_comboBoxDirRight->GetStrings(); + const wxArrayString rightFolderHistory = m_directoryRight->GetStrings(); for (unsigned i = 0; i < rightFolderHistory.GetCount(); ++i) globalSettings.gui.folderHistoryRight.push_back(rightFolderHistory[i]); } @@ -272,59 +368,65 @@ void MainDialog::filterRangeManually(const std::set<int>& rowsToFilterOnUiTable) { if (rowsToFilterOnUiTable.size() > 0) { - int gridSizeUI = gridRefUI.size(); - bool newSelection = false; //default: deselect range //leadingRow determines de-/selection of all other rows - int leadingRow = *rowsToFilterOnUiTable.begin(); - if (0 <= leadingRow && leadingRow < gridSizeUI) - newSelection = !currentGridData[gridRefUI[leadingRow]].selectedForSynchronization; - - if (hideFilteredElements) - assert(!newSelection); //if hidefiltered is active, there should be no filtered elements on screen => current element was filtered out + const int leadingRow = *rowsToFilterOnUiTable.begin(); + if (0 <= leadingRow && leadingRow < int(gridDataView.elementsOnView())) + newSelection = !gridDataView[leadingRow].selectedForSynchronization; + //if hidefiltered is active, there should be no filtered elements on screen => current element was filtered out + assert(!hideFilteredElements || !newSelection); //get all lines that need to be filtered (e.g. if a folder is marked, then its subelements should be marked as well) - std::set<int> rowsToFilterOnGridData; //rows to filter in backend + FolderCompRef compRef; + gridDataView.viewRefToFolderRef(rowsToFilterOnUiTable, compRef); + + assert(compRef.size() == currentGridData.size()); //GridView::viewRefToFolderRef() should ensure this! - for (std::set<int>::iterator i = rowsToFilterOnUiTable.begin(); i != rowsToFilterOnUiTable.end(); ++i) + for (FolderComparison::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) { - if (0 <= *i && *i < gridSizeUI) + FileComparison& fileCmp = i->fileCmp; + const std::set<int>& markedRows = compRef[i - currentGridData.begin()]; + + std::set<int> markedRowsTotal; //retrieve additional rows that need to be filtered, too + for (std::set<int>::iterator i = markedRows.begin(); i != markedRows.end(); ++i) { - unsigned int gridIndex = gridRefUI[*i]; - rowsToFilterOnGridData.insert(gridIndex); - FreeFileSync::addSubElements(currentGridData, currentGridData[gridIndex], rowsToFilterOnGridData); + markedRowsTotal.insert(*i); + FreeFileSync::addSubElements(fileCmp, fileCmp[*i], markedRowsTotal); } - } - //toggle selection of filtered rows - for (std::set<int>::iterator i = rowsToFilterOnGridData.begin(); i != rowsToFilterOnGridData.end(); ++i) - currentGridData[*i].selectedForSynchronization = newSelection; + //toggle selection of filtered rows + for (std::set<int>::iterator i = markedRowsTotal.begin(); i != markedRowsTotal.end(); ++i) + fileCmp[*i].selectedForSynchronization = newSelection; + } //signal UI that grids need to be refreshed on next Update() m_gridLeft->ForceRefresh(); - m_gridRight->ForceRefresh(); m_gridMiddle->ForceRefresh(); + m_gridRight->ForceRefresh(); Update(); //show changes resulting from ForceRefresh() if (hideFilteredElements) { wxMilliSleep(400); //some delay to show user the rows he has filtered out before they are removed - writeGrid(currentGridData); //redraw grid to remove excluded elements (keeping sort sequence) + updateGuiGrid(); //redraw grid to remove excluded elements (keeping sort sequence) + + m_gridLeft->ClearSelection(); + m_gridRight->ClearSelection(); + m_gridMiddle->ClearSelection(); } - } + else + { //this second call to ForceRefresh() looks strange, but it actually fixes occasional graphical artifacts on bottom right of the grid + m_gridLeft->ForceRefresh(); + m_gridMiddle->ForceRefresh(); + m_gridRight->ForceRefresh(); - //clear selection on grids - if (hideFilteredElements) - { - m_gridLeft->ClearSelection(); - m_gridRight->ClearSelection(); - } //exception for grid 3 - m_gridMiddle->ClearSelection(); + m_gridMiddle->ClearSelection(); + } + } } - /*grid event choreography: 1. UI-Mouse-Down => OnGridSelectCell 2. UI-Mouse-Up => SelectRangeEvent (if at least two rows are marked) @@ -391,7 +493,7 @@ void MainDialog::OnIdleEvent(wxEvent& event) } -void MainDialog::copySelectionToClipboard(const wxGrid* selectedGrid) +void MainDialog::copySelectionToClipboard(const CustomGrid* selectedGrid) { const std::set<int> selectedRows = getSelectedRows(selectedGrid); if (selectedRows.size() > 0) @@ -400,10 +502,10 @@ void MainDialog::copySelectionToClipboard(const wxGrid* selectedGrid) for (std::set<int>::iterator i = selectedRows.begin(); i != selectedRows.end(); ++i) { - for (int k = 0; k < const_cast<wxGrid*>(selectedGrid)->GetNumberCols(); ++k) + for (int k = 0; k < const_cast<CustomGrid*>(selectedGrid)->GetNumberCols(); ++k) { - clipboardString+= const_cast<wxGrid*>(selectedGrid)->GetCellValue(*i, k); - if (k != const_cast<wxGrid*>(selectedGrid)->GetNumberCols() - 1) + clipboardString+= const_cast<CustomGrid*>(selectedGrid)->GetCellValue(*i, k); + if (k != const_cast<CustomGrid*>(selectedGrid)->GetNumberCols() - 1) clipboardString+= '\t'; } clipboardString+= '\n'; @@ -436,7 +538,7 @@ void removeInvalidRows(std::set<int>& rows, const int currentUiTableSize) } -std::set<int> MainDialog::getSelectedRows(const wxGrid* grid) +std::set<int> MainDialog::getSelectedRows(const CustomGrid* grid) { std::set<int> output; int rowTop, rowBottom; //coords of selection @@ -450,7 +552,7 @@ std::set<int> MainDialog::getSelectedRows(const wxGrid* grid) if (!grid->GetSelectedCols().IsEmpty()) //if a column is selected this is means all rows are marked for deletion { - for (int k = 0; k < const_cast<wxGrid*>(grid)->GetNumberRows(); ++k) //messy wxGrid implementation... + for (int k = 0; k < const_cast<CustomGrid*>(grid)->GetNumberRows(); ++k) //messy wxGrid implementation... output.insert(k); } @@ -482,10 +584,10 @@ std::set<int> MainDialog::getSelectedRows(const wxGrid* grid) } //some exception: also add current cursor row to selection if there are no others... hopefully improving usability - if (output.empty() && grid == m_gridLeft->getLeadGrid()) - output.insert(const_cast<wxGrid*>(grid)->GetCursorRow()); //messy wxGrid implementation... + if (output.empty() && grid->isLeadGrid()) + output.insert(const_cast<CustomGrid*>(grid)->GetCursorRow()); //messy wxGrid implementation... - removeInvalidRows(output, gridRefUI.size()); + removeInvalidRows(output, gridDataView.elementsOnView()); return output; } @@ -518,7 +620,7 @@ public: case ErrorDlg::BUTTON_RETRY: return ErrorHandler::RETRY; case ErrorDlg::BUTTON_ABORT: - throw AbortThisProcess(); + throw FreeFileSync::AbortThisProcess(); default: assert (false); return ErrorHandler::IGNORE_ERROR; //dummy return value @@ -530,23 +632,26 @@ private: }; -void MainDialog::deleteFilesOnGrid(const std::set<int>& selectedRowsLeft, const std::set<int>& selectedRowsRight) +void MainDialog::deleteSelectedFiles() { - if (selectedRowsLeft.size() + selectedRowsRight.size()) + //get set of selected rows on view + const std::set<int> viewSelectionLeft = getSelectedRows(m_gridLeft); + const std::set<int> viewSelectionRight = getSelectedRows(m_gridRight); + + if (viewSelectionLeft.size() + viewSelectionRight.size()) { - //map grid lines from UI to grid lines in backend (gridData) - std::set<int> rowsOnGridLeft; - for (std::set<int>::iterator i = selectedRowsLeft.begin(); i != selectedRowsLeft.end(); ++i) - rowsOnGridLeft.insert(gridRefUI[*i]); + //map lines from GUI view to grid line references for "currentGridData" + FolderCompRef compRefLeft; + gridDataView.viewRefToFolderRef(viewSelectionLeft, compRefLeft); + + FolderCompRef compRefRight; + gridDataView.viewRefToFolderRef(viewSelectionRight, compRefRight); - std::set<int> rowsOnGridRight; - for (std::set<int>::iterator i = selectedRowsRight.begin(); i != selectedRowsRight.end(); ++i) - rowsOnGridRight.insert(gridRefUI[*i]); DeleteDialog* confirmDeletion = new DeleteDialog(this, //no destruction needed; attached to main window currentGridData, - rowsOnGridLeft, - rowsOnGridRight, + compRefLeft, + compRefRight, globalSettings.gui.deleteOnBothSides, globalSettings.gui.useRecyclerForManualDeletion); if (confirmDeletion->ShowModal() == DeleteDialog::BUTTON_OKAY) @@ -557,25 +662,37 @@ void MainDialog::deleteFilesOnGrid(const std::set<int>& selectedRowsLeft, const return; } - //Attention! Modifying the grid is highly critical! There MUST NOT BE any accesses to gridRefUI until this reference table is updated - //by writeGrid()!! This is easily missed, e.g. when ClearSelection() or ShowModal() or possibly any other wxWidgets function is called - //that might want to redraw the UI (which implicitly uses the information in gridRefUI and currentGridData (see CustomGrid) + //Attention! Modifying the grid is highly critical! There MUST NOT BE any accesses to gridDataView until this reference table is updated + //by updateGuiGrid()!! This is easily missed, e.g. when ClearSelection() or ShowModal() or possibly any other wxWidgets function is called + //that might want to redraw the UI (which implicitly uses the information in gridDataView (see CustomGrid) try { //handle errors when deleting files/folders DeleteErrorHandler errorHandler; - FreeFileSync::deleteFromGridAndHD(currentGridData, - rowsOnGridLeft, - rowsOnGridRight, - globalSettings.gui.deleteOnBothSides, - globalSettings.gui.useRecyclerForManualDeletion, - &errorHandler); + assert(compRefLeft.size() == currentGridData.size()); //GridView::viewRefToFolderRef() should ensure this! + assert(compRefRight.size() == currentGridData.size()); + + for (FolderComparison::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) + { + const int folderPairNr = i - currentGridData.begin(); + + const std::set<int>& rowsToDeleteOnLeft = compRefLeft[folderPairNr]; + const std::set<int>& rowsToDeleteOnRight = compRefRight[folderPairNr]; + + FreeFileSync::deleteFromGridAndHD(i->fileCmp, + rowsToDeleteOnLeft, + rowsToDeleteOnRight, + globalSettings.gui.deleteOnBothSides, + globalSettings.gui.useRecyclerForManualDeletion, + &errorHandler); + } + } - catch (AbortThisProcess&) {} + catch (FreeFileSync::AbortThisProcess&) {} //redraw grid neccessary to update new dimensions and for UI-Backend data linkage - writeGrid(currentGridData); //call immediately after deleteFromGridAndHD!!! + updateGuiGrid(); //call immediately after deleteFromGridAndHD!!! m_gridLeft->ClearSelection(); m_gridMiddle->ClearSelection(); @@ -585,34 +702,34 @@ void MainDialog::deleteFilesOnGrid(const std::set<int>& selectedRowsLeft, const } -void MainDialog::openWithFileManager(int rowNumber, const wxGrid* grid) +void MainDialog::openWithFileManager(const int rowNumber, const bool leftSide) { wxString command; - const FileDescrLine* fileDescr = NULL; - if (grid == m_gridLeft) - { - if (0 <= rowNumber && rowNumber < int(gridRefUI.size())) - fileDescr = ¤tGridData[gridRefUI[rowNumber]].fileDescrLeft; -#ifdef FFS_WIN - command = wxString(wxT("explorer ")) + FreeFileSync::getFormattedDirectoryName(m_comboBoxDirLeft->GetValue().c_str()).c_str(); //default -#endif // FFS_WIN - } - else if (grid == m_gridRight) - { - if (0 <= rowNumber && rowNumber < int(gridRefUI.size())) - fileDescr = ¤tGridData[gridRefUI[rowNumber]].fileDescrRight; -#ifdef FFS_WIN - command = wxString(wxT("explorer ")) + FreeFileSync::getFormattedDirectoryName(m_comboBoxDirRight->GetValue().c_str()).c_str(); //default -#endif // FFS_WIN - } + + wxString defaultFolder; + if (0 <= rowNumber && rowNumber < int(gridDataView.elementsOnView())) + defaultFolder = leftSide ? + gridDataView.getFolderPair(rowNumber).leftDirectory.c_str() : + gridDataView.getFolderPair(rowNumber).rightDirectory.c_str(); else - { - assert(false); - return; - } + defaultFolder = leftSide ? + FreeFileSync::getFormattedDirectoryName(m_directoryLeft->GetValue().c_str()).c_str() : + FreeFileSync::getFormattedDirectoryName(m_directoryRight->GetValue().c_str()).c_str(); - if (fileDescr) +#ifdef FFS_WIN + command = wxString(wxT("explorer ")) + defaultFolder; //default +#elif defined FFS_LINUX + command = globalSettings.gui.commandLineFileManager; + command.Replace(wxT("%name"), defaultFolder); + command.Replace(wxT("%path"), defaultFolder); +#endif + + if (0 <= rowNumber && rowNumber < int(gridDataView.elementsOnView())) { + const FileDescrLine* fileDescr = leftSide ? + &gridDataView[rowNumber].fileDescrLeft : + &gridDataView[rowNumber].fileDescrRight; + if (fileDescr->objType == FileDescrLine::TYPE_FILE) { command = globalSettings.gui.commandLineFileManager; @@ -625,6 +742,9 @@ void MainDialog::openWithFileManager(int rowNumber, const wxGrid* grid) command.Replace(wxT("%name"), fileDescr->fullName.c_str()); command.Replace(wxT("%path"), fileDescr->fullName.c_str()); } + else if (fileDescr->objType == FileDescrLine::TYPE_NOTHING) + { + } } if (!command.empty()) @@ -689,7 +809,7 @@ void MainDialog::onGridLeftButtonEvent(wxKeyEvent& event) m_gridLeft->SelectAll(); } else if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) - deleteFilesOnGrid(getSelectedRows(m_gridLeft), getSelectedRows(m_gridRight)); + deleteSelectedFiles(); event.Skip(); } @@ -721,7 +841,7 @@ void MainDialog::onGridRightButtonEvent(wxKeyEvent& event) m_gridRight->SelectAll(); } else if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) - deleteFilesOnGrid(getSelectedRows(m_gridLeft), getSelectedRows(m_gridRight)); + deleteSelectedFiles(); event.Skip(); } @@ -747,9 +867,7 @@ void MainDialog::OnContextMenu(wxGridEvent& event) else selectionBegin = std::min(*selectionLeft.begin(), *selectionRight.begin()); - const FileCompareLine& firstLine = currentGridData[gridRefUI[selectionBegin]]; - - if (firstLine.selectedForSynchronization) + if (gridDataView[selectionBegin].selectedForSynchronization) //valid access, as getSelectedRows returns valid references only contextMenu->Append(CONTEXT_FILTER_TEMP, _("Exclude temporarily")); else contextMenu->Append(CONTEXT_FILTER_TEMP, _("Include temporarily")); @@ -766,7 +884,7 @@ void MainDialog::OnContextMenu(wxGridEvent& event) FilterObject newFilterEntry; for (std::set<int>::iterator i = selectionLeft.begin(); i != selectionLeft.end(); ++i) { - const FileCompareLine& line = currentGridData[gridRefUI[*i]]; + const FileCompareLine& line = gridDataView[*i]; newFilterEntry.relativeName = line.fileDescrLeft.relativeName.c_str(); newFilterEntry.type = line.fileDescrLeft.objType; if (!newFilterEntry.relativeName.IsEmpty()) @@ -774,7 +892,7 @@ void MainDialog::OnContextMenu(wxGridEvent& event) } for (std::set<int>::iterator i = selectionRight.begin(); i != selectionRight.end(); ++i) { - const FileCompareLine& line = currentGridData[gridRefUI[*i]]; + const FileCompareLine& line = gridDataView[*i]; newFilterEntry.relativeName = line.fileDescrRight.relativeName.c_str(); newFilterEntry.type = line.fileDescrRight.objType; if (!newFilterEntry.relativeName.IsEmpty()) @@ -871,7 +989,7 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) FreeFileSync::filterGridData(currentGridData, cfg.includeFilter, cfg.excludeFilter); - writeGrid(currentGridData); + updateGuiGrid(); if (hideFilteredElements) { m_gridLeft->ClearSelection(); @@ -891,14 +1009,10 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) cfg.excludeFilter+= wxT("\n"); if (i->type == FileDescrLine::TYPE_FILE) - { cfg.excludeFilter+= wxString(wxT("*")) + GlobalResources::FILE_NAME_SEPARATOR + i->relativeName; - } else if (i->type == FileDescrLine::TYPE_DIRECTORY) - { - cfg.excludeFilter+= wxString(wxT("*")) + GlobalResources::FILE_NAME_SEPARATOR + i->relativeName + wxT("\n"); cfg.excludeFilter+= wxString(wxT("*")) + GlobalResources::FILE_NAME_SEPARATOR + i->relativeName + GlobalResources::FILE_NAME_SEPARATOR + wxT("*"); - } + else assert(false); } @@ -907,7 +1021,7 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) FreeFileSync::filterGridData(currentGridData, cfg.includeFilter, cfg.excludeFilter); - writeGrid(currentGridData); + updateGuiGrid(); if (hideFilteredElements) { m_gridLeft->ClearSelection(); @@ -929,20 +1043,24 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) { if (m_gridLeft->isLeadGrid() || m_gridRight->isLeadGrid()) { - const wxGrid* const leadGrid = m_gridLeft->getLeadGrid(); + const CustomGrid* leadGrid = NULL; + if (m_gridLeft->isLeadGrid()) + leadGrid = m_gridLeft; + else + leadGrid = m_gridRight; std::set<int> selection = getSelectedRows(leadGrid); if (selection.size() == 1) - openWithFileManager(*selection.begin(), leadGrid); + openWithFileManager(*selection.begin(), m_gridLeft->isLeadGrid()); else if (selection.size() == 0) - openWithFileManager(-1, leadGrid); + openWithFileManager(-1, m_gridLeft->isLeadGrid()); } } else if (eventId == CONTEXT_DELETE_FILES) { - deleteFilesOnGrid(getSelectedRows(m_gridLeft), getSelectedRows(m_gridRight)); + deleteSelectedFiles(); } } @@ -953,7 +1071,7 @@ void MainDialog::OnContextMenuMiddle(wxGridEvent& event) contextMenu->Append(CONTEXT_CHECK_ALL, _("Check all")); contextMenu->Append(CONTEXT_UNCHECK_ALL, _("Uncheck all")); - if (currentGridData.size() == 0) + if (gridDataView.elementsTotal() == 0) { contextMenu->Enable(CONTEXT_CHECK_ALL, false); contextMenu->Enable(CONTEXT_UNCHECK_ALL, false); @@ -972,12 +1090,12 @@ void MainDialog::OnContextMenuMiddleSelection(wxCommandEvent& event) if (eventId == CONTEXT_CHECK_ALL) { FreeFileSync::includeAllRowsOnGrid(currentGridData); - writeGrid(currentGridData); + updateGuiGrid(); } else if (eventId == CONTEXT_UNCHECK_ALL) { FreeFileSync::excludeAllRowsOnGrid(currentGridData); - writeGrid(currentGridData); + updateGuiGrid(); } } @@ -1035,81 +1153,16 @@ void MainDialog::OnContextColumnSelection(wxCommandEvent& event) } -void MainDialog::OnWriteDirManually(wxCommandEvent& event) -{ - wxString newDir = FreeFileSync::getFormattedDirectoryName(event.GetString().c_str()).c_str(); - if (wxDirExists(newDir)) - { - wxObject* eventObj = event.GetEventObject(); - - //first check if event comes from main folder pair - if (eventObj == (wxObject*)m_comboBoxDirLeft) - m_dirPickerLeft->SetPath(newDir); - else if (eventObj == (wxObject*)m_comboBoxDirRight) - m_dirPickerRight->SetPath(newDir); - else - { - //check if event comes from additional pairs - for (std::vector<FolderPairGenerated*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - { - FolderPairGenerated* dirPair = *i; - if (eventObj == (wxObject*)(dirPair->m_directoryLeft)) - { - dirPair->m_dirPickerLeft->SetPath(newDir); - break; - } - else if (eventObj == (wxObject*)(dirPair->m_directoryRight)) - { - dirPair->m_dirPickerRight->SetPath(newDir); - break; - } - } - } - } - event.Skip(); -} - - void MainDialog::OnDirSelected(wxFileDirPickerEvent& event) { - const wxString newPath = event.GetPath(); - wxObject* eventObj = event.GetEventObject(); - - //first check if event comes from main folder pair - if (eventObj == (wxObject*)m_dirPickerLeft) - { - m_comboBoxDirLeft->SetSelection(wxNOT_FOUND); - m_comboBoxDirLeft->SetValue(newPath); - } - else if (eventObj == (wxObject*)m_dirPickerRight) - { - m_comboBoxDirRight->SetSelection(wxNOT_FOUND); - m_comboBoxDirRight->SetValue(newPath); - } - else //check if event comes from additional pairs - { - for (std::vector<FolderPairGenerated*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - { - FolderPairGenerated* dirPair = *i; - if (eventObj == (wxObject*)(dirPair->m_dirPickerLeft)) - { - dirPair->m_directoryLeft->SetValue(newPath); - break; - } - else if (eventObj == (wxObject*)(dirPair->m_dirPickerRight)) - { - dirPair->m_directoryRight->SetValue(newPath); - break; - } - } - } + //left and right directory text-control and dirpicker are synchronized by MainFolderDragDrop automatically //disable the sync button enableSynchronization(false); //clear grids currentGridData.clear(); - writeGrid(currentGridData); + updateGuiGrid(); event.Skip(); } @@ -1176,7 +1229,7 @@ void MainDialog::addFileToCfgHistory(const wxString& filename) cfgFileNames.insert(cfgFileNames.begin(), filename); //the default config file should receive another name on GUI - if (sameFileSpecified(FreeFileSync::LAST_CONFIG_FILE, filename)) + if (sameFileSpecified(xmlAccess::LAST_CONFIG_FILE, filename)) m_choiceHistory->Insert(_("<Last session>"), 0); //insert at beginning of list else m_choiceHistory->Insert(getFormattedHistoryElement(filename), 0); //insert at beginning of list @@ -1185,107 +1238,11 @@ void MainDialog::addFileToCfgHistory(const wxString& filename) } //keep maximal size of history list - if (cfgFileNames.size() > globalSettings.gui.cfgHistoryMaxItems) + if (cfgFileNames.size() > globalSettings.gui.cfgHistoryMax) { //delete last rows cfgFileNames.pop_back(); - m_choiceHistory->Delete(globalSettings.gui.cfgHistoryMaxItems); - } -} - - -bool MainWindowDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) -{ - if (!filenames.IsEmpty()) - { - const wxString droppedFileName = filenames[0]; - - //create a custom event on main window: execute event after file dropping is completed! (e.g. after mouse is released) - FfsFileDropEvent evt(droppedFileName, dropTarget); - mainDlg->GetEventHandler()->AddPendingEvent(evt); - } - return false; -} - - -template <class Ctrl> -void setDirectoryFromDrop(const wxString& elementName, Ctrl* txtCtrl, wxDirPickerCtrl* dirPicker) -{ - wxString fileName = elementName; - - if (wxDirExists(fileName)) - { - txtCtrl->SetValue(fileName); - dirPicker->SetPath(fileName); - } - else - { - fileName = wxFileName(fileName).GetPath(); - if (wxDirExists(fileName)) - { - txtCtrl->SetValue(fileName); - dirPicker->SetPath(fileName); - } - } -} - - -void MainDialog::OnFilesDropped(FfsFileDropEvent& event) -{ - const wxString droppedFileName = event.m_nameDropped; - const wxPanel* dropTarget = event.m_dropTarget; - - //disable the sync button - enableSynchronization(false); - - //clear grids - currentGridData.clear(); - writeGrid(currentGridData); - - xmlAccess::XmlType fileType = xmlAccess::getXmlType(droppedFileName); - - //test if ffs config file has been dropped - if (fileType == xmlAccess::XML_GUI_CONFIG) - { - if (readConfigurationFromXml(droppedFileName)) - pushStatusInformation(_("Configuration loaded!")); - } - //...or a ffs batch file - else if (fileType == xmlAccess::XML_BATCH_CONFIG) - { - BatchDialog* batchDlg = new BatchDialog(this, droppedFileName); - if (batchDlg->ShowModal() == BatchDialog::BATCH_FILE_SAVED) - pushStatusInformation(_("Batch file created successfully!")); - } - //test if main folder pair is drop target - else if (dropTarget == m_panel1) - { - m_comboBoxDirLeft->SetSelection(wxNOT_FOUND); - setDirectoryFromDrop<wxComboBox>(droppedFileName, m_comboBoxDirLeft, m_dirPickerLeft); - } - - else if (dropTarget == m_panel2) - { - m_comboBoxDirRight->SetSelection(wxNOT_FOUND); - setDirectoryFromDrop<wxComboBox>(droppedFileName, m_comboBoxDirRight, m_dirPickerRight); - } - - else //test if additional folder pairs are drop targets - { - for (std::vector<FolderPairGenerated*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - { - FolderPairGenerated* dirPair = *i; - if (dropTarget == (dirPair->m_panelLeft)) - { - setDirectoryFromDrop<wxTextCtrl>(droppedFileName, dirPair->m_directoryLeft, dirPair->m_dirPickerLeft); - break; - } - else if (dropTarget == (dirPair->m_panelRight)) - { - setDirectoryFromDrop<wxTextCtrl>(droppedFileName, dirPair->m_directoryRight, dirPair->m_dirPickerRight); - break; - } - } + m_choiceHistory->Delete(globalSettings.gui.cfgHistoryMax); } } @@ -1304,7 +1261,7 @@ int findTextPos(const wxArrayString& array, const wxString& text) } -void addPairToFolderHistory(wxComboBox* comboBox, const wxString& newFolder) +void addPairToFolderHistory(wxComboBox* comboBox, const wxString& newFolder, unsigned int maxHistSize) { const wxString oldVal = comboBox->GetValue(); @@ -1315,9 +1272,8 @@ void addPairToFolderHistory(wxComboBox* comboBox, const wxString& newFolder) comboBox->Insert(newFolder, 0); //keep maximal size of history list - const unsigned MAX_FOLDER_HIST_COUNT = 12; - if (comboBox->GetCount() > MAX_FOLDER_HIST_COUNT) - comboBox->Delete(MAX_FOLDER_HIST_COUNT); + if (comboBox->GetCount() > maxHistSize) + comboBox->Delete(maxHistSize); comboBox->SetSelection(wxNOT_FOUND); //don't select anything comboBox->SetValue(oldVal); //but preserve main text! @@ -1326,27 +1282,24 @@ void addPairToFolderHistory(wxComboBox* comboBox, const wxString& newFolder) void MainDialog::addLeftFolderToHistory(const wxString& leftFolder) { - addPairToFolderHistory(m_comboBoxDirLeft, leftFolder); + addPairToFolderHistory(m_directoryLeft, leftFolder, globalSettings.gui.folderHistLeftMax); } void MainDialog::addRightFolderToHistory(const wxString& rightFolder) { - addPairToFolderHistory(m_comboBoxDirRight, rightFolder); + addPairToFolderHistory(m_directoryRight, rightFolder, globalSettings.gui.folderHistRightMax); } void MainDialog::OnSaveConfig(wxCommandEvent& event) { - wxString defaultFileName = wxT("SyncSettings.ffs_gui"); - - if (!proposedConfigFileName.empty()) - defaultFileName = proposedConfigFileName; + const wxString defaultFileName = proposedConfigFileName.empty() ? wxT("SyncSettings.ffs_gui") : proposedConfigFileName; wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, defaultFileName, wxString(_("FreeFileSync configuration")) + wxT(" (*.ffs_gui)|*.ffs_gui"), wxFD_SAVE); if (filePicker->ShowModal() == wxID_OK) { - wxString newFileName = filePicker->GetPath(); + const wxString newFileName = filePicker->GetPath(); if (FreeFileSync::fileExists(newFileName.c_str())) { @@ -1354,7 +1307,7 @@ void MainDialog::OnSaveConfig(wxCommandEvent& event) if (messageDlg->ShowModal() != wxID_OK) { - pushStatusInformation(_("Save aborted!")); + OnSaveConfig(event); //retry return; } } @@ -1397,7 +1350,7 @@ void MainDialog::loadConfiguration(const wxString& filename) if (!filename.IsEmpty()) { //clear grids currentGridData.clear(); - writeGrid(currentGridData); + updateGuiGrid(); if (readConfigurationFromXml(filename)) pushStatusInformation(_("Configuration loaded!")); @@ -1477,12 +1430,33 @@ void MainDialog::OnCompareByContent(wxCommandEvent& event) void MainDialog::OnClose(wxCloseEvent &event) { - Destroy(); + requestShutdown(); } void MainDialog::OnQuit(wxCommandEvent &event) { + requestShutdown(); +} + + +void MainDialog::requestShutdown() +{ + /* + if (globalSettings.gui.popupOnConfigChange) + { + if (lastConfigurationSaved != getCurrentConfiguration()) + { + ... +wxID_OK + OnSaveConfig(wxCommandEvent& event) + + ; + wxMessageBox(wxT("ji")); + } + } + */ + Destroy(); } @@ -1508,7 +1482,7 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program { if (programStartup) { - if (filename == FreeFileSync::LAST_CONFIG_FILE && !wxFileExists(filename)) //do not show error in this case + if (filename == xmlAccess::LAST_CONFIG_FILE && !wxFileExists(filename)) //do not show error in this case ; else //program startup: show error message and load defaults wxMessageBox(error.show().c_str(), _("Error"), wxOK | wxICON_ERROR); @@ -1529,11 +1503,11 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program //read folder pairs: //clear existing pairs first - m_comboBoxDirLeft->SetSelection(wxNOT_FOUND); - m_comboBoxDirLeft->SetValue(wxEmptyString); + m_directoryLeft->SetSelection(wxNOT_FOUND); + m_directoryLeft->SetValue(wxEmptyString); m_dirPickerLeft->SetPath(wxEmptyString); - m_comboBoxDirRight->SetSelection(wxNOT_FOUND); - m_comboBoxDirRight->SetValue(wxEmptyString); + m_directoryRight->SetSelection(wxNOT_FOUND); + m_directoryRight->SetValue(wxEmptyString); m_dirPickerRight->SetPath(wxEmptyString); clearFolderPairs(); @@ -1543,13 +1517,13 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program //set main folder pair std::vector<FolderPair>::const_iterator main = guiCfg.directoryPairs.begin(); - m_comboBoxDirLeft->SetValue(main->leftDirectory.c_str()); + m_directoryLeft->SetValue(main->leftDirectory.c_str()); const wxString leftDirFormatted = FreeFileSync::getFormattedDirectoryName(main->leftDirectory).c_str(); if (wxDirExists(leftDirFormatted)) m_dirPickerLeft->SetPath(leftDirFormatted); addLeftFolderToHistory(main->leftDirectory.c_str()); //another hack: wxCombobox::Insert() asynchronously sends message overwriting a later wxCombobox::SetValue()!!! :( - m_comboBoxDirRight->SetValue(main->rightDirectory.c_str()); + m_directoryRight->SetValue(main->rightDirectory.c_str()); const wxString rightDirFormatted = FreeFileSync::getFormattedDirectoryName(main->rightDirectory).c_str(); if (wxDirExists(rightDirFormatted)) m_dirPickerRight->SetPath(rightDirFormatted); @@ -1571,7 +1545,9 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program //########################################################### addFileToCfgHistory(filename); //put filename on list of last used config files - if (filename == FreeFileSync::LAST_CONFIG_FILE) //set title + lastConfigurationSaved = guiCfg; + + if (filename == xmlAccess::LAST_CONFIG_FILE) //set title { SetTitle(wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")); proposedConfigFileName.clear(); @@ -1588,16 +1564,7 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program bool MainDialog::writeConfigurationToXml(const wxString& filename) { - xmlAccess::XmlGuiConfig guiCfg; - - //load structure with basic settings "mainCfg" - guiCfg.mainCfg = cfg; - guiCfg.directoryPairs = getFolderPairs(); - - //load structure with gui settings - guiCfg.hideFilteredElements = hideFilteredElements; - - guiCfg.ignoreErrors = ignoreErrors; + const xmlAccess::XmlGuiConfig guiCfg = getCurrentConfiguration(); //write config to XML try @@ -1613,7 +1580,9 @@ bool MainDialog::writeConfigurationToXml(const wxString& filename) //put filename on list of last used config files addFileToCfgHistory(filename); - if (filename == FreeFileSync::LAST_CONFIG_FILE) //set title + lastConfigurationSaved = guiCfg; + + if (filename == xmlAccess::LAST_CONFIG_FILE) //set title { SetTitle(wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")); proposedConfigFileName.clear(); @@ -1628,6 +1597,23 @@ bool MainDialog::writeConfigurationToXml(const wxString& filename) } +xmlAccess::XmlGuiConfig MainDialog::getCurrentConfiguration() const +{ + xmlAccess::XmlGuiConfig guiCfg; + + //load structure with basic settings "mainCfg" + guiCfg.mainCfg = cfg; + guiCfg.directoryPairs = getFolderPairs(); + + //load structure with gui settings + guiCfg.hideFilteredElements = hideFilteredElements; + + guiCfg.ignoreErrors = ignoreErrors; + + return guiCfg; +} + + void MainDialog::OnShowHelpDialog(wxCommandEvent &event) { HelpDlg* helpDlg = new HelpDlg(this); @@ -1646,7 +1632,7 @@ void MainDialog::OnFilterButton(wxCommandEvent &event) else FreeFileSync::includeAllRowsOnGrid(currentGridData); - writeGrid(currentGridData); + updateGuiGrid(); } @@ -1659,7 +1645,7 @@ void MainDialog::OnHideFilteredButton(wxCommandEvent &event) m_gridLeft->ClearSelection(); m_gridRight->ClearSelection(); - writeGrid(currentGridData); + updateGuiGrid(); event.Skip(); } @@ -1689,7 +1675,7 @@ void MainDialog::OnConfigureFilter(wxHyperlinkEvent &event) updateFilterButton(m_bpButtonFilter, cfg.filterIsActive); - writeGrid(currentGridData); + updateGuiGrid(); } } //no event.Skip() here, to not start browser @@ -1700,42 +1686,42 @@ void MainDialog::OnLeftOnlyFiles(wxCommandEvent& event) { leftOnlyFilesActive = !leftOnlyFilesActive; updateViewFilterButtons(); - writeGrid(currentGridData); + updateGuiGrid(); }; void MainDialog::OnLeftNewerFiles(wxCommandEvent& event) { leftNewerFilesActive = !leftNewerFilesActive; updateViewFilterButtons(); - writeGrid(currentGridData); + updateGuiGrid(); }; void MainDialog::OnDifferentFiles(wxCommandEvent& event) { differentFilesActive = !differentFilesActive; updateViewFilterButtons(); - writeGrid(currentGridData); + updateGuiGrid(); }; void MainDialog::OnRightNewerFiles(wxCommandEvent& event) { rightNewerFilesActive = !rightNewerFilesActive; updateViewFilterButtons(); - writeGrid(currentGridData); + updateGuiGrid(); }; void MainDialog::OnRightOnlyFiles(wxCommandEvent& event) { rightOnlyFilesActive = !rightOnlyFilesActive; updateViewFilterButtons(); - writeGrid(currentGridData); + updateGuiGrid(); }; void MainDialog::OnEqualFiles(wxCommandEvent& event) { equalFilesActive = !equalFilesActive; updateViewFilterButtons(); - writeGrid(currentGridData); + updateGuiGrid(); }; @@ -1843,29 +1829,22 @@ void MainDialog::updateCompareButtons() //clear grids currentGridData.clear(); - writeGrid(currentGridData); + updateGuiGrid(); } -std::vector<FolderPair> MainDialog::getFolderPairs() +std::vector<FolderPair> MainDialog::getFolderPairs() const { std::vector<FolderPair> output; //add main pair - FolderPair newPair; - newPair.leftDirectory = m_comboBoxDirLeft->GetValue().c_str(); - newPair.rightDirectory = m_comboBoxDirRight->GetValue().c_str(); - output.push_back(newPair); + output.push_back(FolderPair(m_directoryLeft->GetValue().c_str(), + m_directoryRight->GetValue().c_str())); //add additional pairs - for (std::vector<FolderPairGenerated*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - { - FolderPairGenerated* dirPair = *i; - newPair.leftDirectory = dirPair->m_directoryLeft->GetValue().c_str(); - newPair.rightDirectory = dirPair->m_directoryRight->GetValue().c_str(); - output.push_back(newPair); - } - + for (std::vector<FolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) + output.push_back(FolderPair((*i)->m_directoryLeft->GetValue().c_str(), + (*i)->m_directoryRight->GetValue().c_str())); return output; } @@ -1878,9 +1857,9 @@ void MainDialog::OnCompare(wxCommandEvent &event) wxBusyCursor dummy; //show hourglass cursor - //save memory by clearing old result list + //prevent temporary memory peak by clearing old result list currentGridData.clear(); - writeGrid(currentGridData); //refresh GUI grid + updateGuiGrid(); //refresh GUI grid bool aborted = false; try @@ -1900,7 +1879,8 @@ void MainDialog::OnCompare(wxCommandEvent &event) //if (output.size < 50000) statusHandler.updateStatusText(_("Sorting file list...")); statusHandler.forceUiRefresh(); //keep total number of scanned files up to date - std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<true, SORT_ON_LEFT>); + + gridDataView.sortView(GridView::SORT_BY_DIRECTORY, true, true); //filter currentGridData if option is set if (cfg.filterIsActive) @@ -1936,12 +1916,12 @@ void MainDialog::OnCompare(wxCommandEvent &event) m_gridRight->ClearSelection(); //add to folder history after successful comparison only - addLeftFolderToHistory(m_comboBoxDirLeft->GetValue()); - addRightFolderToHistory(m_comboBoxDirRight->GetValue()); + addLeftFolderToHistory(m_directoryLeft->GetValue()); + addRightFolderToHistory(m_directoryRight->GetValue()); } //refresh grid in ANY case! (also on abort) - writeGrid(currentGridData); + updateGuiGrid(); } @@ -1952,28 +1932,33 @@ void MainDialog::OnAbortCompare(wxCommandEvent& event) } -void MainDialog::writeGrid(const FileCompareResult& gridData) +void MainDialog::updateGuiGrid() { - m_gridLeft->BeginBatch(); + m_gridLeft->BeginBatch(); //necessary?? m_gridMiddle->BeginBatch(); m_gridRight->BeginBatch(); - mapGridDataToUI(gridRefUI, gridData); //update gridRefUI - updateStatusInformation(gridRefUI); //write status information for gridRefUI + updateGridViewData(); //update gridDataView and write status information - //all three grids retrieve their data directly via gridRefUI!!! + //all three grids retrieve their data directly via currentGridData!!! //the only thing left to do is notify the grids to update their sizes (nr of rows), since this has to be communicated by the grids via messages m_gridLeft->updateGridSizes(); m_gridMiddle->updateGridSizes(); m_gridRight->updateGridSizes(); //enlarge label width to display row numbers correctly - int nrOfRows = m_gridLeft->GetNumberRows(); + const int nrOfRows = m_gridLeft->GetNumberRows(); + if (nrOfRows >= 1) { - int nrOfDigits = int(floor(log10(double(nrOfRows)) + 1)); - m_gridLeft->SetRowLabelSize(nrOfDigits * 8 + 4); - m_gridRight->SetRowLabelSize(nrOfDigits * 8 + 4); +#ifdef FFS_WIN + const int digitWidth = 8; +#elif defined FFS_LINUX + const int digitWidth = 10; +#endif + const int nrOfDigits = int(floor(log10(double(nrOfRows)) + 1)); + m_gridLeft->SetRowLabelSize(nrOfDigits * digitWidth + 4); + m_gridRight->SetRowLabelSize(nrOfDigits * digitWidth + 4); } m_gridLeft->EndBatch(); @@ -2009,6 +1994,7 @@ void MainDialog::OnSync(wxCommandEvent& event) globalSettings.shared.copyFileSymlinks, globalSettings.shared.traverseDirectorySymlinks, globalSettings.shared.warningSignificantDifference, + globalSettings.shared.warningNotEnoughDiskSpace, &statusHandler); synchronization.startSynchronizationProcess(currentGridData, cfg.syncConfiguration); @@ -2019,14 +2005,14 @@ void MainDialog::OnSync(wxCommandEvent& event) //show remaining files that have not been processed: put DIRECTLY after startSynchronizationProcess() and DON'T call any wxWidgets functions - //in between! Else CustomGrid might access the obsolete gridRefUI! - writeGrid(currentGridData); + //in between! Else CustomGrid might access obsolete data entries in currentGridData! + updateGuiGrid(); m_gridLeft->ClearSelection(); m_gridMiddle->ClearSelection(); m_gridRight->ClearSelection(); - if (currentGridData.size() > 0) + if (gridDataView.elementsTotal() > 0) pushStatusInformation(_("Not all items were synchronized! Have a look at the list.")); else { @@ -2039,14 +2025,14 @@ void MainDialog::OnSync(wxCommandEvent& event) void MainDialog::OnLeftGridDoubleClick(wxGridEvent& event) { - openWithFileManager(event.GetRow(), m_gridLeft); + openWithFileManager(event.GetRow(), true); event.Skip(); } void MainDialog::OnRightGridDoubleClick(wxGridEvent& event) { - openWithFileManager(event.GetRow(), m_gridRight); + openWithFileManager(event.GetRow(), false); event.Skip(); } @@ -2067,35 +2053,30 @@ void MainDialog::OnSortLeftGrid(wxGridEvent& event) lastSortGrid = m_gridLeft; //start sort - xmlAccess::ColumnTypes columnType = m_gridLeft->getTypeAtPos(currentSortColumn); - if (columnType == xmlAccess::FULL_NAME) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<true, SORT_ON_LEFT>); //sort by rel name here too! - else std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<false, SORT_ON_LEFT>); - } - else if (columnType == xmlAccess::FILENAME) + const xmlAccess::ColumnTypes columnType = m_gridLeft->getTypeAtPos(currentSortColumn); + switch (columnType) { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByFileName<true, SORT_ON_LEFT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByFileName<false, SORT_ON_LEFT>); - } - else if (columnType == xmlAccess::REL_PATH) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<true, SORT_ON_LEFT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<false, SORT_ON_LEFT>); - } - else if (columnType == xmlAccess::SIZE) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByFileSize<true, SORT_ON_LEFT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByFileSize<false, SORT_ON_LEFT>); - } - else if (columnType == xmlAccess::DATE) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByDate<true, SORT_ON_LEFT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByDate<false, SORT_ON_LEFT>); + case xmlAccess::FULL_NAME: + gridDataView.sortView(GridView::SORT_BY_DIRECTORY, true, sortAscending); + break; + case xmlAccess::FILENAME: + gridDataView.sortView(GridView::SORT_BY_FILENAME, true, sortAscending); + break; + case xmlAccess::REL_PATH: + gridDataView.sortView(GridView::SORT_BY_REL_NAME, true, sortAscending); + break; + case xmlAccess::DIRECTORY: + gridDataView.sortView(GridView::SORT_BY_DIRECTORY, true, sortAscending); + break; + case xmlAccess::SIZE: + gridDataView.sortView(GridView::SORT_BY_FILESIZE, true, sortAscending); + break; + case xmlAccess::DATE: + gridDataView.sortView(GridView::SORT_BY_DATE, true, sortAscending); + break; } - else assert(false); - writeGrid(currentGridData); //needed to refresh gridRefUI references + updateGuiGrid(); //refresh gridDataView //set sort direction indicator on UI m_gridMiddle->setSortMarker(-1); @@ -2105,7 +2086,32 @@ void MainDialog::OnSortLeftGrid(wxGridEvent& event) else m_gridLeft->setSortMarker(currentSortColumn, globalResource.bitmapSmallDown); } - event.Skip(); +} + + +void MainDialog::OnSortMiddleGrid(wxGridEvent& event) +{ + //determine direction for std::sort() + static bool sortAscending = true; + if (lastSortColumn != 0 || lastSortGrid != m_gridMiddle) + sortAscending = true; + else + sortAscending = !sortAscending; + lastSortColumn = 0; + lastSortGrid = m_gridMiddle; + + //start sort + gridDataView.sortView(GridView::SORT_BY_CMP_RESULT, true, sortAscending); + + updateGuiGrid(); //refresh gridDataView + + //set sort direction indicator on UI + m_gridLeft->setSortMarker(-1); + m_gridRight->setSortMarker(-1); + if (sortAscending) + m_gridMiddle->setSortMarker(0, globalResource.bitmapSmallUp); + else + m_gridMiddle->setSortMarker(0, globalResource.bitmapSmallDown); } @@ -2125,35 +2131,30 @@ void MainDialog::OnSortRightGrid(wxGridEvent& event) lastSortGrid = m_gridRight; //start sort - xmlAccess::ColumnTypes columnType = m_gridRight->getTypeAtPos(currentSortColumn); - if (columnType == xmlAccess::FULL_NAME) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<true, SORT_ON_RIGHT>); //sort by rel name here too! - else std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<false, SORT_ON_RIGHT>); - } - else if (columnType == xmlAccess::FILENAME) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByFileName<true, SORT_ON_RIGHT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByFileName<false, SORT_ON_RIGHT>); - } - else if (columnType == xmlAccess::REL_PATH) + const xmlAccess::ColumnTypes columnType = m_gridRight->getTypeAtPos(currentSortColumn); + switch (columnType) { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<true, SORT_ON_RIGHT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByRelativeName<false, SORT_ON_RIGHT>); - } - else if (columnType == xmlAccess::SIZE) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByFileSize<true, SORT_ON_RIGHT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByFileSize<false, SORT_ON_RIGHT>); - } - else if (columnType == xmlAccess::DATE) - { - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByDate<true, SORT_ON_RIGHT>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByDate<false, SORT_ON_RIGHT>); + case xmlAccess::FULL_NAME: + gridDataView.sortView(GridView::SORT_BY_DIRECTORY, false, sortAscending); + break; + case xmlAccess::FILENAME: + gridDataView.sortView(GridView::SORT_BY_FILENAME, false, sortAscending); + break; + case xmlAccess::REL_PATH: + gridDataView.sortView(GridView::SORT_BY_REL_NAME, false, sortAscending); + break; + case xmlAccess::DIRECTORY: + gridDataView.sortView(GridView::SORT_BY_DIRECTORY, false, sortAscending); + break; + case xmlAccess::SIZE: + gridDataView.sortView(GridView::SORT_BY_FILESIZE, false, sortAscending); + break; + case xmlAccess::DATE: + gridDataView.sortView(GridView::SORT_BY_DATE, false, sortAscending); + break; } - else assert(false); - writeGrid(currentGridData); //needed to refresh gridRefUI references + updateGuiGrid(); //refresh gridDataView //set sort direction indicator on UI m_gridLeft->setSortMarker(-1); @@ -2163,55 +2164,25 @@ void MainDialog::OnSortRightGrid(wxGridEvent& event) else m_gridRight->setSortMarker(currentSortColumn, globalResource.bitmapSmallDown); } - event.Skip(); -} - - -void MainDialog::OnSortMiddleGrid(wxGridEvent& event) -{ - //determine direction for std::sort() - static bool sortAscending = true; - if (lastSortColumn != 0 || lastSortGrid != m_gridMiddle) - sortAscending = true; - else - sortAscending = !sortAscending; - lastSortColumn = 0; - lastSortGrid = m_gridMiddle; - - //start sort - if (sortAscending) std::sort(currentGridData.begin(), currentGridData.end(), sortByCmpResult<true>); - else std::sort(currentGridData.begin(), currentGridData.end(), sortByCmpResult<false>); - - writeGrid(currentGridData); //needed to refresh gridRefUI references - - //set sort direction indicator on UI - m_gridLeft->setSortMarker(-1); - m_gridRight->setSortMarker(-1); - if (sortAscending) - m_gridMiddle->setSortMarker(0, globalResource.bitmapSmallUp); - else - m_gridMiddle->setSortMarker(0, globalResource.bitmapSmallDown); - - event.Skip(); } void MainDialog::OnSwapDirs( wxCommandEvent& event ) { - //swap directory names : main pair - const wxString leftDir = m_comboBoxDirLeft->GetValue(); - const wxString rightDir = m_comboBoxDirRight->GetValue(); - m_comboBoxDirLeft->SetSelection(wxNOT_FOUND); - m_comboBoxDirRight->SetSelection(wxNOT_FOUND); + //swap directory names: main pair + const wxString leftDir = m_directoryLeft->GetValue(); + const wxString rightDir = m_directoryRight->GetValue(); + m_directoryLeft->SetSelection(wxNOT_FOUND); + m_directoryRight->SetSelection(wxNOT_FOUND); - m_comboBoxDirLeft->SetValue(rightDir); - m_comboBoxDirRight->SetValue(leftDir); + m_directoryLeft->SetValue(rightDir); + m_directoryRight->SetValue(leftDir); //additional pairs wxString tmp; - for (std::vector<FolderPairGenerated*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) + for (std::vector<FolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) { - FolderPairGenerated* dirPair = *i; + FolderPairPanel* dirPair = *i; tmp = dirPair->m_directoryLeft->GetValue(); dirPair->m_directoryLeft->SetValue(dirPair->m_directoryRight->GetValue()); dirPair->m_directoryRight->SetValue(tmp); @@ -2224,87 +2195,117 @@ void MainDialog::OnSwapDirs( wxCommandEvent& event ) //swap grid information FreeFileSync::swapGrids(currentGridData); - writeGrid(currentGridData); + updateGuiGrid(); event.Skip(); } -void MainDialog::updateStatusInformation(const GridView& visibleGrid) +void MainDialog::updateGridViewData() { + const GridView::StatusInfo result = gridDataView.update( + leftOnlyFilesActive, + rightOnlyFilesActive, + leftNewerFilesActive, + rightNewerFilesActive, + differentFilesActive, + equalFilesActive, + hideFilteredElements); + + //hide or enable view filter buttons + if (result.existsLeftOnly) + m_bpButtonLeftOnly->Show(); + else + m_bpButtonLeftOnly->Hide(); + + if (result.existsRightOnly) + m_bpButtonRightOnly->Show(); + else + m_bpButtonRightOnly->Hide(); + + if (result.existsLeftNewer) + m_bpButtonLeftNewer->Show(); + else + m_bpButtonLeftNewer->Hide(); + + if (result.existsRightNewer) + m_bpButtonRightNewer->Show(); + else + m_bpButtonRightNewer->Hide(); + + if (result.existsDifferent) + m_bpButtonDifferent->Show(); + else + m_bpButtonDifferent->Hide(); + + if (result.existsEqual) + m_bpButtonEqual->Show(); + else + m_bpButtonEqual->Hide(); + + if ( result.existsLeftOnly || + result.existsRightOnly || + result.existsLeftNewer || + result.existsRightNewer || + result.existsDifferent || + result.existsEqual) + { + m_panel112->Show(); + m_panel112->Layout(); + } + else + m_panel112->Hide(); + + bSizer3->Layout(); + + + //update status information while (stackObjects.size() > 0) stackObjects.pop(); - unsigned int filesOnLeftView = 0; - unsigned int foldersOnLeftView = 0; - unsigned int filesOnRightView = 0; - unsigned int foldersOnRightView = 0; - wxULongLong filesizeLeftView; - wxULongLong filesizeRightView; - wxString statusLeftNew; wxString statusMiddleNew; wxString statusRightNew; - for (GridView::const_iterator i = visibleGrid.begin(); i != visibleGrid.end(); ++i) - { - const FileCompareLine& refLine = currentGridData[*i]; - - //calculate total number of bytes for each side - if (refLine.fileDescrLeft.objType == FileDescrLine::TYPE_FILE) - { - filesizeLeftView+= refLine.fileDescrLeft.fileSize; - ++filesOnLeftView; - } - else if (refLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - ++foldersOnLeftView; - - if (refLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE) - { - filesizeRightView+= refLine.fileDescrRight.fileSize; - ++filesOnRightView; - } - else if (refLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - ++foldersOnRightView; - } //################################################# //format numbers to text: //show status information on "root" level. - if (foldersOnLeftView) + if (result.foldersOnLeftView) { - if (foldersOnLeftView == 1) - statusLeftNew+= _("1 directory"); + if (result.foldersOnLeftView == 1) + statusLeftNew += _("1 directory"); else { - wxString folderCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(foldersOnLeftView)); + wxString folderCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(result.foldersOnLeftView)); wxString outputString = _("%x directories"); outputString.Replace(wxT("%x"), folderCount, false); - statusLeftNew+= outputString; + statusLeftNew += outputString; } - if (filesOnLeftView) - statusLeftNew+= wxT(", "); + if (result.filesOnLeftView) + statusLeftNew += wxT(", "); } - if (filesOnLeftView) + if (result.filesOnLeftView) { - if (filesOnLeftView == 1) - statusLeftNew+= _("1 file,"); + if (result.filesOnLeftView == 1) + statusLeftNew += _("1 file,"); else { - wxString fileCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(filesOnLeftView)); + wxString fileCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(result.filesOnLeftView)); wxString outputString = _("%x files,"); outputString.Replace(wxT("%x"), fileCount, false); - statusLeftNew+= outputString; + statusLeftNew += outputString; } - statusLeftNew+= wxT(" "); - statusLeftNew+= FreeFileSync::formatFilesizeToShortString(filesizeLeftView); + statusLeftNew += wxT(" "); + statusLeftNew += FreeFileSync::formatFilesizeToShortString(result.filesizeLeftView); } - wxString objectsView = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(visibleGrid.size())); - if (currentGridData.size() == 1) + const wxString objectsView = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(gridDataView.elementsOnView())); + const unsigned int objCount = gridDataView.elementsTotal(); + if (objCount == 1) { wxString outputString = _("%x of 1 row in view"); outputString.Replace(wxT("%x"), objectsView, false); @@ -2312,7 +2313,7 @@ void MainDialog::updateStatusInformation(const GridView& visibleGrid) } else { - wxString objectsTotal = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(currentGridData.size())); + const wxString objectsTotal = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objCount)); wxString outputString = _("%x of %y rows in view"); outputString.Replace(wxT("%x"), objectsView, false); @@ -2320,38 +2321,38 @@ void MainDialog::updateStatusInformation(const GridView& visibleGrid) statusMiddleNew = outputString; } - if (foldersOnRightView) + if (result.foldersOnRightView) { - if (foldersOnRightView == 1) - statusRightNew+= _("1 directory"); + if (result.foldersOnRightView == 1) + statusRightNew += _("1 directory"); else { - wxString folderCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(foldersOnRightView)); + wxString folderCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(result.foldersOnRightView)); wxString outputString = _("%x directories"); outputString.Replace(wxT("%x"), folderCount, false); - statusRightNew+= outputString; + statusRightNew += outputString; } - if (filesOnRightView) - statusRightNew+= wxT(", "); + if (result.filesOnRightView) + statusRightNew += wxT(", "); } - if (filesOnRightView) + if (result.filesOnRightView) { - if (filesOnRightView == 1) - statusRightNew+= _("1 file,"); + if (result.filesOnRightView == 1) + statusRightNew += _("1 file,"); else { - wxString fileCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(filesOnRightView)); + wxString fileCount = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(result.filesOnRightView)); wxString outputString = _("%x files,"); outputString.Replace(wxT("%x"), fileCount, false); - statusRightNew+= outputString; + statusRightNew += outputString; } - statusRightNew+= wxT(" "); - statusRightNew+= FreeFileSync::formatFilesizeToShortString(filesizeRightView); + statusRightNew += wxT(" "); + statusRightNew += FreeFileSync::formatFilesizeToShortString(result.filesizeRightView); } @@ -2367,97 +2368,6 @@ void MainDialog::updateStatusInformation(const GridView& visibleGrid) } -void MainDialog::mapGridDataToUI(GridView& output, const FileCompareResult& fileCmpResult) -{ - output.clear(); - - //only show those view filter buttons that really need to be displayed - bool leftOnly, rightOnly, leftNewer, rightNewer, different, equal; - leftOnly = rightOnly = leftNewer = rightNewer = different = equal = false; - - unsigned int currentRow = 0; - for (FileCompareResult::const_iterator i = fileCmpResult.begin(); i != fileCmpResult.end(); ++i, ++currentRow) - { - //hide filtered row, if corresponding option is set - if (hideFilteredElements && !i->selectedForSynchronization) - continue; - - //process UI filter settings - switch (i->cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - leftOnly = true; - if (!leftOnlyFilesActive) continue; - break; - case FILE_RIGHT_SIDE_ONLY: - rightOnly = true; - if (!rightOnlyFilesActive) continue; - break; - case FILE_LEFT_NEWER: - leftNewer = true; - if (!leftNewerFilesActive) continue; - break; - case FILE_RIGHT_NEWER: - rightNewer = true; - if (!rightNewerFilesActive) continue; - break; - case FILE_DIFFERENT: - different = true; - if (!differentFilesActive) continue; - break; - case FILE_EQUAL: - equal = true; - if (!equalFilesActive) continue; - break; - default: - assert (false); - } - output.push_back(currentRow); - } - - //hide or enable view filter buttons - if (leftOnly) - m_bpButtonLeftOnly->Show(); - else - m_bpButtonLeftOnly->Hide(); - - if (rightOnly) - m_bpButtonRightOnly->Show(); - else - m_bpButtonRightOnly->Hide(); - - if (leftNewer) - m_bpButtonLeftNewer->Show(); - else - m_bpButtonLeftNewer->Hide(); - - if (rightNewer) - m_bpButtonRightNewer->Show(); - else - m_bpButtonRightNewer->Hide(); - - if (different) - m_bpButtonDifferent->Show(); - else - m_bpButtonDifferent->Hide(); - - if (equal) - m_bpButtonEqual->Show(); - else - m_bpButtonEqual->Hide(); - - if (leftOnly || rightOnly || leftNewer || rightNewer || different || equal) - { - m_panel112->Show(); - m_panel112->Layout(); - } - else - m_panel112->Hide(); - - bSizer3->Layout(); -} - - void MainDialog::OnAddFolderPair(wxCommandEvent& event) { addFolderPair(wxEmptyString, wxEmptyString); @@ -2467,7 +2377,7 @@ void MainDialog::OnAddFolderPair(wxCommandEvent& event) //clear grids currentGridData.clear(); - writeGrid(currentGridData); + updateGuiGrid(); } @@ -2475,7 +2385,7 @@ void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) { //find folder pair originating the event const wxObject* const eventObj = event.GetEventObject(); - for (std::vector<FolderPairGenerated*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) + for (std::vector<FolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) { if (eventObj == static_cast<wxObject*>((*i)->m_bpButtonRemovePair)) { @@ -2486,7 +2396,7 @@ void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) //clear grids currentGridData.clear(); - writeGrid(currentGridData); + updateGuiGrid(); return; } } @@ -2513,7 +2423,7 @@ void MainDialog::addFolderPair(const std::vector<FolderPair>& newPairs) for (std::vector<FolderPair>::const_iterator i = newPairs.begin(); i != newPairs.end(); ++i) { //add new folder pair - FolderPairGenerated* newPair = new FolderPairGenerated(m_scrolledWindowFolderPairs); + FolderPairPanel* newPair = new FolderPairPanel(m_scrolledWindowFolderPairs); newPair->m_bitmap23->SetBitmap(*globalResource.bitmapLink); newPair->m_bpButtonRemovePair->SetBitmapLabel(*globalResource.bitmapRemoveFolderPair); @@ -2530,18 +2440,8 @@ void MainDialog::addFolderPair(const std::vector<FolderPair>& newPairs) m_scrolledWindowFolderPairs->Fit(); //register events - newPair->m_dirPickerLeft->Connect(wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler(MainDialog::OnDirSelected), NULL, this); - newPair->m_dirPickerRight->Connect(wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler(MainDialog::OnDirSelected), NULL, this); - - newPair->m_directoryLeft->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(MainDialog::OnWriteDirManually), NULL, this ); - newPair->m_directoryRight->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(MainDialog::OnWriteDirManually), NULL, this ); - newPair->m_bpButtonRemovePair->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolderPair), NULL, this ); - //prepare drag & drop - newPair->m_panelLeft->SetDropTarget(new MainWindowDropTarget(this, newPair->m_panelLeft)); //ownership passed - newPair->m_panelRight->SetDropTarget(new MainWindowDropTarget(this, newPair->m_panelRight)); - //insert directory names if provided newPair->m_directoryLeft->SetValue(i->leftDirectory.c_str()); const wxString leftDirFormatted = FreeFileSync::getFormattedDirectoryName(i->leftDirectory).c_str(); @@ -2579,7 +2479,7 @@ void MainDialog::removeFolderPair(const int pos, bool refreshLayout) { wxSize pairSize; //remove folder pairs from window - FolderPairGenerated* pairToDelete = additionalFolderPairs[pos]; + FolderPairPanel* pairToDelete = additionalFolderPairs[pos]; pairSize = pairToDelete->GetSize(); bSizerFolderPairs->Detach(pairToDelete); //Remove() does not work on Window*, so do it manually @@ -2620,404 +2520,6 @@ void MainDialog::clearFolderPairs() //######################################################################################################## -CompareStatusHandler::CompareStatusHandler(MainDialog* dlg) : - mainDialog(dlg), - ignoreErrors(false), - currentProcess(StatusHandler::PROCESS_NONE) -{ - //prevent user input during "compare", do not disable maindialog since abort-button would also be disabled - //it's not nice, but works - even has the advantage that certain actions are still possible: exit, about.. - mainDialog->m_radioBtnSizeDate->Disable(); - mainDialog->m_radioBtnContent->Disable(); - mainDialog->m_bpButtonFilter->Disable(); - mainDialog->m_hyperlinkCfgFilter->Disable(); - mainDialog->m_checkBoxHideFilt->Disable(); - mainDialog->m_buttonSync->Disable(); - mainDialog->m_dirPickerLeft->Disable(); - mainDialog->m_dirPickerRight->Disable(); - mainDialog->m_bpButtonSwap->Disable(); - mainDialog->m_bpButtonLeftOnly->Disable(); - mainDialog->m_bpButtonLeftNewer->Disable(); - mainDialog->m_bpButtonEqual->Disable(); - mainDialog->m_bpButtonDifferent->Disable(); - mainDialog->m_bpButtonRightNewer->Disable(); - mainDialog->m_bpButtonRightOnly->Disable(); - mainDialog->m_panel1->Disable(); - mainDialog->m_panel2->Disable(); - mainDialog->m_panel3->Disable(); - mainDialog->m_panel11->Disable(); - mainDialog->m_panel12->Disable(); - mainDialog->m_panel13->Disable(); - mainDialog->m_bpButtonSave->Disable(); - mainDialog->m_bpButtonLoad->Disable(); - mainDialog->m_choiceHistory->Disable(); - mainDialog->m_bpButton10->Disable(); - mainDialog->m_bpButton14->Disable(); - mainDialog->m_scrolledWindowFolderPairs->Disable(); - mainDialog->m_menubar1->EnableTop(0, false); - mainDialog->m_menubar1->EnableTop(1, false); - mainDialog->m_menubar1->EnableTop(2, false); - - //show abort button - mainDialog->m_buttonAbort->Enable(); - mainDialog->m_buttonAbort->Show(); - mainDialog->m_buttonCompare->Disable(); - mainDialog->m_buttonCompare->Hide(); - mainDialog->m_buttonAbort->SetFocus(); - - //display status panel during compare - mainDialog->compareStatus->init(); //clear old values - mainDialog->compareStatus->Show(); - - //updateUiNow(); - mainDialog->bSizer1->Layout(); //both sizers need to recalculate! - mainDialog->bSizer6->Layout(); //adapt layout for wxBitmapWithImage - mainDialog->Refresh(); -} - - -CompareStatusHandler::~CompareStatusHandler() -{ - updateUiNow(); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks - - //reenable complete main dialog - mainDialog->m_radioBtnSizeDate->Enable(); - mainDialog->m_radioBtnContent->Enable(); - mainDialog->m_bpButtonFilter->Enable(); - mainDialog->m_hyperlinkCfgFilter->Enable(); - mainDialog->m_checkBoxHideFilt->Enable(); - mainDialog->m_buttonSync->Enable(); - mainDialog->m_dirPickerLeft->Enable(); - mainDialog->m_dirPickerRight->Enable(); - mainDialog->m_bpButtonSwap->Enable(); - mainDialog->m_bpButtonLeftOnly->Enable(); - mainDialog->m_bpButtonLeftNewer->Enable(); - mainDialog->m_bpButtonEqual->Enable(); - mainDialog->m_bpButtonDifferent->Enable(); - mainDialog->m_bpButtonRightNewer->Enable(); - mainDialog->m_bpButtonRightOnly->Enable(); - mainDialog->m_panel1->Enable(); - mainDialog->m_panel2->Enable(); - mainDialog->m_panel3->Enable(); - mainDialog->m_panel11->Enable(); - mainDialog->m_panel12->Enable(); - mainDialog->m_panel13->Enable(); - mainDialog->m_bpButtonSave->Enable(); - mainDialog->m_bpButtonLoad->Enable(); - mainDialog->m_choiceHistory->Enable(); - mainDialog->m_bpButton10->Enable(); - mainDialog->m_bpButton14->Enable(); - mainDialog->m_scrolledWindowFolderPairs->Enable(); - mainDialog->m_menubar1->EnableTop(0, true); - mainDialog->m_menubar1->EnableTop(1, true); - mainDialog->m_menubar1->EnableTop(2, true); - - if (abortRequested) - mainDialog->pushStatusInformation(_("Operation aborted!")); - - mainDialog->m_buttonAbort->Disable(); - mainDialog->m_buttonAbort->Hide(); - mainDialog->m_buttonCompare->Enable(); //enable compare button - mainDialog->m_buttonCompare->Show(); - - //hide status panel from main window - mainDialog->compareStatus->Hide(); - - mainDialog->bSizer6->Layout(); //adapt layout for wxBitmapWithImage - - mainDialog->Layout(); - mainDialog->Refresh(); -} - - -inline -void CompareStatusHandler::updateStatusText(const Zstring& text) -{ - mainDialog->compareStatus->setStatusText_NoUpdate(text); -} - - -void CompareStatusHandler::initNewProcess(int objectsTotal, double dataTotal, Process processID) -{ - currentProcess = processID; - - if (currentProcess == StatusHandler::PROCESS_SCANNING) - ; - else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) - { - mainDialog->compareStatus->switchToCompareBytewise(objectsTotal, dataTotal); - mainDialog->Layout(); - } - - else assert(false); -} - - -inline -void CompareStatusHandler::updateProcessedData(int objectsProcessed, double dataProcessed) -{ - if (currentProcess == StatusHandler::PROCESS_SCANNING) - mainDialog->compareStatus->incScannedObjects_NoUpdate(objectsProcessed); - else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) - mainDialog->compareStatus->incProcessedCmpData_NoUpdate(objectsProcessed, dataProcessed); - else assert(false); -} - - -ErrorHandler::Response CompareStatusHandler::reportError(const Zstring& text) -{ - if (ignoreErrors) - return ErrorHandler::IGNORE_ERROR; - - mainDialog->compareStatus->updateStatusPanelNow(); - - bool ignoreNextErrors = false; - wxString errorMessage = wxString(text.c_str()) + wxT("\n\n") + _("Ignore this error, retry or abort?"); - ErrorDlg* errorDlg = new ErrorDlg(mainDialog, - ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, - errorMessage, ignoreNextErrors); - int rv = errorDlg->ShowModal(); - switch (rv) - { - case ErrorDlg::BUTTON_IGNORE: - ignoreErrors = ignoreNextErrors; - return ErrorHandler::IGNORE_ERROR; - - case ErrorDlg::BUTTON_RETRY: - return ErrorHandler::RETRY; - - case ErrorDlg::BUTTON_ABORT: - abortThisProcess(); - } - - assert(false); - return ErrorHandler::IGNORE_ERROR; //dummy return value -} - - -void CompareStatusHandler::reportFatalError(const Zstring& errorMessage) -{ - mainDialog->compareStatus->updateStatusPanelNow(); - - bool dummy = false; - ErrorDlg* errorDlg = new ErrorDlg(mainDialog, - ErrorDlg::BUTTON_ABORT, - errorMessage.c_str(), dummy); - errorDlg->ShowModal(); - abortThisProcess(); -} - - -void CompareStatusHandler::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) -{ - if (ignoreErrors) //if errors are ignored, then warnings should also - return; - - mainDialog->compareStatus->updateStatusPanelNow(); - - //show popup and ask user how to handle warning - bool dontWarnAgain = false; - WarningDlg* warningDlg = new WarningDlg(mainDialog, - WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, - warningMessage.c_str(), - dontWarnAgain); - switch (warningDlg->ShowModal()) - { - case WarningDlg::BUTTON_ABORT: - abortThisProcess(); - - case WarningDlg::BUTTON_IGNORE: - dontShowAgain = dontWarnAgain; - return; - } - - assert(false); -} - - -inline -void CompareStatusHandler::forceUiRefresh() -{ - mainDialog->compareStatus->updateStatusPanelNow(); -} - - -void CompareStatusHandler::abortThisProcess() -{ - abortRequested = true; - throw AbortThisProcess(); //abort can be triggered by syncStatusFrame -} -//######################################################################################################## - - -SyncStatusHandler::SyncStatusHandler(wxWindow* dlg, bool ignoreAllErrors) : - ignoreErrors(ignoreAllErrors) -{ - syncStatusFrame = new SyncStatus(this, dlg); - syncStatusFrame->Show(); - updateUiNow(); -} - - -SyncStatusHandler::~SyncStatusHandler() -{ - //print the results list - unsigned int failedItems = unhandledErrors.GetCount(); - wxString result; - if (failedItems) - { - result = wxString(_("Warning: Synchronization failed for %x item(s):")) + wxT("\n\n"); - result.Replace(wxT("%x"), globalFunctions::numberToWxString(failedItems), false); - - for (unsigned int j = 0; j < failedItems; ++j) - { //remove linebreaks - wxString errorMessage = unhandledErrors[j]; - for (wxString::iterator i = errorMessage.begin(); i != errorMessage.end(); ++i) - if (*i == wxChar('\n')) - *i = wxChar(' '); - - result += errorMessage + wxT("\n"); - } - result+= wxT("\n"); - } - - //notify to syncStatusFrame that current process has ended - if (abortRequested) - { - result+= wxString(_("Synchronization aborted!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); - syncStatusFrame->setStatusText_NoUpdate(result.c_str()); - syncStatusFrame->processHasFinished(SyncStatus::ABORTED); //enable okay and close events - } - else if (failedItems) - { - result+= wxString(_("Synchronization completed with errors!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); - syncStatusFrame->setStatusText_NoUpdate(result.c_str()); - syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_ERROR); - } - else - { - result+= _("Synchronization completed successfully!"); - syncStatusFrame->setStatusText_NoUpdate(result.c_str()); - syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS); - } -} - - -inline -void SyncStatusHandler::updateStatusText(const Zstring& text) -{ - syncStatusFrame->setStatusText_NoUpdate(text); -} - - -void SyncStatusHandler::initNewProcess(int objectsTotal, double dataTotal, Process processID) -{ - assert (processID == StatusHandler::PROCESS_SYNCHRONIZING); - - syncStatusFrame->resetGauge(objectsTotal, dataTotal); - syncStatusFrame->setCurrentStatus(SyncStatus::SYNCHRONIZING); -} - - -inline -void SyncStatusHandler::updateProcessedData(int objectsProcessed, double dataProcessed) -{ - syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); -} - - -ErrorHandler::Response SyncStatusHandler::reportError(const Zstring& text) -{ - //add current time before error message - wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + text.c_str(); - - if (ignoreErrors) - { - unhandledErrors.Add(errorWithTime); - return ErrorHandler::IGNORE_ERROR; - } - - syncStatusFrame->updateStatusDialogNow(); - - bool ignoreNextErrors = false; - ErrorDlg* errorDlg = new ErrorDlg(syncStatusFrame, - ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, - wxString(text) + wxT("\n\n") + _("Ignore this error, retry or abort synchronization?"), - ignoreNextErrors); - int rv = errorDlg->ShowModal(); - switch (rv) - { - case ErrorDlg::BUTTON_IGNORE: - ignoreErrors = ignoreNextErrors; - unhandledErrors.Add(errorWithTime); - return ErrorHandler::IGNORE_ERROR; - - case ErrorDlg::BUTTON_RETRY: - return ErrorHandler::RETRY; - - case ErrorDlg::BUTTON_ABORT: - unhandledErrors.Add(errorWithTime); - abortThisProcess(); - } - - assert (false); - unhandledErrors.Add(errorWithTime); - return ErrorHandler::IGNORE_ERROR; -} - - -void SyncStatusHandler::reportFatalError(const Zstring& errorMessage) -{ //add current time before error message - wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + errorMessage.c_str(); - - unhandledErrors.Add(errorWithTime); - abortThisProcess(); -} - - -void SyncStatusHandler::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) -{ //add current time before warning message - wxString warningWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + warningMessage.c_str(); - - if (ignoreErrors) //if errors are ignored, then warnings should also - return; //no unhandled error situation! - - syncStatusFrame->updateStatusDialogNow(); - - //show popup and ask user how to handle warning - bool dontWarnAgain = false; - WarningDlg* warningDlg = new WarningDlg(syncStatusFrame, - WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, - warningMessage.c_str(), - dontWarnAgain); - switch (warningDlg->ShowModal()) - { - case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! - dontShowAgain = dontWarnAgain; - return; - - case WarningDlg::BUTTON_ABORT: - unhandledErrors.Add(warningWithTime); - abortThisProcess(); - } - - assert(false); -} - - -void SyncStatusHandler::forceUiRefresh() -{ - syncStatusFrame->updateStatusDialogNow(); -} - - -void SyncStatusHandler::abortThisProcess() -{ - abortRequested = true; - throw AbortThisProcess(); //abort can be triggered by syncStatusFrame -} -//######################################################################################################## - //menu events void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event) @@ -3032,26 +2534,26 @@ void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event) void MainDialog::OnMenuExportFileList(wxCommandEvent& event) { //get a filename - wxString fileName = wxT("FileList.csv"); //proposal - wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, fileName, wxString(_("Comma separated list")) + wxT(" (*.csv)|*.csv"), wxFD_SAVE); + const wxString defaultFileName = wxT("FileList.csv"); //proposal + wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, defaultFileName, wxString(_("Comma separated list")) + wxT(" (*.csv)|*.csv"), wxFD_SAVE); if (filePicker->ShowModal() == wxID_OK) { - fileName = filePicker->GetPath(); - if (FreeFileSync::fileExists(fileName.c_str())) + const wxString newFileName = filePicker->GetPath(); + if (FreeFileSync::fileExists(newFileName.c_str())) { - wxMessageDialog* messageDlg = new wxMessageDialog(this, wxString(_("File already exists. Overwrite?")) + wxT(" \"") + fileName + wxT("\""), _("Warning") , wxOK | wxCANCEL); + wxMessageDialog* messageDlg = new wxMessageDialog(this, wxString(_("File already exists. Overwrite?")) + wxT(" \"") + newFileName + wxT("\""), _("Warning") , wxOK | wxCANCEL); if (messageDlg->ShowModal() != wxID_OK) { - pushStatusInformation(_("Save aborted!")); + OnMenuExportFileList(event); //retry return; } } //begin work wxString exportString; - for (unsigned int i = 0; i < gridRefUI.size(); ++i) + for (int i = 0; i < m_gridLeft->GetNumberRows(); ++i) { for (int k = 0; k < m_gridLeft->GetNumberCols(); ++k) { @@ -3075,7 +2577,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) } //write export file - wxFFile output(fileName.c_str(), wxT("w")); //don't write in binary mode + wxFFile output(newFileName.c_str(), wxT("w")); //don't write in binary mode if (output.IsOpened()) { output.Write(exportString); @@ -3083,7 +2585,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) } else { - wxMessageBox(wxString(_("Error writing file:")) + wxT(" \"") + fileName + wxT("\""), _("Error"), wxOK | wxICON_ERROR); + wxMessageBox(wxString(_("Error writing file:")) + wxT(" \"") + newFileName + wxT("\""), _("Error"), wxOK | wxICON_ERROR); } } } @@ -3110,7 +2612,16 @@ void MainDialog::OnMenuBatchJob(wxCommandEvent& event) void MainDialog::OnMenuCheckVersion(wxCommandEvent& event) { - FreeFileSync::checkForNewVersion(); + FreeFileSync::checkForUpdateNow(); +} + + +void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event) +{ + //execute just once per startup! + Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), NULL, this); + + FreeFileSync::checkForUpdatePeriodically(globalSettings.shared.lastUpdateCheck); } @@ -3123,7 +2634,7 @@ void MainDialog::OnMenuAbout(wxCommandEvent& event) void MainDialog::OnMenuQuit(wxCommandEvent& event) { - Destroy(); + requestShutdown(); } @@ -3138,7 +2649,7 @@ void MainDialog::switchProgramLanguage(const int langID) cleanUp(); //destructor's code: includes writing settings to HD //create new dialog with respect to new language - MainDialog* frame = new MainDialog(NULL, FreeFileSync::LAST_CONFIG_FILE, programLanguage, globalSettings); + MainDialog* frame = new MainDialog(NULL, xmlAccess::LAST_CONFIG_FILE, programLanguage, globalSettings); frame->SetIcon(*globalResource.programIcon); //set application icon frame->Show(); @@ -3206,6 +2717,12 @@ void MainDialog::OnMenuLangPortuguese(wxCommandEvent& event) } +void MainDialog::OnMenuLangPortugueseBrazil(wxCommandEvent& event) +{ + switchProgramLanguage(wxLANGUAGE_PORTUGUESE_BRAZILIAN); +} + + void MainDialog::OnMenuLangSlovenian(wxCommandEvent& event) { switchProgramLanguage(wxLANGUAGE_SLOVENIAN); @@ -3217,3 +2734,4 @@ void MainDialog::OnMenuLangSpanish(wxCommandEvent& event) switchProgramLanguage(wxLANGUAGE_SPANISH); } + diff --git a/ui/MainDialog.h b/ui/MainDialog.h index 6e60fab3..49d5ceee 100644 --- a/ui/MainDialog.h +++ b/ui/MainDialog.h @@ -1,6 +1,6 @@ /*************************************************************** * Name: mainDialog.h - * Purpose: Defines Application Frame + * Purpose: Main Application Dialog * Author: ZenJu (zhnmju123@gmx.de) * Created: 2008-07-16 **************************************************************/ @@ -9,48 +9,47 @@ #define MAINDIALOG_H #include "guiGenerated.h" -#include "syncDialog.h" -#include "smallDialogs.h" -#include "../library/resources.h" -#include "../library/misc.h" -#include <wx/dnd.h> #include <stack> #include "../library/processXml.h" -#include <wx/event.h> +#include "gridView.h" #include <memory> +class CompareStatusHandler; +class CompareStatus; +class CustomLocale; +class MainFolderDragDrop; +class FolderPairPanel; +class CustomGrid; -//IDs for context menu items -enum //context menu for left and right grids -{ - CONTEXT_FILTER_TEMP = 10, - CONTEXT_EXCLUDE_EXT, - CONTEXT_EXCLUDE_OBJ, - CONTEXT_CLIPBOARD, - CONTEXT_EXPLORER, - CONTEXT_DELETE_FILES, -}; -enum //context menu for middle grid +class MainDialog : public MainDialogGenerated { - CONTEXT_CHECK_ALL, - CONTEXT_UNCHECK_ALL -}; + friend class CompareStatusHandler; + friend class MainFolderDragDrop; -enum //context menu for column settings -{ - CONTEXT_CUSTOMIZE_COLUMN_LEFT, - CONTEXT_CUSTOMIZE_COLUMN_RIGHT -}; +//IDs for context menu items + enum //context menu for left and right grids + { + CONTEXT_FILTER_TEMP = 10, + CONTEXT_EXCLUDE_EXT, + CONTEXT_EXCLUDE_OBJ, + CONTEXT_CLIPBOARD, + CONTEXT_EXPLORER, + CONTEXT_DELETE_FILES, + }; -class CompareStatusHandler; -class FileDropEvent; -class FfsFileDropEvent; + enum //context menu for middle grid + { + CONTEXT_CHECK_ALL, + CONTEXT_UNCHECK_ALL + }; + + enum //context menu for column settings + { + CONTEXT_CUSTOMIZE_COLUMN_LEFT, + CONTEXT_CUSTOMIZE_COLUMN_RIGHT + }; -class MainDialog : public MainDialogGenerated -{ - friend class CompareStatusHandler; - friend class FileDropEvent; public: MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale* language, xmlAccess::XmlGlobalSettings& settings); @@ -62,6 +61,9 @@ private: //configuration load/save bool readConfigurationFromXml(const wxString& filename, bool programStartup = false); bool writeConfigurationToXml(const wxString& filename); + xmlAccess::XmlGuiConfig getCurrentConfiguration() const; + + xmlAccess::XmlGuiConfig lastConfigurationSaved; //support for: "Save changed configuration?" dialog void readGlobalSettings(); void writeGlobalSettings(); @@ -75,21 +77,21 @@ private: void addRightFolderToHistory(const wxString& rightFolder); void addFolderPair(const Zstring& leftDir, const Zstring& rightDir); - void addFolderPair(const std::vector<FolderPair>& newPairs); + void addFolderPair(const std::vector<FreeFileSync::FolderPair>& newPairs); void removeFolderPair(const int pos, bool refreshLayout = true); //keep it an int, allow negative values! void clearFolderPairs(); - //main method for putting gridData on UI: maps data respecting current view settings - void writeGrid(const FileCompareResult& gridData); - void mapGridDataToUI(GridView& output, const FileCompareResult& fileCmpResult); - void updateStatusInformation(const GridView& output); + //main method for putting gridDataView on UI: updates data respecting current view settings + void updateGuiGrid(); + + void updateGridViewData(); //context menu functions - std::set<int> getSelectedRows(const wxGrid* grid); + std::set<int> getSelectedRows(const CustomGrid* grid); void filterRangeManually(const std::set<int>& rowsToFilterOnUiTable); - void copySelectionToClipboard(const wxGrid* selectedGrid); - void openWithFileManager(int rowNumber, const wxGrid* grid); - void deleteFilesOnGrid(const std::set<int>& selectedRowsLeft, const std::set<int>& selectedRowsRight); + void copySelectionToClipboard(const CustomGrid* selectedGrid); + void openWithFileManager(const int rowNumber, const bool leftSide); + void deleteSelectedFiles(); //work to be done in idle time void OnIdleEvent(wxEvent& event); @@ -110,9 +112,10 @@ private: void OnContextColumnRight(wxGridEvent& event); void OnContextColumnSelection(wxCommandEvent& event); - void OnWriteDirManually(wxCommandEvent& event); void OnDirSelected(wxFileDirPickerEvent& event); + void requestShutdown(); //try to exit application + //manual filtering of rows: void OnGridSelectCell(wxGridEvent& event); void OnGrid3LeftMouseUp(wxEvent& event); @@ -121,8 +124,8 @@ private: void OnLeftGridDoubleClick( wxGridEvent& event); void OnRightGridDoubleClick(wxGridEvent& event); void OnSortLeftGrid( wxGridEvent& event); - void OnSortRightGrid( wxGridEvent& event); void OnSortMiddleGrid( wxGridEvent& event); + void OnSortRightGrid( wxGridEvent& event); void OnLeftOnlyFiles( wxCommandEvent& event); void OnLeftNewerFiles( wxCommandEvent& event); @@ -137,8 +140,8 @@ private: void loadConfiguration(const wxString& filename); void OnCfgHistoryKeyEvent( wxKeyEvent& event); void OnFolderHistoryKeyEvent(wxKeyEvent& event); + void OnRegularUpdateCheck( wxIdleEvent& event); - void OnFilesDropped( FfsFileDropEvent& event); void onResizeMainWindow( wxEvent& event); void OnAbortCompare( wxCommandEvent& event); void OnFilterButton( wxCommandEvent& event); @@ -175,6 +178,7 @@ private: void OnMenuLangJapanese( wxCommandEvent& event); void OnMenuLangPolish( wxCommandEvent& event); void OnMenuLangPortuguese( wxCommandEvent& event); + void OnMenuLangPortugueseBrazil(wxCommandEvent& event); void OnMenuLangSlovenian( wxCommandEvent& event); void OnMenuLangSpanish( wxCommandEvent& event); @@ -188,18 +192,18 @@ private: xmlAccess::XmlGlobalSettings& globalSettings; //technical representation of grid-data - FileCompareResult currentGridData; + FreeFileSync::FolderComparison currentGridData; //UI view of currentGridData - GridView gridRefUI; + FreeFileSync::GridView gridDataView; //------------------------------------- //functional configuration - MainConfiguration cfg; + FreeFileSync::MainConfiguration cfg; //folder pairs: //m_directoryLeft, m_directoryRight - std::vector<FolderPairGenerated*> additionalFolderPairs; //additional pairs to the standard pair + std::vector<FolderPairPanel*> additionalFolderPairs; //additional pairs to the standard pair //gui settings int widthNotMaximized; @@ -211,7 +215,7 @@ private: //------------------------------------- //convenience method to get all folder pairs (unformatted) - std::vector<FolderPair> getFolderPairs(); + std::vector<FreeFileSync::FolderPair> getFolderPairs() const; //UI View Filter settings bool leftOnlyFilesActive; @@ -248,7 +252,7 @@ private: struct FilterObject { wxString relativeName; - FileDescrLine::ObjectType type; + FreeFileSync::FileDescrLine::ObjectType type; }; std::vector<FilterObject> exFilterCandidateObj; @@ -261,98 +265,10 @@ private: //remember last sort executed (for determination of sort order) int lastSortColumn; const wxGrid* lastSortGrid; -}; - -//###################################################################################### - -//define new event type -const wxEventType FFS_DROP_FILE_EVENT = wxNewEventType(); -typedef void (wxEvtHandler::*FffsFileDropEventFunction)(FfsFileDropEvent&); -#define FfsFileDropEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FffsFileDropEventFunction, &func) - -class FfsFileDropEvent : public wxCommandEvent -{ -public: - FfsFileDropEvent(const wxString& nameDropped, const wxPanel* dropTarget) : - wxCommandEvent(FFS_DROP_FILE_EVENT), - m_nameDropped(nameDropped), - m_dropTarget(dropTarget) {} - - virtual wxEvent* Clone() const - { - return new FfsFileDropEvent(m_nameDropped, m_dropTarget); - } - - const wxString m_nameDropped; - const wxPanel* m_dropTarget; -}; - -class MainWindowDropTarget : public wxFileDropTarget -{ -public: - MainWindowDropTarget(MainDialog* dlg, const wxPanel* obj) : - mainDlg(dlg), - dropTarget(obj) {} - - virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames); - -private: - MainDialog* mainDlg; - const wxPanel* dropTarget; + //support for drag and drop + std::auto_ptr<MainFolderDragDrop> dragDropOnLeft; + std::auto_ptr<MainFolderDragDrop> dragDropOnRight; }; -//###################################################################################### - -//classes handling sync and compare error as well as status information - -class CompareStatusHandler : public StatusHandler -{ -public: - CompareStatusHandler(MainDialog* dlg); - ~CompareStatusHandler(); - - virtual void updateStatusText(const Zstring& text); - virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID); - virtual void updateProcessedData(int objectsProcessed, double dataProcessed); - virtual void forceUiRefresh(); - - virtual ErrorHandler::Response reportError(const Zstring& text); - virtual void reportFatalError(const Zstring& errorMessage); - virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); - -private: - virtual void abortThisProcess(); - - MainDialog* mainDialog; - bool ignoreErrors; - Process currentProcess; -}; - - -class SyncStatusHandler : public StatusHandler -{ -public: - SyncStatusHandler(wxWindow* dlg, bool ignoreAllErrors); - ~SyncStatusHandler(); - - virtual void updateStatusText(const Zstring& text); - virtual void initNewProcess(int objectsTotal, double dataTotal, Process processID); - virtual void updateProcessedData(int objectsProcessed, double dataProcessed); - virtual void forceUiRefresh(); - - virtual ErrorHandler::Response reportError(const Zstring& text); - virtual void reportFatalError(const Zstring& errorMessage); - virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); - -private: - virtual void abortThisProcess(); - - SyncStatus* syncStatusFrame; - bool ignoreErrors; - wxArrayString unhandledErrors; //list of non-resolved errors -}; - - #endif // MAINDIALOG_H diff --git a/ui/SmallDialogs.cpp b/ui/SmallDialogs.cpp index c32f8ce6..b553dff7 100644 --- a/ui/SmallDialogs.cpp +++ b/ui/SmallDialogs.cpp @@ -5,6 +5,7 @@ #include <wx/msgdlg.h> #include "../library/customGrid.h" #include "../library/customButton.h" +#include "../library/statistics.h" using namespace FreeFileSync; @@ -16,14 +17,26 @@ AboutDlg::AboutDlg(wxWindow* window) : AboutDlgGenerated(window) m_bitmap11->SetBitmap(*globalResource.bitmapLogo); m_bitmap13->SetBitmap(*globalResource.bitmapGPL); + //flag bitmaps + m_bitmapFrench ->SetBitmap(*globalResource.bitmapFrance); + m_bitmapJapanese ->SetBitmap(*globalResource.bitmapJapan); + m_bitmapDutch ->SetBitmap(*globalResource.bitmapHolland); + m_bitmapChineseSimple ->SetBitmap(*globalResource.bitmapChina); + m_bitmapPolish ->SetBitmap(*globalResource.bitmapPoland); + m_bitmapPortuguese ->SetBitmap(*globalResource.bitmapPortugal); + m_bitmapItalian ->SetBitmap(*globalResource.bitmapItaly); + m_bitmapSlovenian ->SetBitmap(*globalResource.bitmapSlovakia); + m_bitmapHungarian ->SetBitmap(*globalResource.bitmapHungary); + m_bitmapSpanish ->SetBitmap(*globalResource.bitmapSpain); + m_bitmapPortugueseBrazil->SetBitmap(*globalResource.bitmapBrazil); + //build information wxString build = wxString(wxT("(")) + _("Build:") + wxT(" ") + __TDATE__; #if wxUSE_UNICODE - build+= wxT(" - Unicode"); + build+= wxT(" - Unicode)"); #else - build+= wxT(" - ANSI"); + build+= wxT(" - ANSI)"); #endif //wxUSE_UNICODE - build+= + wxT(")"); m_build->SetLabel(build); m_animationControl1->SetAnimation(*globalResource.animationMoney); @@ -35,9 +48,6 @@ AboutDlg::AboutDlg(wxWindow* window) : AboutDlgGenerated(window) } -AboutDlg::~AboutDlg() {} - - void AboutDlg::OnClose(wxCloseEvent& event) { EndModal(0); @@ -124,8 +134,6 @@ FilterDlg::FilterDlg(wxWindow* window, wxString& filterIncl, wxString& filterExc Fit(); } -FilterDlg::~FilterDlg() {} - void FilterDlg::OnHelp(wxCommandEvent& event) { @@ -173,13 +181,13 @@ void FilterDlg::OnClose(wxCloseEvent& event) //######################################################################################## DeleteDialog::DeleteDialog(wxWindow* main, - const FileCompareResult& grid, - const std::set<int>& rowsOnLeft, - const std::set<int>& rowsOnRight, + const FreeFileSync::FolderComparison& folderCmp, + const FreeFileSync::FolderCompRef& rowsOnLeft, + const FreeFileSync::FolderCompRef& rowsOnRight, bool& deleteOnBothSides, bool& useRecycleBin) : DeleteDlgGenerated(main), - mainGrid(grid), + m_folderCmp(folderCmp), rowsToDeleteOnLeft(rowsOnLeft), rowsToDeleteOnRight(rowsOnRight), m_deleteOnBothSides(deleteOnBothSides), @@ -208,10 +216,24 @@ void DeleteDialog::updateTexts() } m_staticTextHeader->SetLabel(headerText); - wxString filesToDelete = FreeFileSync::deleteFromGridAndHDPreview(mainGrid, - rowsToDeleteOnLeft, - rowsToDeleteOnRight, + assert(m_folderCmp.size() == rowsToDeleteOnLeft.size()); + assert(m_folderCmp.size() == rowsToDeleteOnRight.size()); + + wxString filesToDelete; + for (FolderComparison::const_iterator j = m_folderCmp.begin(); j != m_folderCmp.end(); ++j) + { + const FileComparison& fileCmp = j->fileCmp; + + const int pairIndex = j - m_folderCmp.begin(); + if ( pairIndex < int(rowsToDeleteOnLeft.size()) && //just to be sure + pairIndex < int(rowsToDeleteOnRight.size())) + { + filesToDelete += FreeFileSync::deleteFromGridAndHDPreview(fileCmp, + rowsToDeleteOnLeft[pairIndex], + rowsToDeleteOnRight[pairIndex], m_checkBoxDeleteBothSides->GetValue()); + } + } m_textCtrlMessage->SetValue(filesToDelete); Layout(); @@ -262,6 +284,7 @@ ErrorDlg::ErrorDlg(wxWindow* parentWindow, const int activeButtons, const wxStri { m_bitmap10->SetBitmap(*globalResource.bitmapError); m_textCtrl8->SetValue(messageText); + m_checkBoxIgnoreErrors->SetValue(ignoreNextErrors); if (~activeButtons & BUTTON_IGNORE) { @@ -322,6 +345,7 @@ WarningDlg::WarningDlg(wxWindow* parentWindow, int activeButtons, const wxStrin { m_bitmap10->SetBitmap(*globalResource.bitmapWarning); m_textCtrl8->SetValue(messageText); + m_checkBoxDontShowAgain->SetValue(dontShowAgain); if (~activeButtons & BUTTON_IGNORE) { @@ -333,7 +357,7 @@ WarningDlg::WarningDlg(wxWindow* parentWindow, int activeButtons, const wxStrin m_buttonAbort->Hide(); //set button focus precedence - else if (activeButtons & BUTTON_IGNORE) + if (activeButtons & BUTTON_IGNORE) m_buttonIgnore->SetFocus(); else if (activeButtons & BUTTON_ABORT) m_buttonAbort->SetFocus(); @@ -363,6 +387,70 @@ void WarningDlg::OnAbort(wxCommandEvent& event) } //######################################################################################## + + +QuestionDlg::QuestionDlg(wxWindow* parentWindow, int activeButtons, const wxString messageText, bool& dontShowDlgAgain) : + QuestionDlgGenerated(parentWindow), + dontShowAgain(dontShowDlgAgain) +{ + m_bitmap10->SetBitmap(*globalResource.bitmapQuestion); + m_textCtrl8->SetValue(messageText); + m_checkBoxDontAskAgain->SetValue(dontShowAgain); + + if (~activeButtons & BUTTON_YES) + m_buttonYes->Hide(); + + if (~activeButtons & BUTTON_NO) + { + m_buttonNo->Hide(); + m_checkBoxDontAskAgain->Hide(); + } + + if (~activeButtons & BUTTON_CANCEL) + m_buttonCancel->Hide(); + + //set button focus precedence + if (activeButtons & BUTTON_YES) + m_buttonYes->SetFocus(); + else if (activeButtons & BUTTON_CANCEL) + m_buttonCancel->SetFocus(); + else if (activeButtons & BUTTON_NO) + m_buttonNo->SetFocus(); +} + + +QuestionDlg::~QuestionDlg() {} + + +void QuestionDlg::OnClose(wxCloseEvent& event) +{ + dontShowAgain = m_checkBoxDontAskAgain->GetValue(); + EndModal(BUTTON_CANCEL); +} + + +void QuestionDlg::OnCancel(wxCommandEvent& event) +{ + dontShowAgain = m_checkBoxDontAskAgain->GetValue(); + EndModal(BUTTON_CANCEL); +} + + +void QuestionDlg::OnYes(wxCommandEvent& event) +{ + dontShowAgain = m_checkBoxDontAskAgain->GetValue(); + EndModal(BUTTON_YES); +} + +void QuestionDlg::OnNo(wxCommandEvent& event) +{ + dontShowAgain = m_checkBoxDontAskAgain->GetValue(); + EndModal(BUTTON_NO); +} + +//######################################################################################## + + CustomizeColsDlg::CustomizeColsDlg(wxWindow* window, xmlAccess::ColumnAttributes& attr) : CustomizeColsDlgGenerated(window), output(attr) @@ -376,7 +464,7 @@ CustomizeColsDlg::CustomizeColsDlg(wxWindow* window, xmlAccess::ColumnAttributes for (xmlAccess::ColumnAttributes::const_iterator i = columnSettings.begin(); i != columnSettings.end(); ++i) //love these iterators! { - m_checkListColumns->Append(CustomGrid::getTypeName(i->type)); + m_checkListColumns->Append(CustomGridRim::getTypeName(i->type)); m_checkListColumns->Check(i - columnSettings.begin(), i->visible); } @@ -392,7 +480,7 @@ void CustomizeColsDlg::OnOkay(wxCommandEvent& event) const wxString label = m_checkListColumns->GetString(i); for (xmlAccess::ColumnAttributes::iterator j = output.begin(); j != output.end(); ++j) { - if (CustomGrid::getTypeName(j->type) == label) //not nice but short and no performance issue + if (CustomGridRim::getTypeName(j->type) == label) //not nice but short and no performance issue { j->position = i; j->visible = m_checkListColumns->IsChecked(i);; @@ -407,12 +495,12 @@ void CustomizeColsDlg::OnOkay(wxCommandEvent& event) void CustomizeColsDlg::OnDefault(wxCommandEvent& event) { - xmlAccess::ColumnAttributes defaultColumnAttr = CustomGrid::getDefaultColumnAttributes(); + xmlAccess::ColumnAttributes defaultColumnAttr = CustomGridRim::getDefaultColumnAttributes(); m_checkListColumns->Clear(); for (xmlAccess::ColumnAttributes::const_iterator i = defaultColumnAttr.begin(); i != defaultColumnAttr.end(); ++i) { - m_checkListColumns->Append(CustomGrid::getTypeName(i->type)); + m_checkListColumns->Append(CustomGridRim::getTypeName(i->type)); m_checkListColumns->Check(i - defaultColumnAttr.begin(), i->visible); } } @@ -521,96 +609,166 @@ void GlobalSettingsDlg::OnClose(wxCloseEvent& event) //######################################################################################## -/* -class for calculation of remaining time: ----------------------------------------- -"filesize |-> time" is an affine linear function f(x) = z_1 + z_2 x -For given n measurements, sizes x_0, ..., x_n and times f_0, ..., f_n, the function f (as a polynom of degree 1) can be lineary approximated by +CompareStatus::CompareStatus(wxWindow* parentWindow) : + CompareStatusGenerated(parentWindow), + scannedObjects(0), + totalObjects(0), + totalData(0), + currentObjects(0), + currentData(0), + scalingFactor(0), + statistics(NULL), + lastStatCallSpeed(-1000000), //some big number + lastStatCallRemTime(-1000000) +{ + init(); +} -z_1 = (r - s * q / p) / ((n + 1) - s * s / p) -z_2 = (q - s * z_1) / p = (r - (n + 1) z_1) / s -with -p := x_0^2 + ... + x_n^2 -q := f_0 x_0 + ... + f_n x_n -r := f_0 + ... + f_n -s := x_0 + ... + x_n +void CompareStatus::init() +{ + //initialize gauge + m_gauge2->SetRange(50000); + m_gauge2->SetValue(0); -=> the time to process N files with amount of data D is: N * z_1 + D * z_2 + //initially hide status that's relevant for comparing bytewise only + bSizer42->Hide(sbSizer13); + m_gauge2->Hide(); + bSizer42->Layout(); -Problem: --------- -Times f_0, ..., f_n can be very small so that precision of the PC clock is poor. -=> Times have to be accumulated to enhance precision: -Copying of m files with sizes x_i and times f_i (i = 1, ..., m) takes sum_i f(x_i) := m * z_1 + z_2 * sum x_i = sum f_i -With X defined as the accumulated sizes and F the accumulated times this gives: (in theory...) -m * z_1 + z_2 * X = F <=> -z_1 + z_2 * X / m = F / m + scannedObjects = 0; -=> we optain a new (artificial) measurement with size X / m and time F / m to be used in the linear approximation above + totalObjects = 0; + totalData = 0; + currentObjects = 0; + currentData = 0; + scalingFactor = 0; + statistics.reset(); + + timeElapsed.Start(); //measure total time + + updateStatusPanelNow(); +} -RemainingTime::RemainingTime() : n(0), m(0), X(0), F(0), p(0), q(0), r(0), s(0), z_1(0), z_2(0), lastExec(0) {} -RemainingTime::~RemainingTime() +void CompareStatus::switchToCompareBytewise(int totalObjectsToProcess, wxLongLong totalDataToProcess) { - ofstream output("test.txt"); - for (unsigned i = 0; i < x.size(); ++i) - { - output<<x[i]<<" "<<f[i]<<'\n'; - } - output<<'\n'<<z_1<<" "<<z_2<<'\n'; - output.close(); + currentData = 0; + totalData = totalDataToProcess; + + currentObjects = 0; + totalObjects = totalObjectsToProcess; + + if (totalData != 0) + scalingFactor = 50000 / totalData.ToDouble(); //let's normalize to 50000 + else + scalingFactor = 0; + + //set new statistics handler: 10 seconds "window" for remaining time, 5 seconds for speed + statistics.reset(new Statistics(totalObjectsToProcess, totalDataToProcess.ToDouble(), 10000, 5000)); + lastStatCallSpeed = -1000000; //some big number + lastStatCallRemTime = -1000000; + + //show status for comparing bytewise + bSizer42->Show(sbSizer13); + m_gauge2->Show(); + bSizer42->Layout(); } -wxLongLong RemainingTime::getRemainingTime(double processedDataSinceLastCall, int remainingFiles, double remainingData) //returns the remaining time in seconds +void CompareStatus::incScannedObjects_NoUpdate(int number) { - wxLongLong newExec = wxGetLocalTimeMillis(); + scannedObjects += number; +} - if (lastExec != 0) - { - X+= processedDataSinceLastCall; - F = (newExec - lastExec).ToDouble(); - ++m; - if (F > 1000) //add new measurement only if F is accumulated to a certain degree - { - lastExec = newExec; - ++n; +void CompareStatus::incProcessedCmpData_NoUpdate(int objectsProcessed, wxLongLong dataProcessed) +{ + currentData += dataProcessed; + currentObjects += objectsProcessed; +} + + +void CompareStatus::setStatusText_NoUpdate(const Zstring& text) +{ + currentStatusText = text; +} - double x_i = X / m; - double f_i = F / m; - X = 0; - F = 0; - m = 0; - x.push_back(x_i); - f.push_back(f_i); +void CompareStatus::updateStatusPanelNow() +{ + //static RetrieveStatistics statistic; + //statistic.writeEntry(currentData, currentObjects); + + bool screenChanged = false; //avoid screen flicker by calling layout() only if necessary + + //remove linebreaks from currentStatusText + wxString formattedStatusText = currentStatusText.c_str(); + for (wxString::iterator i = formattedStatusText.begin(); i != formattedStatusText.end(); ++i) + if (*i == wxChar('\n')) + *i = wxChar(' '); + + //status texts + if (m_textCtrlFilename->GetValue() != formattedStatusText && (screenChanged = true)) //avoid screen flicker + m_textCtrlFilename->SetValue(formattedStatusText); + + //nr of scanned objects + const wxString scannedObjTmp = globalFunctions::numberToWxString(scannedObjects); + if (m_staticTextScanned->GetLabel() != scannedObjTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextScanned->SetLabel(scannedObjTmp); - p+= x_i * x_i; - q+= f_i * x_i; - r+= f_i; - s+= x_i; + //progress indicator for "compare file content" + m_gauge2->SetValue(int(currentData.ToDouble() * scalingFactor)); + + //remaining files left for file comparison + const wxString filesToCompareTmp = globalFunctions::numberToWxString(totalObjects - currentObjects); + if (m_staticTextFilesRemaining->GetLabel() != filesToCompareTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextFilesRemaining->SetLabel(filesToCompareTmp); - if (p != 0) + //remaining bytes left for file comparison + const wxString remainingBytesTmp = FreeFileSync::formatFilesizeToShortString(totalData - currentData); + if (m_staticTextDataRemaining->GetLabel() != remainingBytesTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextDataRemaining->SetLabel(remainingBytesTmp); + + if (statistics.get()) + { + if (timeElapsed.Time() - lastStatCallSpeed >= 500) //call method every 500 ms + { + lastStatCallSpeed = timeElapsed.Time(); + + statistics->addMeasurement(currentObjects, currentData.ToDouble()); + + //current speed + const wxString speedTmp = statistics->getBytesPerSecond(); + if (m_staticTextSpeed->GetLabel() != speedTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextSpeed->SetLabel(speedTmp); + + if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //call method every two seconds only { - double tmp = (n - s * s / p); - if (tmp != 0 && s != 0) - { //recalculate coefficients for affine-linear function - z_1 = (r - s * q / p) / tmp; - z_2 = (r - n * z_1) / s; //not (n + 1) here, since n already is the number of measurements - } + lastStatCallRemTime = timeElapsed.Time(); + + //remaining time + const wxString timeRemainingTmp = statistics->getRemainingTime(); + if (m_staticTextTimeRemaining->GetLabel() != timeRemainingTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextTimeRemaining->SetLabel(timeRemainingTmp); } } - - return int(remainingFiles * z_1 + remainingData * z_2); } - //else - lastExec = newExec; - return 0; -}*/ + + //time elapsed + const wxString timeElapsedTmp = (wxTimeSpan::Milliseconds(timeElapsed.Time())).Format(); + if (m_staticTextTimeElapsed->GetLabel() != timeElapsedTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextTimeElapsed->SetLabel(timeElapsedTmp); + + //do the ui update + if (screenChanged) + bSizer42->Layout(); + + updateUiNow(); +} + //######################################################################################## @@ -619,13 +777,16 @@ SyncStatus::SyncStatus(StatusHandler* updater, wxWindow* parentWindow) : currentStatusHandler(updater), windowToDis(parentWindow), currentProcessIsRunning(true), + totalObjects(0), totalData(0), + currentObjects(0), currentData(0), scalingFactor(0), - currentObjects(0), - totalObjects(0), processPaused(false), - currentStatus(SyncStatus::ABORTED) + currentStatus(SyncStatus::ABORTED), + statistics(NULL), + lastStatCallSpeed(-1000000), //some big number + lastStatCallRemTime(-1000000) { m_animationControl1->SetAnimation(*globalResource.animationSync); m_animationControl1->Play(); @@ -654,7 +815,7 @@ SyncStatus::~SyncStatus() } -void SyncStatus::resetGauge(int totalObjectsToProcess, double totalDataToProcess) +void SyncStatus::resetGauge(int totalObjectsToProcess, wxLongLong totalDataToProcess) { currentData = 0; totalData = totalDataToProcess; @@ -663,16 +824,21 @@ void SyncStatus::resetGauge(int totalObjectsToProcess, double totalDataToProcess totalObjects = totalObjectsToProcess; if (totalData != 0) - scalingFactor = 50000 / totalData; //let's normalize to 50000 + scalingFactor = 50000 / totalData.ToDouble(); //let's normalize to 50000 else scalingFactor = 0; + + //set new statistics handler: 10 seconds "window" for remaining time, 5 seconds for speed + statistics.reset(new Statistics(totalObjectsToProcess, totalDataToProcess.ToDouble(), 10000, 5000)); + lastStatCallSpeed = -1000000; //some big number + lastStatCallRemTime = -1000000; } -void SyncStatus::incProgressIndicator_NoUpdate(int objectsProcessed, double dataProcessed) +void SyncStatus::incProgressIndicator_NoUpdate(int objectsProcessed, wxLongLong dataProcessed) { - currentData+= dataProcessed; - currentObjects+= objectsProcessed; + currentData += dataProcessed; + currentObjects += objectsProcessed; } @@ -684,10 +850,16 @@ void SyncStatus::setStatusText_NoUpdate(const Zstring& text) void SyncStatus::updateStatusDialogNow() { + + //static RetrieveStatistics statistic; + //statistic.writeEntry(currentData, currentObjects); + + + bool screenChanged = false; //avoid screen flicker by calling layout() only if necessary //progress indicator - m_gauge1->SetValue(globalFunctions::round(currentData * scalingFactor)); + m_gauge1->SetValue(globalFunctions::round(currentData.ToDouble() * scalingFactor)); //status text if (m_textCtrlInfo->GetValue() != wxString(currentStatusText.c_str()) && (screenChanged = true)) //avoid screen flicker @@ -703,11 +875,37 @@ void SyncStatus::updateStatusDialogNow() if (m_staticTextDataRemaining->GetLabel() != remainingBytesTmp && (screenChanged = true)) //avoid screen flicker m_staticTextDataRemaining->SetLabel(remainingBytesTmp); + if (statistics.get()) + { + if (timeElapsed.Time() - lastStatCallSpeed >= 500) //call method every 500 ms + { + lastStatCallSpeed = timeElapsed.Time(); + + statistics->addMeasurement(currentObjects, currentData.ToDouble()); + + //current speed + const wxString speedTmp = statistics->getBytesPerSecond(); + if (m_staticTextSpeed->GetLabel() != speedTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextSpeed->SetLabel(speedTmp); + + if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //call method every two seconds only + { + lastStatCallRemTime = timeElapsed.Time(); + + //remaining time + const wxString timeRemainingTmp = statistics->getRemainingTime(); + if (m_staticTextTimeRemaining->GetLabel() != timeRemainingTmp && (screenChanged = true)) //avoid screen flicker + m_staticTextTimeRemaining->SetLabel(timeRemainingTmp); + } + } + } + //time elapsed const wxString timeElapsedTmp = (wxTimeSpan::Milliseconds(timeElapsed.Time())).Format(); if (m_staticTextTimeElapsed->GetLabel() != timeElapsedTmp && (screenChanged = true)) //avoid screen flicker m_staticTextTimeElapsed->SetLabel(timeElapsedTmp); + //do the ui update if (screenChanged) { @@ -746,7 +944,7 @@ void SyncStatus::setCurrentStatus(SyncStatusID id) case PAUSE: m_bitmapStatus->SetBitmap(*globalResource.bitmapStatusPause); - m_staticTextStatus->SetLabel(_("Pause")); + m_staticTextStatus->SetLabel(_("Paused")); break; case SCANNING: @@ -772,7 +970,7 @@ void SyncStatus::setCurrentStatus(SyncStatusID id) void SyncStatus::processHasFinished(SyncStatusID id) //essential to call this in StatusHandler derived class destructor { //at the LATEST(!) to prevent access to currentStatusHandler - currentProcessIsRunning = false; //enable okay and close events; may be set ONLY in this method + currentProcessIsRunning = false; //enable okay and close events; may be set in this method ONLY setCurrentStatus(id); @@ -784,9 +982,11 @@ void SyncStatus::processHasFinished(SyncStatusID id) //essential to call this in m_buttonOK->SetFocus(); m_animationControl1->Stop(); - //m_animationControl1->SetInactiveBitmap(*globalResource.bitmapFinished); m_animationControl1->Hide(); + bSizerSpeed->Show(false); + bSizerRemTime->Show(false); + updateStatusDialogNow(); //keep this sequence to avoid display distortion, if e.g. only 1 item is sync'ed Layout(); // } @@ -808,7 +1008,11 @@ void SyncStatus::OnPause(wxCommandEvent& event) processPaused = false; m_buttonPause->SetLabel(_("Pause")); m_animationControl1->Play(); + + //resume timers timeElapsed.Resume(); + if (statistics.get()) + statistics->resumeTimer(); } else { @@ -818,7 +1022,11 @@ void SyncStatus::OnPause(wxCommandEvent& event) processPaused = true; m_buttonPause->SetLabel(_("Continue")); m_animationControl1->Stop(); + + //pause timers timeElapsed.Pause(); + if (statistics.get()) + statistics->pauseTimer(); } } @@ -849,143 +1057,3 @@ void SyncStatus::OnClose(wxCloseEvent& event) else Destroy(); } -//######################################################################################## - - -CompareStatus::CompareStatus(wxWindow* parentWindow) : - CompareStatusGenerated(parentWindow), - scannedObjects(0), - scalingFactorCmp(0), - totalCmpData(0), - processedCmpData(0), - totalCmpObjects(0), - processedCmpObjects(0) - /*timeRemaining(0), - timeRemainingTimeStamp(0)*/ -{ - init(); -} - - -CompareStatus::~CompareStatus() {} - - -void CompareStatus::init() -{ - //initialize gauge - m_gauge2->SetRange(50000); - m_gauge2->SetValue(0); - - //initially hide status that's relevant for comparing bytewise only - bSizer42->Hide(sbSizer13); - m_gauge2->Hide(); - bSizer42->Layout(); - - scannedObjects = 0; - scalingFactorCmp = 0; - - totalCmpData = 0; - processedCmpData = 0; - totalCmpObjects = 0; - processedCmpObjects = 0; - - timeElapsed.Start(); //measure total time - - updateStatusPanelNow(); -} - - -void CompareStatus::switchToCompareBytewise(int totalCmpObjectsToProcess, double totalCmpDataToProcess) -{ - processedCmpData = 0; - totalCmpData = totalCmpDataToProcess; - - processedCmpObjects = 0; - totalCmpObjects = totalCmpObjectsToProcess; - - if (totalCmpData != 0) - scalingFactorCmp = 50000 / totalCmpData; //let's normalize to 50000 - else - scalingFactorCmp = 0; - - //show status for comparing bytewise - bSizer42->Show(sbSizer13); - m_gauge2->Show(); - bSizer42->Layout(); -} - - -void CompareStatus::incScannedObjects_NoUpdate(int number) -{ - scannedObjects+= number; -} - - -void CompareStatus::incProcessedCmpData_NoUpdate(int objectsProcessed, double dataProcessed) -{ - processedCmpData+= dataProcessed; - processedCmpObjects+= objectsProcessed; - - /* timeRemaining = calcTimeLeft.getRemainingTime(dataProcessed, totalCmpObjects - processedCmpObjects, totalCmpData - processedCmpData); - timeRemainingTimeStamp = wxGetLocalTimeMillis();*/ -} - - -void CompareStatus::setStatusText_NoUpdate(const Zstring& text) -{ - currentStatusText = text; -} - - -void CompareStatus::updateStatusPanelNow() -{ - bool screenChanged = false; //avoid screen flicker by calling layout() only if necessary - - //remove linebreaks from currentStatusText - wxString formattedStatusText = currentStatusText.c_str(); - for (wxString::iterator i = formattedStatusText.begin(); i != formattedStatusText.end(); ++i) - if (*i == wxChar('\n')) - *i = wxChar(' '); - - //status texts - if (m_textCtrlFilename->GetValue() != formattedStatusText && (screenChanged = true)) //avoid screen flicker - m_textCtrlFilename->SetValue(formattedStatusText); - - //nr of scanned objects - const wxString scannedObjTmp = globalFunctions::numberToWxString(scannedObjects); - if (m_staticTextScanned->GetLabel() != scannedObjTmp && (screenChanged = true)) //avoid screen flicker - m_staticTextScanned->SetLabel(scannedObjTmp); - - //progress indicator for "compare file content" - m_gauge2->SetValue(int(processedCmpData * scalingFactorCmp)); - - //remaining files left for file comparison - const wxString filesToCompareTmp = globalFunctions::numberToWxString(totalCmpObjects - processedCmpObjects); - if (m_staticTextFilesToCompare->GetLabel() != filesToCompareTmp && (screenChanged = true)) //avoid screen flicker - m_staticTextFilesToCompare->SetLabel(filesToCompareTmp); - - //remaining bytes left for file comparison - const wxString remainingBytesTmp = FreeFileSync::formatFilesizeToShortString(totalCmpData - processedCmpData); - if (m_staticTextDataToCompare->GetLabel() != remainingBytesTmp && (screenChanged = true)) //avoid screen flicker - m_staticTextDataToCompare->SetLabel(remainingBytesTmp); - - /* - //remaining time in seconds - if (timeRemaining != 0) - { - int time = ((timeRemaining - (wxGetLocalTimeMillis() - timeRemainingTimeStamp)) / 1000).GetLo(); - m_staticTextRemainingTime->SetLabel(numberToWxString(time) + " s"); - } - */ - - //time elapsed - const wxString timeElapsedTmp = (wxTimeSpan::Milliseconds(timeElapsed.Time())).Format(); - if (m_staticTextTimeElapsed->GetLabel() != timeElapsedTmp && (screenChanged = true)) //avoid screen flicker - m_staticTextTimeElapsed->SetLabel(timeElapsedTmp); - - //do the ui update - if (screenChanged) - bSizer42->Layout(); - - updateUiNow(); -} diff --git a/ui/SmallDialogs.h b/ui/SmallDialogs.h index 0e310d94..a2d2c752 100644 --- a/ui/SmallDialogs.h +++ b/ui/SmallDialogs.h @@ -1,17 +1,21 @@ #ifndef SMALLDIALOGS_H_INCLUDED #define SMALLDIALOGS_H_INCLUDED -#include "../FreeFileSync.h" +#include "../structures.h" #include "../library/statusHandler.h" #include "../library/processXml.h" #include "guiGenerated.h" #include <wx/stopwatch.h> +#include <memory> + +class Statistics; + class AboutDlg : public AboutDlgGenerated { public: AboutDlg(wxWindow* window); - ~AboutDlg(); + ~AboutDlg() {} private: void OnClose(wxCloseEvent& event); @@ -35,7 +39,7 @@ class FilterDlg : public FilterDlgGenerated { public: FilterDlg(wxWindow* window, wxString& filterIncl, wxString& filterExcl); - ~FilterDlg(); + ~FilterDlg() {} enum { @@ -58,9 +62,9 @@ class DeleteDialog : public DeleteDlgGenerated { public: DeleteDialog(wxWindow* main, - const FileCompareResult& grid, - const std::set<int>& rowsOnLeft, - const std::set<int>& rowsOnRight, + const FreeFileSync::FolderComparison& folderCmp, + const FreeFileSync::FolderCompRef& rowsOnLeft, + const FreeFileSync::FolderCompRef& rowsOnRight, bool& deleteOnBothSides, bool& useRecycleBin); @@ -81,9 +85,9 @@ private: void updateTexts(); - const FileCompareResult& mainGrid; - const std::set<int>& rowsToDeleteOnLeft; - const std::set<int>& rowsToDeleteOnRight; + const FreeFileSync::FolderComparison& m_folderCmp; + const FreeFileSync::FolderCompRef& rowsToDeleteOnLeft; + const FreeFileSync::FolderCompRef& rowsToDeleteOnRight; bool& m_deleteOnBothSides; bool& m_useRecycleBin; }; @@ -135,6 +139,29 @@ private: }; +class QuestionDlg : public QuestionDlgGenerated +{ +public: + QuestionDlg(wxWindow* parentWindow, int activeButtons, const wxString messageText, bool& dontShowAgain); + ~QuestionDlg(); + + enum + { + BUTTON_YES = 1, + BUTTON_NO = 2, + BUTTON_CANCEL = 4 + }; + +private: + void OnClose(wxCloseEvent& event); + void OnCancel(wxCommandEvent& event); + void OnYes(wxCommandEvent& event); + void OnNo(wxCommandEvent& event); + + bool& dontShowAgain; +}; + + class CustomizeColsDlg : public CustomizeColsDlgGenerated { public: @@ -181,10 +208,44 @@ private: }; +class CompareStatus : public CompareStatusGenerated +{ +public: + CompareStatus(wxWindow* parentWindow); + + void init(); //initialize all status values + + void switchToCompareBytewise(int totalObjectsToProcess, wxLongLong totalDataToProcess); + void incScannedObjects_NoUpdate(int number); + void incProcessedCmpData_NoUpdate(int objectsProcessed, wxLongLong dataProcessed); + void setStatusText_NoUpdate(const Zstring& text); + void updateStatusPanelNow(); + +private: + //status variables + unsigned int scannedObjects; + Zstring currentStatusText; + + wxStopWatch timeElapsed; + + //gauge variables + int totalObjects; + wxLongLong totalData; //each data element represents one byte for proper progress indicator scaling + int currentObjects; //each object represents a file or directory processed + wxLongLong currentData; + double scalingFactor; //nr of elements has to be normalized to smaller nr. because of range of int limitation + + //remaining time + std::auto_ptr<Statistics> statistics; + long lastStatCallSpeed; //used for calculating intervals between statistics update + long lastStatCallRemTime; // +}; + + class SyncStatus : public SyncStatusDlgGenerated { public: - SyncStatus(StatusHandler* updater, wxWindow* parentWindow = NULL); + SyncStatus(StatusHandler* updater, wxWindow* parentWindow); ~SyncStatus(); enum SyncStatusID @@ -198,8 +259,8 @@ public: SYNCHRONIZING }; - void resetGauge(int totalObjectsToProcess, double totalDataToProcess); - void incProgressIndicator_NoUpdate(int objectsProcessed, double dataProcessed); + void resetGauge(int totalObjectsToProcess, wxLongLong totalDataToProcess); + void incProgressIndicator_NoUpdate(int objectsProcessed, wxLongLong dataProcessed); void setStatusText_NoUpdate(const Zstring& text); void updateStatusDialogNow(); @@ -219,77 +280,20 @@ private: bool currentProcessIsRunning; //gauge variables - double totalData; //each data element represents one byte for proper progress indicator scaling - double currentData; - double scalingFactor; //nr of elements has to be normalized to smaller nr. because of range of int limitation - int currentObjects; //each object represents a file or directory processed - int totalObjects; + int totalObjects; + wxLongLong totalData; + int currentObjects; //each object represents a file or directory processed + wxLongLong currentData; //each data element represents one byte for proper progress indicator scaling + double scalingFactor; //nr of elements has to be normalized to smaller nr. because of range of int limitation Zstring currentStatusText; bool processPaused; SyncStatusID currentStatus; -}; -/* -class RemainingTime -{ -public: - RemainingTime(); - ~RemainingTime(); - wxLongLong getRemainingTime(double processedDataSinceLastCall, int remainingFiles, double remainingData); //returns the remaining time in milliseconds - -private: - double n; - double m; - double X; - double F; - double p; - double q; - double r; - double s; - double z_1; - double z_2; - wxLongLong lastExec; - - vector<double> x; //dummy: DELETE asap! - vector<double> f; + //remaining time + std::auto_ptr<Statistics> statistics; + long lastStatCallSpeed; //used for calculating intervals between statistics update + long lastStatCallRemTime; // }; -*/ - -class CompareStatus : public CompareStatusGenerated -{ -public: - CompareStatus(wxWindow* parentWindow); - ~CompareStatus(); - - void init(); //initialize all status values - - void switchToCompareBytewise(int totalCmpObjectsToProcess, double totalCmpDataToProcess); - void incScannedObjects_NoUpdate(int number); - void incProcessedCmpData_NoUpdate(int objectsProcessed, double dataProcessed); - void setStatusText_NoUpdate(const Zstring& text); - void updateStatusPanelNow(); - -private: - //status variables - unsigned int scannedObjects; - Zstring currentStatusText; - - wxStopWatch timeElapsed; - - //gauge variables - double scalingFactorCmp; //nr of elements has to be normalized to smaller nr. because of range of int limitation - double totalCmpData; //each data element represents one byte for proper progress indicator scaling - double processedCmpData; - int totalCmpObjects; - int processedCmpObjects; //each object represents a file or directory processed - /* - //remaining time - RemainingTime calcTimeLeft; - wxLongLong timeRemaining; //time in milliseconds - wxLongLong timeRemainingTimeStamp; //time in milliseconds - */ -}; - #endif // SMALLDIALOGS_H_INCLUDED diff --git a/ui/SyncDialog.cpp b/ui/SyncDialog.cpp index fe36d751..3c684700 100644 --- a/ui/SyncDialog.cpp +++ b/ui/SyncDialog.cpp @@ -8,15 +8,18 @@ #include "../synchronization.h" #include "../algorithm.h" #include <wx/dnd.h> +#include "dragAndDrop.h" + +using namespace FreeFileSync; SyncDialog::SyncDialog(wxWindow* window, - const FileCompareResult& gridDataRef, + const FolderComparison& folderCmpRef, MainConfiguration& config, bool& ignoreErrors, bool synchronizationEnabled) : SyncDlgGenerated(window), - gridData(gridDataRef), + folderCmp(folderCmpRef), cfg(config), m_ignoreErrors(ignoreErrors) { @@ -180,16 +183,15 @@ void SyncDialog::updateConfigIcons(wxBitmapButton* button1, void SyncDialog::adjustToolTips(wxStaticBitmap* bitmap, const CompareVariant var) { //set tooltip for ambivalent category "different" - if (var == CMP_BY_TIME_SIZE) + switch (var) { + case CMP_BY_TIME_SIZE: bitmap->SetToolTip(_("Files that exist on both sides, have same date but different filesizes")); - } - else if (var == CMP_BY_CONTENT) - { + break; + case CMP_BY_CONTENT: bitmap->SetToolTip(_("Files that exist on both sides and have different content")); + break; } - else - assert(false); } @@ -199,18 +201,18 @@ void SyncDialog::calculatePreview() int objectsToCreate = 0; int objectsToOverwrite = 0; int objectsToDelete = 0; - double dataToProcess = 0; - FreeFileSync::calcTotalBytesToSync(gridData, + wxULongLong dataToProcess; + FreeFileSync::calcTotalBytesToSync(folderCmp, localSyncConfiguration, objectsToCreate, objectsToOverwrite, objectsToDelete, dataToProcess); - wxString toCreate = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToCreate)); - wxString toUpdate = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToOverwrite)); - wxString toDelete = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToDelete)); - wxString data = FreeFileSync::formatFilesizeToShortString(dataToProcess); + const wxString toCreate = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToCreate)); + const wxString toUpdate = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToOverwrite)); + const wxString toDelete = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToDelete)); + const wxString data = FreeFileSync::formatFilesizeToShortString(dataToProcess); m_textCtrlCreate->SetValue(toCreate); m_textCtrlUpdate->SetValue(toUpdate); @@ -377,6 +379,23 @@ void SyncDialog::OnDifferent( wxCommandEvent& event ) //################################################################################################################################### +class BatchFolderPairPanel : public BatchFolderPairGenerated +{ +public: + BatchFolderPairPanel(wxWindow* parent) : + BatchFolderPairGenerated(parent), + dragDropOnLeft(m_panelLeft, m_dirPickerLeft, m_directoryLeft), + dragDropOnRight(m_panelRight, m_dirPickerRight, m_directoryRight) {} + +private: + //support for drag and drop + DragDropOnDlg dragDropOnLeft; + DragDropOnDlg dragDropOnRight; +}; + +//################################################################################################################################### + + class BatchFileDropEvent : public wxFileDropTarget { public: @@ -394,6 +413,12 @@ public: //test if ffs batch file has been dropped if (fileType == xmlAccess::XML_BATCH_CONFIG) batchDlg->loadBatchFile(droppedFileName); + else + { + wxString errorMessage = _("%x is not a valid FreeFileSync batch file!"); + errorMessage.Replace(wxT("%x"), wxString(wxT("\"")) + droppedFileName + wxT("\""), false); + wxMessageBox(errorMessage, _("Error"), wxOK | wxICON_ERROR); + } } return false; } @@ -421,9 +446,11 @@ BatchDialog::BatchDialog(wxWindow* window, const wxString& filename) : void BatchDialog::init() { - //prepare drag & drop + //prepare drag & drop for loading of *.ffs_batch files SetDropTarget(new BatchFileDropEvent(this)); + dragDropOnLogfileDir.reset(new DragDropOnDlg(m_panelLogging, m_dirPickerLogfileDir, m_textCtrlLogfileDir)); + //set icons for this dialog m_bitmap13->SetBitmap(*globalResource.bitmapLeftOnly); m_bitmap14->SetBitmap(*globalResource.bitmapRightOnly); @@ -436,27 +463,6 @@ void BatchDialog::init() } -void BatchDialog::updateFilterButton() -{ - if (filterIsActive) - { - m_bpButtonFilter->SetBitmapLabel(*globalResource.bitmapFilterOn); - m_bpButtonFilter->SetToolTip(_("Filter active: Press again to deactivate")); - - m_textCtrlInclude->Enable(); - m_textCtrlExclude->Enable(); - } - else - { - m_bpButtonFilter->SetBitmapLabel(*globalResource.bitmapFilterOff); - m_bpButtonFilter->SetToolTip(_("Press button to activate filter")); - - m_textCtrlInclude->Disable(); - m_textCtrlExclude->Disable(); - } -} - - xmlAccess::OnError BatchDialog::getSelectionHandleError() { switch (m_choiceHandleError->GetSelection()) @@ -562,10 +568,52 @@ void BatchDialog::OnDifferent(wxCommandEvent& event) } -void BatchDialog::OnFilterButton(wxCommandEvent& event) +void BatchDialog::OnCheckFilter(wxCommandEvent& event) +{ + updateVisibleTabs(); +} + + +void BatchDialog::OnCheckLogging(wxCommandEvent& event) +{ + updateVisibleTabs(); +} + + +void BatchDialog::updateVisibleTabs() { - filterIsActive = !filterIsActive; - updateFilterButton(); + showNotebookpage(m_panelFilter, _("Filter"), m_checkBoxFilter->GetValue()); + showNotebookpage(m_panelLogging, _("Logging"), m_checkBoxSilent->GetValue()); +} + + +void BatchDialog::showNotebookpage(wxWindow* page, const wxString& pageName, bool show) +{ + int windowPosition = -1; + for (size_t i = 0; i < m_notebookSettings->GetPageCount(); ++i) + if ( static_cast<wxWindow*>(m_notebookSettings->GetPage(i)) == + static_cast<wxWindow*>(page)) + { + windowPosition = i; + break; + } + + if (show) + { + if (windowPosition == -1) + m_notebookSettings->AddPage(page, pageName, false); + } + else + { + if (windowPosition != -1) + { + //do not delete currently selected tab!! + if (m_notebookSettings->GetCurrentPage() == m_notebookSettings->GetPage(windowPosition)) + m_notebookSettings->ChangeSelection(0); + + m_notebookSettings->RemovePage(windowPosition); + } + } } @@ -615,26 +663,25 @@ void BatchDialog::OnCancel(wxCommandEvent& event) void BatchDialog::OnSaveBatchJob(wxCommandEvent& event) { //get a filename - wxString fileName = wxT("SyncJob.ffs_batch"); //proposal - - if (!proposedBatchFileName.empty()) - fileName = proposedBatchFileName; - - wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, fileName, wxString(_("FreeFileSync batch file")) + wxT(" (*.ffs_batch)|*.ffs_batch"), wxFD_SAVE); + const wxString defaultFileName = proposedBatchFileName.empty() ? wxT("SyncJob.ffs_batch") : proposedBatchFileName; + wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, defaultFileName, wxString(_("FreeFileSync batch file")) + wxT(" (*.ffs_batch)|*.ffs_batch"), wxFD_SAVE); if (filePicker->ShowModal() == wxID_OK) { - fileName = filePicker->GetPath(); - if (FreeFileSync::fileExists(fileName.c_str())) + const wxString newFileName = filePicker->GetPath(); + if (FreeFileSync::fileExists(newFileName.c_str())) { - wxMessageDialog* messageDlg = new wxMessageDialog(this, wxString(_("File already exists. Overwrite?")) + wxT(" \"") + fileName + wxT("\""), _("Warning") , wxOK | wxCANCEL); + wxMessageDialog* messageDlg = new wxMessageDialog(this, wxString(_("File already exists. Overwrite?")) + wxT(" \"") + newFileName + wxT("\""), _("Warning") , wxOK | wxCANCEL); if (messageDlg->ShowModal() != wxID_OK) + { + OnSaveBatchJob(event); //retry return; + } } //create batch file - if (saveBatchFile(fileName)) + if (saveBatchFile(newFileName)) EndModal(BATCH_FILE_SAVED); } } @@ -661,7 +708,7 @@ bool BatchDialog::saveBatchFile(const wxString& filename) return false; batchCfg.mainCfg.syncConfiguration = localSyncConfiguration; - batchCfg.mainCfg.filterIsActive = filterIsActive; + batchCfg.mainCfg.filterIsActive = m_checkBoxFilter->GetValue(); batchCfg.mainCfg.includeFilter = m_textCtrlInclude->GetValue(); batchCfg.mainCfg.excludeFilter = m_textCtrlExclude->GetValue(); batchCfg.mainCfg.useRecycleBin = m_checkBoxUseRecycler->GetValue(); @@ -678,6 +725,7 @@ bool BatchDialog::saveBatchFile(const wxString& filename) //load structure with batch settings "batchCfg" batchCfg.silent = m_checkBoxSilent->GetValue(); + batchCfg.logFileDirectory = m_textCtrlLogfileDir->GetValue(); //write config to XML try @@ -741,13 +789,12 @@ void BatchDialog::loadBatchCfg(const xmlAccess::XmlBatchConfig& batchCfg) //adjust toolTip SyncDialog::adjustToolTips(m_bitmap17, batchCfg.mainCfg.compareVar); - filterIsActive = batchCfg.mainCfg.filterIsActive; - updateFilterButton(); - + m_checkBoxFilter->SetValue(batchCfg.mainCfg.filterIsActive); m_textCtrlInclude->SetValue(batchCfg.mainCfg.includeFilter); m_textCtrlExclude->SetValue(batchCfg.mainCfg.excludeFilter); m_checkBoxSilent->SetValue(batchCfg.silent); + m_textCtrlLogfileDir->SetValue(batchCfg.logFileDirectory); //remove existing folder pairs localFolderPairs.clear(); @@ -757,7 +804,7 @@ void BatchDialog::loadBatchCfg(const xmlAccess::XmlBatchConfig& batchCfg) int scrWindowHeight = 0; for (std::vector<FolderPair>::const_iterator i = batchCfg.directoryPairs.begin(); i != batchCfg.directoryPairs.end(); ++i) { - BatchFolderPairGenerated* newPair = new BatchFolderPairGenerated(m_scrolledWindow6); + BatchFolderPairPanel* newPair = new BatchFolderPairPanel(m_scrolledWindow6); newPair->m_directoryLeft->SetValue(i->leftDirectory.c_str()); newPair->m_directoryRight->SetValue(i->rightDirectory.c_str()); @@ -771,10 +818,12 @@ void BatchDialog::loadBatchCfg(const xmlAccess::XmlBatchConfig& batchCfg) int pairCount = std::min(localFolderPairs.size(), size_t(3)); //up to 3 additional pairs shall be shown m_scrolledWindow6->SetMinSize(wxSize( -1, scrWindowHeight * pairCount)); - m_scrolledWindow6->Layout(); - //m_scrolledWindow6->Fit(); + updateVisibleTabs(); + + m_scrolledWindow6->Layout(); //needed + m_panelOverview->Layout(); //needed - Fit(); + Fit(); //needed Centre(); m_buttonSave->SetFocus(); } diff --git a/ui/SyncDialog.h b/ui/SyncDialog.h index 05a2d65b..2c705122 100644 --- a/ui/SyncDialog.h +++ b/ui/SyncDialog.h @@ -1,19 +1,26 @@ #ifndef SYNCDIALOG_H_INCLUDED #define SYNCDIALOG_H_INCLUDED -#include "../FreeFileSync.h" +#include "../structures.h" #include "guiGenerated.h" #include "../library/processXml.h" +#include <memory> -using namespace FreeFileSync; +class BatchFileDropEvent; +class BatchFolderPairPanel; + +namespace FreeFileSync +{ + class DragDropOnDlg; +} class SyncDialog: public SyncDlgGenerated { public: SyncDialog(wxWindow* window, - const FileCompareResult& gridDataRef, - MainConfiguration& config, + const FreeFileSync::FolderComparison& folderCmpRef, + FreeFileSync::MainConfiguration& config, bool& ignoreErrors, bool synchronizationEnabled); @@ -29,9 +36,9 @@ public: wxBitmapButton* button3, wxBitmapButton* button4, wxBitmapButton* button5, - const SyncConfiguration& syncConfig); + const FreeFileSync::SyncConfiguration& syncConfig); - static void adjustToolTips(wxStaticBitmap* bitmap, const CompareVariant var); + static void adjustToolTips(wxStaticBitmap* bitmap, const FreeFileSync::CompareVariant var); private: void calculatePreview(); @@ -54,16 +61,13 @@ private: void OnSelectRecycleBin(wxCommandEvent& event); //temporal copy of maindialog.cfg.syncConfiguration - SyncConfiguration localSyncConfiguration; - const FileCompareResult& gridData; - MainConfiguration& cfg; + FreeFileSync::SyncConfiguration localSyncConfiguration; + const FreeFileSync::FolderComparison& folderCmp; + FreeFileSync::MainConfiguration& cfg; bool& m_ignoreErrors; }; -class BatchFileDropEvent; - - class BatchDialog: public BatchDlgGenerated { friend class BatchFileDropEvent; @@ -89,16 +93,19 @@ private: void OnRightNewer( wxCommandEvent& event); void OnDifferent( wxCommandEvent& event); - void OnFilterButton( wxCommandEvent& event); + void OnCheckFilter( wxCommandEvent& event); + void OnCheckLogging( wxCommandEvent& event); void OnSelectRecycleBin(wxCommandEvent& event); void OnChangeCompareVar(wxCommandEvent& event); + void updateVisibleTabs(); + void showNotebookpage(wxWindow* page, const wxString& pageName, bool show); + void OnClose( wxCloseEvent& event); void OnCancel( wxCommandEvent& event); void OnSaveBatchJob( wxCommandEvent& event); void OnLoadBatchJob( wxCommandEvent& event); - void updateFilterButton(); xmlAccess::OnError getSelectionHandleError(); void setSelectionHandleError(const xmlAccess::OnError value); @@ -106,13 +113,14 @@ private: void loadBatchFile(const wxString& filename); void loadBatchCfg(const xmlAccess::XmlBatchConfig& batchCfg); - SyncConfiguration localSyncConfiguration; - std::vector<BatchFolderPairGenerated*> localFolderPairs; - - bool filterIsActive; + FreeFileSync::SyncConfiguration localSyncConfiguration; + std::vector<BatchFolderPairPanel*> localFolderPairs; //used when saving batch file wxString proposedBatchFileName; + + //add drag & drop support when selecting logfile directory + std::auto_ptr<FreeFileSync::DragDropOnDlg> dragDropOnLogfileDir; }; #endif // SYNCDIALOG_H_INCLUDED diff --git a/ui/batchStatusHandler.cpp b/ui/batchStatusHandler.cpp new file mode 100644 index 00000000..462b1921 --- /dev/null +++ b/ui/batchStatusHandler.cpp @@ -0,0 +1,596 @@ +#include "batchStatusHandler.h" +#include "smallDialogs.h" +#include <wx/stopwatch.h> +#include <wx/taskbar.h> +#include "../algorithm.h" +#include <wx/ffile.h> +#include <wx/msgdlg.h> +#include "../library/globalFunctions.h" + + +class LogFile +{ +public: + LogFile(const wxString& logfileDirectory) + { + //create logfile directory + const Zstring logfileDir = logfileDirectory.empty() ? wxT("Logs") : logfileDirectory.c_str(); + if (!wxDirExists(logfileDir)) + try + { + FreeFileSync::createDirectory(logfileDir, Zstring(), false); + } + catch (FileError&) + { + readyToWrite = false; + return; + } + + //assemble logfile name + wxString logfileName = logfileDir.c_str(); + if (!FreeFileSync::endsWithPathSeparator(logfileName.c_str())) + logfileName += GlobalResources::FILE_NAME_SEPARATOR; + wxString timeNow = wxDateTime::Now().FormatISOTime(); + timeNow.Replace(wxT(":"), wxEmptyString); + logfileName += wxT("FFS_") + wxDateTime::Now().FormatISODate() + wxChar('_') + timeNow + wxT(".log"); + + + logFile.Open(logfileName.c_str(), wxT("w")); + readyToWrite = logFile.IsOpened(); + if (readyToWrite) + { + wxString headerLine = wxString(wxT("FreeFileSync - ")) + + _("Batch execution") + wxT(" (") + + _("Date") + wxT(": ") + wxDateTime::Now().FormatDate() + wxT(" ") + //"Date" is used at other places too + _("Time") + wxT(":") + wxT(" ") + wxDateTime::Now().FormatTime() + wxT(")"); + logFile.Write(headerLine + wxChar('\n')); + logFile.Write(wxString().Pad(headerLine.Len(), wxChar('-')) + wxChar('\n') + wxChar('\n')); + + wxString caption = _("Log-messages:"); + logFile.Write(caption + wxChar('\n')); + logFile.Write(wxString().Pad(caption.Len(), wxChar('-')) + wxChar('\n')); + + write(_("Start")); //attention: write() replaces '\n'-characters + logFile.Write(wxChar('\n')); // + + totalTime.Start(); //measure total time + } + } + + ~LogFile() + { + if (readyToWrite) + close(); + } + + bool isOkay() + { + return readyToWrite; + } + + void write(const wxString& logText, const wxString& problemType = wxEmptyString) + { + logFile.Write(wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ")); + + if (problemType != wxEmptyString) + logFile.Write(problemType + wxT(": ")); + + //remove linebreaks + wxString formattedText = logText; + for (wxString::iterator i = formattedText.begin(); i != formattedText.end(); ++i) + if (*i == wxChar('\n')) + *i = wxChar(' '); + + logFile.Write(formattedText + wxChar('\n')); + } + +private: + + void close() + { + logFile.Write(wxChar('\n')); + + long time = totalTime.Time(); //retrieve total time + + logFile.Write(wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ")); + logFile.Write(wxString(_("Stop")) + wxT(" (") + _("Total time:") + wxT(" ") + (wxTimeSpan::Milliseconds(time)).Format() + wxT(")")); + + //logFile.close(); <- not needed + } + + bool readyToWrite; + wxFFile logFile; + wxStopWatch totalTime; +}; + + +class FfsTrayIcon : public wxTaskBarIcon +{ +public: + FfsTrayIcon(StatusHandler* statusHandler) : + m_statusHandler(statusHandler), + processPaused(false) + { + running.reset(new wxIcon(*globalResource.programIcon)); + paused.reset(new wxIcon); + paused->CopyFromBitmap(*globalResource.bitmapFFSPaused); + + wxTaskBarIcon::SetIcon(*running); + } + + ~FfsTrayIcon() {} + + enum + { + CONTEXT_PAUSE, + CONTEXT_ABORT, + CONTEXT_ABOUT + }; + + virtual wxMenu* CreatePopupMenu() + { + wxMenu* contextMenu = new wxMenu; + contextMenu->Append(CONTEXT_PAUSE, _("&Pause"), wxEmptyString, wxITEM_CHECK); + contextMenu->Check(CONTEXT_PAUSE, processPaused); + contextMenu->Append(CONTEXT_ABORT, _("&Abort")); + contextMenu->AppendSeparator(); + contextMenu->Append(CONTEXT_ABOUT, _("&About...")); + //event handling + contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FfsTrayIcon::onContextMenuSelection), NULL, this); + + return contextMenu; //ownership transferred to library + } + + void onContextMenuSelection(wxCommandEvent& event) + { + int eventId = event.GetId(); + if (eventId == CONTEXT_PAUSE) + { + processPaused = !processPaused; + if (processPaused) + wxTaskBarIcon::SetIcon(*paused); + else + wxTaskBarIcon::SetIcon(*running); + } + else if (eventId == CONTEXT_ABORT) + { + processPaused = false; + wxTaskBarIcon::SetIcon(*running); + m_statusHandler->requestAbortion(); + } + else if (eventId == CONTEXT_ABOUT) + { + AboutDlg* aboutDlg = new AboutDlg(NULL); + aboutDlg->ShowModal(); + aboutDlg->Destroy(); + } + } + + void updateSysTray() + { + updateUiNow(); + + //support for pause button + while (processPaused) + { + wxMilliSleep(UI_UPDATE_INTERVAL); + updateUiNow(); + } + } + +private: + StatusHandler* m_statusHandler; + bool processPaused; + std::auto_ptr<wxIcon> running; + std::auto_ptr<wxIcon> paused; +}; + + +//############################################################################################################################## + +BatchStatusHandlerSilent::BatchStatusHandlerSilent(const xmlAccess::OnError handleError, const wxString& logfileDirectory, int& returnVal) : + m_handleError(handleError), + currentProcess(StatusHandler::PROCESS_NONE), + returnValue(returnVal), + trayIcon(new FfsTrayIcon(this)), + m_log(new LogFile(logfileDirectory)) +{ + //test if log was instantiated successfully + if (!m_log->isOkay()) + { //handle error: file load + wxMessageBox(_("Unable to create logfile!"), _("Error"), wxOK | wxICON_ERROR); + throw FreeFileSync::AbortThisProcess(); + } +} + + +BatchStatusHandlerSilent::~BatchStatusHandlerSilent() +{ + unsigned int failedItems = unhandledErrors.GetCount(); + + //write result + if (abortRequested) + { + returnValue = -4; + m_log->write(_("Synchronization aborted!"), _("Error")); + } + else if (failedItems) + { + returnValue = -5; + m_log->write(_("Synchronization completed with errors!"), _("Info")); + } + else + m_log->write(_("Synchronization completed successfully!"), _("Info")); +} + + +inline +void BatchStatusHandlerSilent::updateStatusText(const Zstring& text) +{ + if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING) + m_log->write(text.c_str(), _("Info")); +} + + +inline +void BatchStatusHandlerSilent::initNewProcess(int objectsTotal, wxLongLong dataTotal, StatusHandler::Process processID) +{ + currentProcess = processID; +} + + +ErrorHandler::Response BatchStatusHandlerSilent::reportError(const Zstring& errorMessage) +{ + switch (m_handleError) + { + case xmlAccess::ON_ERROR_POPUP: + { + bool ignoreNextErrors = false; + ErrorDlg* errorDlg = new ErrorDlg(NULL, + ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, + wxString(errorMessage) + wxT("\n\n") + _("Ignore this error, retry or abort?"), + ignoreNextErrors); + const int rv = errorDlg->ShowModal(); + errorDlg->Destroy(); + switch (rv) + { + case ErrorDlg::BUTTON_IGNORE: + if (ignoreNextErrors) //falsify only + m_handleError = xmlAccess::ON_ERROR_IGNORE; + unhandledErrors.Add(errorMessage.c_str()); + m_log->write(errorMessage.c_str(), _("Error")); + return ErrorHandler::IGNORE_ERROR; + + case ErrorDlg::BUTTON_RETRY: + return ErrorHandler::RETRY; + + case ErrorDlg::BUTTON_ABORT: + unhandledErrors.Add(errorMessage.c_str()); + m_log->write(errorMessage.c_str(), _("Error")); + abortThisProcess(); + } + } + break; //used if last switch didn't find a match + + case xmlAccess::ON_ERROR_EXIT: //abort + unhandledErrors.Add(errorMessage.c_str()); + m_log->write(errorMessage.c_str(), _("Error")); + abortThisProcess(); + + case xmlAccess::ON_ERROR_IGNORE: + unhandledErrors.Add(errorMessage.c_str()); + m_log->write(errorMessage.c_str(), _("Error")); + return ErrorHandler::IGNORE_ERROR; + } + + assert(false); + return ErrorHandler::IGNORE_ERROR; //dummy value +} + + +void BatchStatusHandlerSilent::reportFatalError(const Zstring& errorMessage) +{ + switch (m_handleError) + { + case xmlAccess::ON_ERROR_POPUP: + { + bool dummy = false; + ErrorDlg* errorDlg = new ErrorDlg(NULL, + ErrorDlg::BUTTON_ABORT, + errorMessage.c_str(), dummy); + errorDlg->ShowModal(); + errorDlg->Destroy(); + } + break; + + case xmlAccess::ON_ERROR_EXIT: + break; + + case xmlAccess::ON_ERROR_IGNORE: + break; + } + + unhandledErrors.Add(errorMessage.c_str()); + m_log->write(errorMessage.c_str(), _("Error")); + abortThisProcess(); +} + + +void BatchStatusHandlerSilent::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) +{ + switch (m_handleError) + { + case xmlAccess::ON_ERROR_POPUP: + { + //show popup and ask user how to handle warning + bool dontWarnAgain = false; + WarningDlg* warningDlg = new WarningDlg(NULL, + WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, + warningMessage.c_str(), + dontWarnAgain); + const int rv = warningDlg->ShowModal(); + warningDlg->Destroy(); + switch (rv) + { + case WarningDlg::BUTTON_ABORT: + unhandledErrors.Add(warningMessage.c_str()); + m_log->write(warningMessage.c_str(), _("Warning")); + abortThisProcess(); + case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! + dontShowAgain = dontWarnAgain; + m_log->write(warningMessage.c_str(), _("Warning")); + return; + } + } + break; //keep it! last switch might not find match + + case xmlAccess::ON_ERROR_EXIT: //abort + unhandledErrors.Add(warningMessage.c_str()); + m_log->write(warningMessage.c_str(), _("Warning")); + abortThisProcess(); + + case xmlAccess::ON_ERROR_IGNORE: //no unhandled error situation! + m_log->write(warningMessage.c_str(), _("Warning")); + return; + } + + assert(false); +} + + +void BatchStatusHandlerSilent::addFinalInfo(const Zstring& infoMessage) +{ + m_log->write(infoMessage.c_str(), _("Info")); +} + + +void BatchStatusHandlerSilent::forceUiRefresh() +{ + trayIcon->updateSysTray(); //needed by sys-tray icon only +} + + +void BatchStatusHandlerSilent::abortThisProcess() //used by sys-tray menu +{ + abortRequested = true; + throw FreeFileSync::AbortThisProcess(); +} + +//###################################################################################################### + + +BatchStatusHandlerGui::BatchStatusHandlerGui(const xmlAccess::OnError handleError, int& returnVal) : + m_handleError(handleError), + currentProcess(StatusHandler::PROCESS_NONE), + returnValue(returnVal) +{ + syncStatusFrame = new SyncStatus(this, NULL); + syncStatusFrame->Show(); +} + + +BatchStatusHandlerGui::~BatchStatusHandlerGui() +{ + //display result + wxString finalMessage; + + unsigned int failedItems = unhandledErrors.GetCount(); + if (failedItems) + { + finalMessage = wxString(_("Warning: Synchronization failed for %x item(s):")) + wxT("\n\n"); + finalMessage.Replace(wxT("%x"), globalFunctions::numberToWxString(failedItems), false); + + for (unsigned int j = 0; j < failedItems; ++j) + { //remove linebreaks + wxString errorMessage = unhandledErrors[j]; + for (wxString::iterator i = errorMessage.begin(); i != errorMessage.end(); ++i) + if (*i == wxChar('\n')) + *i = wxChar(' '); + + finalMessage += errorMessage + wxT("\n"); + } + finalMessage += wxT("\n"); + } + + if (!finalInfo.IsEmpty()) + finalMessage += finalInfo + wxT("\n\n"); + + //notify to syncStatusFrame that current process has ended + if (abortRequested) + { + returnValue = -4; + finalMessage += _("Synchronization aborted!"); + syncStatusFrame->setStatusText_NoUpdate(finalMessage.c_str()); + syncStatusFrame->processHasFinished(SyncStatus::ABORTED); //enable okay and close events + } + else if (failedItems) + { + returnValue = -5; + finalMessage += _("Synchronization completed with errors!"); + syncStatusFrame->setStatusText_NoUpdate(finalMessage.c_str()); + syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_ERROR); + } + else + { + finalMessage += _("Synchronization completed successfully!"); + syncStatusFrame->setStatusText_NoUpdate(finalMessage.c_str()); + syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS); + } +} + + +inline +void BatchStatusHandlerGui::updateStatusText(const Zstring& text) +{ + syncStatusFrame->setStatusText_NoUpdate(text); +} + + +void BatchStatusHandlerGui::initNewProcess(int objectsTotal, wxLongLong dataTotal, StatusHandler::Process processID) +{ + currentProcess = processID; + + if (currentProcess == StatusHandler::PROCESS_SCANNING) + syncStatusFrame->setCurrentStatus(SyncStatus::SCANNING); + + else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) + { + syncStatusFrame->resetGauge(objectsTotal, dataTotal); + syncStatusFrame->setCurrentStatus(SyncStatus::COMPARING); + } + + else if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING) + { + syncStatusFrame->resetGauge(objectsTotal, dataTotal); + syncStatusFrame->setCurrentStatus(SyncStatus::SYNCHRONIZING); + } + else assert(false); +} + + +inline +void BatchStatusHandlerGui::updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) +{ + if (currentProcess == StatusHandler::PROCESS_SCANNING) + ; + else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) + syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); + else if (currentProcess == StatusHandler::PROCESS_SYNCHRONIZING) + syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); + else assert(false); +} + + +ErrorHandler::Response BatchStatusHandlerGui::reportError(const Zstring& errorMessage) +{ + //add current time before error message + wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + errorMessage.c_str(); + + switch (m_handleError) + { + case xmlAccess::ON_ERROR_POPUP: + { + syncStatusFrame->updateStatusDialogNow(); + + bool ignoreNextErrors = false; + ErrorDlg* errorDlg = new ErrorDlg(syncStatusFrame, + ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, + wxString(errorMessage) + wxT("\n\n") + _("Ignore this error, retry or abort?"), + ignoreNextErrors); + switch (errorDlg->ShowModal()) + { + case ErrorDlg::BUTTON_IGNORE: + if (ignoreNextErrors) //falsify only + m_handleError = xmlAccess::ON_ERROR_IGNORE; + unhandledErrors.Add(errorWithTime); + return ErrorHandler::IGNORE_ERROR; + case ErrorDlg::BUTTON_RETRY: + return ErrorHandler::RETRY; + case ErrorDlg::BUTTON_ABORT: + unhandledErrors.Add(errorWithTime); + abortThisProcess(); + } + } + break; //used IF last switch didn't find a match + + case xmlAccess::ON_ERROR_EXIT: //abort + unhandledErrors.Add(errorWithTime); + abortThisProcess(); + + case xmlAccess::ON_ERROR_IGNORE: + unhandledErrors.Add(errorWithTime); + return ErrorHandler::IGNORE_ERROR; + } + + assert(false); + return ErrorHandler::IGNORE_ERROR; //dummy value +} + + +void BatchStatusHandlerGui::reportFatalError(const Zstring& errorMessage) +{ //add current time before error message + wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + errorMessage.c_str(); + + unhandledErrors.Add(errorWithTime); + abortThisProcess(); +} + + +void BatchStatusHandlerGui::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) +{ //add current time before warning message + wxString warningWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + warningMessage.c_str(); + + switch (m_handleError) + { + case xmlAccess::ON_ERROR_POPUP: + case xmlAccess::ON_ERROR_EXIT: //show popup in this case also + { + //show popup and ask user how to handle warning + bool dontWarnAgain = false; + WarningDlg* warningDlg = new WarningDlg(NULL, + WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, + warningMessage.c_str(), + dontWarnAgain); + const int rv = warningDlg->ShowModal(); + warningDlg->Destroy(); + switch (rv) + { + case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! + dontShowAgain = dontWarnAgain; + return; + case WarningDlg::BUTTON_ABORT: + unhandledErrors.Add(warningWithTime); + abortThisProcess(); + } + } + break; //keep it! last switch might not find match + + case xmlAccess::ON_ERROR_IGNORE: //no unhandled error situation! + return; + } + + assert(false); +} + + +inline +void BatchStatusHandlerGui::forceUiRefresh() +{ + if (currentProcess == StatusHandler::PROCESS_SCANNING) + syncStatusFrame->m_gauge1->Pulse(); //expensive! So put it here! + + syncStatusFrame->updateStatusDialogNow(); +} + + +void BatchStatusHandlerGui::abortThisProcess() +{ + abortRequested = true; + throw FreeFileSync::AbortThisProcess(); //abort can be triggered by syncStatusFrame +} + + +void BatchStatusHandlerGui::addFinalInfo(const Zstring& infoMessage) +{ + finalInfo = infoMessage.c_str(); +} diff --git a/ui/batchStatusHandler.h b/ui/batchStatusHandler.h new file mode 100644 index 00000000..7087d3c9 --- /dev/null +++ b/ui/batchStatusHandler.h @@ -0,0 +1,83 @@ +#ifndef BATCHSTATUSHANDLER_H_INCLUDED +#define BATCHSTATUSHANDLER_H_INCLUDED + +#include "../library/statusHandler.h" +#include <memory> +#include "../library/processXml.h" +#include <wx/arrstr.h> + +class LogFile; +class FfsTrayIcon; +class SyncStatus; + + +class BatchStatusHandler : public StatusHandler +{ +public: + BatchStatusHandler() {} + virtual ~BatchStatusHandler() {} + + virtual void addFinalInfo(const Zstring& infoMessage) = 0; +}; + + +class BatchStatusHandlerSilent : public BatchStatusHandler +{ +public: + BatchStatusHandlerSilent(const xmlAccess::OnError handleError, const wxString& logfileDirectory, int& returnVal); + ~BatchStatusHandlerSilent(); + + + virtual void updateStatusText(const Zstring& text); + virtual void initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID); + virtual void updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) {} + virtual void forceUiRefresh(); + + virtual ErrorHandler::Response reportError(const Zstring& errorMessage); + virtual void reportFatalError(const Zstring& errorMessage); + virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); + virtual void addFinalInfo(const Zstring& infoMessage); + +private: + virtual void abortThisProcess(); + + xmlAccess::OnError m_handleError; + wxArrayString unhandledErrors; //list of non-resolved errors + Process currentProcess; + int& returnValue; + std::auto_ptr<FfsTrayIcon> trayIcon; + + std::auto_ptr<LogFile> m_log; +}; + + +class BatchStatusHandlerGui : public BatchStatusHandler +{ +public: + BatchStatusHandlerGui(const xmlAccess::OnError handleError, int& returnVal); + ~BatchStatusHandlerGui(); + + virtual void updateStatusText(const Zstring& text); + virtual void initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID); + virtual void updateProcessedData(int objectsProcessed, wxLongLong dataProcessed); + virtual void forceUiRefresh(); + + virtual ErrorHandler::Response reportError(const Zstring& errorMessage); + virtual void reportFatalError(const Zstring& errorMessage); + virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); + virtual void addFinalInfo(const Zstring& infoMessage); + +private: + virtual void abortThisProcess(); + + xmlAccess::OnError m_handleError; + wxArrayString unhandledErrors; //list of non-resolved errors + Process currentProcess; + int& returnValue; + + SyncStatus* syncStatusFrame; + wxString finalInfo; //workaround to display "Nothing to synchronize..." +}; + + +#endif // BATCHSTATUSHANDLER_H_INCLUDED diff --git a/ui/checkVersion.cpp b/ui/checkVersion.cpp index 5344b45e..914e5e09 100644 --- a/ui/checkVersion.cpp +++ b/ui/checkVersion.cpp @@ -5,6 +5,8 @@ #include "../version/version.h" #include <wx/msgdlg.h> #include <wx/utils.h> +#include <wx/timer.h> +#include "../library/globalFunctions.h" class CloseConnectionOnExit @@ -26,7 +28,7 @@ private: }; -void FreeFileSync::checkForNewVersion() +bool getOnlineVersion(wxString& version) { wxHTTP webAccess; wxInputStream* httpStream = NULL; @@ -50,21 +52,85 @@ void FreeFileSync::checkForNewVersion() httpStream->Read(out_stream); if (!newestVersion.empty()) { - if (FreeFileSync::currentVersion == newestVersion) - { - wxMessageBox(_("FreeFileSync is up to date!"), _("Information"), wxICON_INFORMATION); - return; - } - else - { - const int rv = wxMessageBox(wxString(_("A newer version is available:")) + wxT(" v") + newestVersion + wxT(". ") + _("Download now?"), _("Information"), wxYES_NO | wxICON_QUESTION); - if (rv == wxYES) - wxLaunchDefaultBrowser(wxT("http://sourceforge.net/project/showfiles.php?group_id=234430")); - return; - } + version = newestVersion; + return true; } } } - wxMessageBox(_("Unable to connect to sourceforge.net!"), _("Error"), wxOK | wxICON_ERROR); + return false; } + + +bool newerVersionExists(const wxString& onlineVersion) +{ + const wxString currentMajor = FreeFileSync::currentVersion.BeforeLast(wxT('.')); + const wxString onlineMajor = onlineVersion.BeforeLast(wxT('.')); + + if (currentMajor != onlineMajor) + return globalFunctions::wxStringToInt(currentMajor) < globalFunctions::wxStringToInt(onlineMajor); + + const wxString currentMinor = FreeFileSync::currentVersion.AfterLast(wxT('.')); + const wxString onlineMinor = onlineVersion.AfterLast(wxT('.')); + + return globalFunctions::wxStringToInt(currentMinor) < globalFunctions::wxStringToInt(onlineMinor); +} + + +void FreeFileSync::checkForUpdateNow() +{ + wxString onlineVersion; + if (!getOnlineVersion(onlineVersion)) + { + wxMessageBox(_("Unable to connect to sourceforge.net!"), _("Error"), wxOK | wxICON_ERROR); + 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); + if (rv == wxYES) + wxLaunchDefaultBrowser(wxT("http://sourceforge.net/project/showfiles.php?group_id=234430")); + } + else + wxMessageBox(_("FreeFileSync is up to date!"), _("Information"), wxICON_INFORMATION); +} + + +void FreeFileSync::checkForUpdatePeriodically(long& lastUpdateCheck) +{ + if (lastUpdateCheck != -1) + { + if (lastUpdateCheck == 0) + { + const int rv = wxMessageBox(_("Do you want FreeFileSync to automatically check for updates every week?"), _("Information"), wxYES_NO | wxICON_QUESTION); + if (rv == wxYES) + { + lastUpdateCheck = 123; //some old date (few seconds after 1970) + + checkForUpdatePeriodically(lastUpdateCheck); //check for updates now + } + else + lastUpdateCheck = -1; //don't check for updates anymore + } + else if (wxGetLocalTime() >= lastUpdateCheck + 7 * 24 * 3600) //check weekly + { + wxString onlineVersion; + if (!getOnlineVersion(onlineVersion)) + return; //do not handle error + + lastUpdateCheck = wxGetLocalTime(); + + 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); + if (rv == wxYES) + wxLaunchDefaultBrowser(wxT("http://sourceforge.net/project/showfiles.php?group_id=234430")); + } + } + } +} + + + + diff --git a/ui/checkVersion.h b/ui/checkVersion.h index e6705774..e59235f4 100644 --- a/ui/checkVersion.h +++ b/ui/checkVersion.h @@ -4,7 +4,9 @@ namespace FreeFileSync { - void checkForNewVersion(); + void checkForUpdateNow(); + + void checkForUpdatePeriodically(long& lastUpdateCheck); } #endif // UPDATEVERSION_H_INCLUDED diff --git a/ui/dragAndDrop.cpp b/ui/dragAndDrop.cpp new file mode 100644 index 00000000..c7aea906 --- /dev/null +++ b/ui/dragAndDrop.cpp @@ -0,0 +1,207 @@ +#include "dragAndDrop.h" +#include <wx/dnd.h> +#include <wx/window.h> +#include <wx/combobox.h> +#include <wx/textctrl.h> +#include <wx/filepicker.h> +#include <wx/filename.h> +#include "../algorithm.h" + + +//define new event type +const wxEventType FFS_DROP_FILE_EVENT = wxNewEventType(); + +typedef void (wxEvtHandler::*FFSFileDropEventFunction)(FFSFileDropEvent&); + +#define FFSFileDropEventHandler(func) \ + (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FFSFileDropEventFunction, &func) + +class FFSFileDropEvent : public wxCommandEvent +{ +public: + FFSFileDropEvent(const wxString& nameDropped, const wxWindow* dropWindow) : + wxCommandEvent(FFS_DROP_FILE_EVENT), + nameDropped_(nameDropped), + dropWindow_(dropWindow) {} + + virtual wxEvent* Clone() const + { + return new FFSFileDropEvent(nameDropped_, dropWindow_); + } + + const wxString nameDropped_; + const wxWindow* dropWindow_; +}; + +//############################################################################################################## + + +class WindowDropTarget : public wxFileDropTarget +{ +public: + WindowDropTarget(wxWindow* dropWindow) : + dropWindow_(dropWindow) {} + + virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) + { + if (!filenames.IsEmpty()) + { + const wxString droppedFileName = filenames[0]; + + //create a custom event on drop window: execute event after file dropping is completed! (e.g. after mouse is released) + FFSFileDropEvent evt(droppedFileName, dropWindow_); + dropWindow_->GetEventHandler()->AddPendingEvent(evt); + } + return false; + } + +private: + wxWindow* dropWindow_; +}; + + + +//############################################################################################################## + +using FreeFileSync::DragDropOnMainDlg; + +DragDropOnMainDlg::DragDropOnMainDlg(wxWindow* dropWindow1, + wxWindow* dropWindow2, + wxDirPickerCtrl* dirPicker, + wxComboBox* dirName) : + dropWindow1_(dropWindow1), + dropWindow2_(dropWindow2), + dirPicker_(dirPicker), + dirName_(dirName) +{ + //prepare drag & drop + dropWindow1->SetDropTarget(new WindowDropTarget(dropWindow1)); //takes ownership + dropWindow2->SetDropTarget(new WindowDropTarget(dropWindow2)); //takes ownership + + //redirect drag & drop event back to this class + dropWindow1->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnMainDlg::OnFilesDropped), NULL, this); + dropWindow2->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnMainDlg::OnFilesDropped), NULL, this); + + //keep dirPicker and dirName synchronous + dirName->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DragDropOnMainDlg::OnWriteDirManually ), NULL, this ); + dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DragDropOnMainDlg::OnDirSelected ), NULL, this ); +} + + +void DragDropOnMainDlg::OnFilesDropped(FFSFileDropEvent& event) +{ + if ( this->dropWindow1_ == event.dropWindow_ || //file may be dropped on window 1 or 2 + this->dropWindow2_ == event.dropWindow_) + { + if (AcceptDrop(event.nameDropped_)) + { + wxString fileName = event.nameDropped_; + if (wxDirExists(fileName)) + { + dirName_->SetSelection(wxNOT_FOUND); + dirName_->SetValue(fileName); + dirPicker_->SetPath(fileName); + } + else + { + fileName = wxFileName(fileName).GetPath(); + if (wxDirExists(fileName)) + { + dirName_->SetSelection(wxNOT_FOUND); + dirName_->SetValue(fileName); + dirPicker_->SetPath(fileName); + } + } + } + } + else //should never be reached + event.Skip(); +}; + + +void DragDropOnMainDlg::OnWriteDirManually(wxCommandEvent& event) +{ + const wxString newDir = FreeFileSync::getFormattedDirectoryName(event.GetString().c_str()).c_str(); + if (wxDirExists(newDir)) + dirPicker_->SetPath(newDir); + + event.Skip(); +} + + +void DragDropOnMainDlg::OnDirSelected(wxFileDirPickerEvent& event) +{ + const wxString newPath = event.GetPath(); + dirName_->SetSelection(wxNOT_FOUND); + dirName_->SetValue(newPath); + + event.Skip(); +} + +//############################################################################################################## + + +using FreeFileSync::DragDropOnDlg; + +DragDropOnDlg::DragDropOnDlg(wxWindow* dropWindow, + wxDirPickerCtrl* dirPicker, + wxTextCtrl* dirName) : + dropWindow_(dropWindow), + dirPicker_(dirPicker), + dirName_(dirName) +{ + //prepare drag & drop + dropWindow->SetDropTarget(new WindowDropTarget(dropWindow)); //takes ownership + + //redirect drag & drop event back to this class + dropWindow->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnDlg::OnFilesDropped), NULL, this); + + //keep dirPicker and dirName synchronous + dirName->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DragDropOnDlg::OnWriteDirManually ), NULL, this ); + dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DragDropOnDlg::OnDirSelected ), NULL, this ); +} + + +void DragDropOnDlg::OnFilesDropped(FFSFileDropEvent& event) +{ + if (this->dropWindow_ == event.dropWindow_) + { + wxString fileName = event.nameDropped_; + if (wxDirExists(fileName)) + { + dirName_->SetValue(fileName); + dirPicker_->SetPath(fileName); + } + else + { + fileName = wxFileName(fileName).GetPath(); + if (wxDirExists(fileName)) + { + dirName_->SetValue(fileName); + dirPicker_->SetPath(fileName); + } + } + } + else //should never be reached + event.Skip(); +}; + + +void DragDropOnDlg::OnWriteDirManually(wxCommandEvent& event) +{ + const wxString newDir = FreeFileSync::getFormattedDirectoryName(event.GetString().c_str()).c_str(); + if (wxDirExists(newDir)) + dirPicker_->SetPath(newDir); + + event.Skip(); +} + + +void DragDropOnDlg::OnDirSelected(wxFileDirPickerEvent& event) +{ + const wxString newPath = event.GetPath(); + dirName_->SetValue(newPath); + + event.Skip(); +} + diff --git a/ui/dragAndDrop.h b/ui/dragAndDrop.h new file mode 100644 index 00000000..cca6f865 --- /dev/null +++ b/ui/dragAndDrop.h @@ -0,0 +1,63 @@ +#ifndef DRAGANDDROP_H_INCLUDED +#define DRAGANDDROP_H_INCLUDED + +#include <wx/event.h> + +class wxWindow; +class wxDirPickerCtrl; +class wxComboBox; +class wxTextCtrl; +class wxString; +class FFSFileDropEvent; +class wxCommandEvent; +class wxFileDirPickerEvent; + + +namespace FreeFileSync +{ + //add drag and drop functionality, coordinating a wxWindow, wxDirPickerCtrl, and wxComboBox/wxTextCtrl + + class DragDropOnMainDlg : public wxEvtHandler + { + public: + DragDropOnMainDlg(wxWindow* dropWindow1, + wxWindow* dropWindow2, + wxDirPickerCtrl* dirPicker, + wxComboBox* dirName); + + virtual ~DragDropOnMainDlg() {} + + virtual bool AcceptDrop(const wxString& dropName) = 0; //return true if drop should be processed + + private: + void OnFilesDropped(FFSFileDropEvent& event); + void OnWriteDirManually(wxCommandEvent& event); + void OnDirSelected(wxFileDirPickerEvent& event); + + const wxWindow* dropWindow1_; + const wxWindow* dropWindow2_; + wxDirPickerCtrl* dirPicker_; + wxComboBox* dirName_; + }; + + + class DragDropOnDlg: public wxEvtHandler + { + public: + DragDropOnDlg(wxWindow* dropWindow, + wxDirPickerCtrl* dirPicker, + wxTextCtrl* dirName); + + private: + void OnFilesDropped(FFSFileDropEvent& event); + void OnWriteDirManually(wxCommandEvent& event); + void OnDirSelected(wxFileDirPickerEvent& event); + + const wxWindow* dropWindow_; + wxDirPickerCtrl* dirPicker_; + wxTextCtrl* dirName_; + }; +} + + +#endif // DRAGANDDROP_H_INCLUDED diff --git a/ui/gridView.cpp b/ui/gridView.cpp new file mode 100644 index 00000000..ed950c15 --- /dev/null +++ b/ui/gridView.cpp @@ -0,0 +1,213 @@ +#include "gridView.h" +#include "sorting.h" + +using FreeFileSync::GridView; + + +GridView::StatusInfo GridView::update( + const bool includeLeftOnly, + const bool includeRightOnly, + const bool includeLeftNewer, + const bool includeRightNewer, + const bool includeDifferent, + const bool includeEqual, + const bool hideFiltered) +{ + StatusInfo output; + output.existsLeftOnly = false; + output.existsRightOnly = false; + output.existsLeftNewer = false; + output.existsRightNewer = false; + output.existsDifferent = false; + output.existsEqual = false; + + output.filesOnLeftView = 0; + output.foldersOnLeftView = 0; + output.filesOnRightView = 0; + output.foldersOnRightView = 0; + + refView.clear(); + + for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + { + const FileComparison& fileCmp = j->fileCmp; + + RefIndex newEntry; + newEntry.folderIndex = j - folderCmp.begin(); + + for (FileComparison::const_iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) + { + //hide filtered row, if corresponding option is set + if (hideFiltered && !i->selectedForSynchronization) + continue; + + //process UI filter settings + switch (i->cmpResult) + { + case FILE_LEFT_SIDE_ONLY: + output.existsLeftOnly = true; + if (!includeLeftOnly) continue; + break; + case FILE_RIGHT_SIDE_ONLY: + output.existsRightOnly = true; + if (!includeRightOnly) continue; + break; + case FILE_LEFT_NEWER: + output.existsLeftNewer = true; + if (!includeLeftNewer) continue; + break; + case FILE_RIGHT_NEWER: + output.existsRightNewer = true; + if (!includeRightNewer) continue; + break; + case FILE_DIFFERENT: + output.existsDifferent = true; + if (!includeDifferent) continue; + break; + case FILE_EQUAL: + output.existsEqual = true; + if (!includeEqual) continue; + break; + default: + assert (false); + } + + //calculate total number of bytes for each side + if (i->fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + { + output.filesizeLeftView += i->fileDescrLeft.fileSize; + ++output.filesOnLeftView; + } + else if (i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + ++output.foldersOnLeftView; + + if (i->fileDescrRight.objType == FileDescrLine::TYPE_FILE) + { + output.filesizeRightView += i->fileDescrRight.fileSize; + ++output.filesOnRightView; + } + else if (i->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + ++output.foldersOnRightView; + + + newEntry.rowIndex = i - fileCmp.begin(); + refView.push_back(newEntry); + } + +// //add some empty line after each folder pair +// RefIndex emptyLine; +// emptyLine.folderIndex = -1; +// emptyLine.rowIndex = 0; +// refView.push_back(emptyLine); + } + + return output; +} + + +void GridView::viewRefToFolderRef(const std::set<int>& viewRef, FolderCompRef& output) +{ + output.clear(); + for (int i = 0; i < int(folderCmp.size()); ++i) + output.push_back(std::set<int>()); //avoid copy by value for full set<int> + + for (std::set<int>::iterator i = viewRef.begin(); i != viewRef.end(); ++i) + { + const unsigned int folder = refView[*i].folderIndex; + const unsigned int row = refView[*i].rowIndex; + + output[folder].insert(row); + } +} + + +unsigned int GridView::elementsTotal() const +{ + unsigned int total = 0; + for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + total += j->fileCmp.size(); + + return total; +} + + +template <typename CompareFct> +void bubbleSort(FreeFileSync::FolderComparison& folderCmp, CompareFct compare) +{ + for (int i = folderCmp.size() - 2; i >= 0; --i) + { + bool swapped = false; + for (int j = 0; j <= i; ++j) + if (compare(folderCmp[j + 1], folderCmp[j])) + { + std::swap(folderCmp[j + 1].syncPair, folderCmp[j].syncPair); + folderCmp[j + 1].fileCmp.swap(folderCmp[j].fileCmp); + + swapped = true; + } + + if (!swapped) + return; + } +} + + +void GridView::sortView(const SortType type, const bool onLeft, const bool ascending) +{ + using namespace FreeFileSync; + + if (type == SORT_BY_DIRECTORY) + { + //specialization: use own sorting function based on vector<FileCompareLine>::swap() + //bubble sort is no performance issue since number of folder pairs should be "small" + if (ascending && onLeft) bubbleSort(folderCmp, sortByDirectory<ASCENDING, SORT_ON_LEFT>); + else if (ascending && !onLeft) bubbleSort(folderCmp, sortByDirectory<ASCENDING, SORT_ON_RIGHT>); + else if (!ascending && onLeft) bubbleSort(folderCmp, sortByDirectory<DESCENDING, SORT_ON_LEFT>); + else if (!ascending && !onLeft) bubbleSort(folderCmp, sortByDirectory<DESCENDING, SORT_ON_RIGHT>); + + //then sort by relative name + GridView::sortView(SORT_BY_REL_NAME, onLeft, ascending); + return; + } + + + for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + { + FileComparison& fileCmp = j->fileCmp; + + switch (type) + { + case SORT_BY_REL_NAME: + if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByRelativeName<ASCENDING, SORT_ON_LEFT>); + else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByRelativeName<ASCENDING, SORT_ON_RIGHT>); + else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByRelativeName<DESCENDING, SORT_ON_LEFT>); + else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByRelativeName<DESCENDING, SORT_ON_RIGHT>); + break; + case SORT_BY_FILENAME: + if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileName<ASCENDING, SORT_ON_LEFT>); + else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileName<ASCENDING, SORT_ON_RIGHT>); + else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileName<DESCENDING, SORT_ON_LEFT>); + else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileName<DESCENDING, SORT_ON_RIGHT>); + break; + case SORT_BY_FILESIZE: + if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileSize<ASCENDING, SORT_ON_LEFT>); + else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileSize<ASCENDING, SORT_ON_RIGHT>); + else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileSize<DESCENDING, SORT_ON_LEFT>); + else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileSize<DESCENDING, SORT_ON_RIGHT>); + break; + case SORT_BY_DATE: + if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByDate<ASCENDING, SORT_ON_LEFT>); + else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByDate<ASCENDING, SORT_ON_RIGHT>); + else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByDate<DESCENDING, SORT_ON_LEFT>); + else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByDate<DESCENDING, SORT_ON_RIGHT>); + break; + case SORT_BY_CMP_RESULT: + if ( ascending) std::sort(fileCmp.begin(), fileCmp.end(), sortByCmpResult<ASCENDING>); + else if (!ascending) std::sort(fileCmp.begin(), fileCmp.end(), sortByCmpResult<DESCENDING>); + break; + default: + assert(false); + } + } +} + diff --git a/ui/gridView.h b/ui/gridView.h new file mode 100644 index 00000000..b4101da7 --- /dev/null +++ b/ui/gridView.h @@ -0,0 +1,108 @@ +#ifndef GRIDVIEW_H_INCLUDED +#define GRIDVIEW_H_INCLUDED + +#include "../structures.h" + + +namespace FreeFileSync +{ + //gui view of FolderComparison + class GridView + { + public: + GridView(FolderComparison& results) : folderCmp(results) {} + + const FileCompareLine& operator[] (unsigned row) const; + + //unsigned getResultsIndex(const unsigned viewIndex); //convert index on GridView to index on FolderComparison + + unsigned int elementsOnView() const; + + unsigned int elementsTotal() const; + + //convert view references to FolderCompRef + void viewRefToFolderRef(const std::set<int>& viewRef, FolderCompRef& output); + + const FolderPair getFolderPair(const unsigned int row) const; + + struct StatusInfo + { + bool existsLeftOnly; + bool existsRightOnly; + bool existsLeftNewer; + bool existsRightNewer; + bool existsDifferent; + bool existsEqual; + + unsigned int filesOnLeftView; + unsigned int foldersOnLeftView; + unsigned int filesOnRightView; + unsigned int foldersOnRightView; + + wxULongLong filesizeLeftView; + wxULongLong filesizeRightView; + }; + + StatusInfo update(const bool includeLeftOnly, + const bool includeRightOnly, + const bool includeLeftNewer, + const bool includeRightNewer, + const bool includeDifferent, + const bool includeEqual, + const bool hideFiltered); + + //sorting... + enum SortType + { + SORT_BY_REL_NAME, + SORT_BY_FILENAME, + SORT_BY_FILESIZE, + SORT_BY_DATE, + SORT_BY_CMP_RESULT, + SORT_BY_DIRECTORY + }; + + void sortView(const SortType type, const bool onLeft, const bool ascending); + + private: + struct RefIndex + { + unsigned int folderIndex; + unsigned int rowIndex; + }; + + std::vector<RefIndex> refView; + FolderComparison& folderCmp; + }; + + +//############################################################################ +//inline implementation + inline + const FileCompareLine& GridView::operator[] (unsigned row) const + { + const unsigned int folderInd = refView[row].folderIndex; + const unsigned int rowInd = refView[row].rowIndex; + + return folderCmp[folderInd].fileCmp[rowInd]; + } + + + inline + unsigned int GridView::elementsOnView() const + { + return refView.size(); + } + + + inline + const FolderPair GridView::getFolderPair(const unsigned int row) const + { + const unsigned int folderInd = refView[row].folderIndex; + const FolderCompareLine& folderCmpLine = folderCmp[folderInd]; + return folderCmpLine.syncPair; + } +} + + +#endif // GRIDVIEW_H_INCLUDED diff --git a/ui/guiGenerated.cpp b/ui/guiGenerated.cpp index c7188b38..c56fafbb 100644 --- a/ui/guiGenerated.cpp +++ b/ui/guiGenerated.cpp @@ -17,96 +17,99 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const this->SetSizeHints( wxDefaultSize, wxDefaultSize ); m_menubar1 = new wxMenuBar( 0 ); - m_menu1 = new wxMenu(); - m_menuItem10 = new wxMenuItem( m_menu1, wxID_ANY, wxString( _("1. &Compare") ) + wxT('\t') + wxT("ALT-C"), wxEmptyString, wxITEM_NORMAL ); - m_menu1->Append( m_menuItem10 ); + m_menuFile = new wxMenu(); + m_menuItem10 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("1. &Compare") ) + wxT('\t') + wxT("ALT-C"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem10 ); - m_menuItem11 = new wxMenuItem( m_menu1, wxID_ANY, wxString( _("2. &Synchronize...") ) + wxT('\t') + wxT("ALT-S"), wxEmptyString, wxITEM_NORMAL ); - m_menu1->Append( m_menuItem11 ); + m_menuItem11 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("2. &Synchronize...") ) + wxT('\t') + wxT("ALT-S"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem11 ); - m_menu1->AppendSeparator(); + m_menuFile->AppendSeparator(); wxMenuItem* m_menuItem14; - m_menuItem14 = new wxMenuItem( m_menu1, wxID_ANY, wxString( _("S&ave configuration") ) + wxT('\t') + wxT("CTRL-S"), wxEmptyString, wxITEM_NORMAL ); - m_menu1->Append( m_menuItem14 ); + m_menuItem14 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("S&ave configuration") ) + wxT('\t') + wxT("CTRL-S"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem14 ); wxMenuItem* m_menuItem13; - m_menuItem13 = new wxMenuItem( m_menu1, wxID_ANY, wxString( _("&Load configuration") ) + wxT('\t') + wxT("CTRL-L"), wxEmptyString, wxITEM_NORMAL ); - m_menu1->Append( m_menuItem13 ); + m_menuItem13 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("&Load configuration") ) + wxT('\t') + wxT("CTRL-L"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem13 ); - m_menu1->AppendSeparator(); + m_menuFile->AppendSeparator(); wxMenuItem* m_menuItem4; - m_menuItem4 = new wxMenuItem( m_menu1, wxID_EXIT, wxString( _("&Quit") ) + wxT('\t') + wxT("CTRL-Q"), wxEmptyString, wxITEM_NORMAL ); - m_menu1->Append( m_menuItem4 ); + m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("&Quit") ) + wxT('\t') + wxT("CTRL-Q"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem4 ); - m_menubar1->Append( m_menu1, _("&File") ); + m_menubar1->Append( m_menuFile, _("&File") ); - m_menu3 = new wxMenu(); - m_menu31 = new wxMenu(); - m_menuItemGerman = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Deutsch") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemGerman ); + m_menuAdvanced = new wxMenu(); + m_menuLanguages = new wxMenu(); + m_menuItemGerman = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Deutsch") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemGerman ); - m_menuItemEnglish = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("English") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemEnglish ); + m_menuItemEnglish = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("English") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemEnglish ); - m_menuItemSpanish = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Español") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemSpanish ); + m_menuItemSpanish = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Español") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemSpanish ); - m_menuItemFrench = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Français") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemFrench ); + m_menuItemFrench = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Français") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemFrench ); - m_menuItemHungarian = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Magyar Nyelv") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemHungarian ); + m_menuItemItalian = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Italiano") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemItalian ); - m_menuItemItalian = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Italiano") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemItalian ); + m_menuItemHungarian = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Magyar") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemHungarian ); - m_menuItemPolish = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Język Polski") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemPolish ); + m_menuItemDutch = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Nederlands") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemDutch ); - m_menuItemDutch = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Nederlands") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemDutch ); + m_menuItemPolish = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Polski") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemPolish ); - m_menuItemPortuguese = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Português") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemPortuguese ); + m_menuItemPortuguese = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Português") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemPortuguese ); - m_menuItemSlovenian = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("Slovenščina") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemSlovenian ); + m_menuItemPortugueseBrazil = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Português do Brasil") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemPortugueseBrazil ); - m_menuItemJapanese = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("日本語") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemJapanese ); + m_menuItemSlovenian = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("Slovenščina") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemSlovenian ); - m_menuItemChineseSimple = new wxMenuItem( m_menu31, wxID_ANY, wxString( _("简体中文") ) , wxEmptyString, wxITEM_RADIO ); - m_menu31->Append( m_menuItemChineseSimple ); + m_menuItemJapanese = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("日本語") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemJapanese ); - m_menu3->Append( -1, _("&Language"), m_menu31 ); + m_menuItemChineseSimple = new wxMenuItem( m_menuLanguages, wxID_ANY, wxString( _("简体中文") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuLanguages->Append( m_menuItemChineseSimple ); - m_menu3->AppendSeparator(); + m_menuAdvanced->Append( -1, _("&Language"), m_menuLanguages ); - m_menuItemGlobSett = new wxMenuItem( m_menu3, wxID_ANY, wxString( _("&Global settings") ) , wxEmptyString, wxITEM_NORMAL ); - m_menu3->Append( m_menuItemGlobSett ); + m_menuAdvanced->AppendSeparator(); - m_menuItem7 = new wxMenuItem( m_menu3, wxID_ANY, wxString( _("&Create batch job") ) , wxEmptyString, wxITEM_NORMAL ); - m_menu3->Append( m_menuItem7 ); + m_menuItemGlobSett = new wxMenuItem( m_menuAdvanced, wxID_ANY, wxString( _("&Global settings") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuAdvanced->Append( m_menuItemGlobSett ); + + m_menuItem7 = new wxMenuItem( m_menuAdvanced, wxID_ANY, wxString( _("&Create batch job") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuAdvanced->Append( m_menuItem7 ); wxMenuItem* m_menuItem5; - m_menuItem5 = new wxMenuItem( m_menu3, wxID_ANY, wxString( _("&Export file list") ) , wxEmptyString, wxITEM_NORMAL ); - m_menu3->Append( m_menuItem5 ); + m_menuItem5 = new wxMenuItem( m_menuAdvanced, wxID_ANY, wxString( _("&Export file list") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuAdvanced->Append( m_menuItem5 ); - m_menubar1->Append( m_menu3, _("&Advanced") ); + m_menubar1->Append( m_menuAdvanced, _("&Advanced") ); - m_menu33 = new wxMenu(); + m_menuHelp = new wxMenu(); wxMenuItem* m_menuItemCheckVer; - m_menuItemCheckVer = new wxMenuItem( m_menu33, wxID_ANY, wxString( _("&Check for new version") ) , wxEmptyString, wxITEM_NORMAL ); - m_menu33->Append( m_menuItemCheckVer ); + m_menuItemCheckVer = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("&Check for new version") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemCheckVer ); - m_menu33->AppendSeparator(); + m_menuHelp->AppendSeparator(); - m_menuItemAbout = new wxMenuItem( m_menu33, wxID_ABOUT, wxString( _("&About...") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); - m_menu33->Append( m_menuItemAbout ); + m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About...") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemAbout ); - m_menubar1->Append( m_menu33, _("&Help") ); + m_menubar1->Append( m_menuHelp, _("&Help") ); this->SetMenuBar( m_menubar1 ); @@ -128,7 +131,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer30->Add( m_buttonCompare, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_buttonAbort = new wxButton( m_panel71, wxID_CANCEL, _("Abort"), wxDefaultPosition, wxSize( 180,37 ), 0 ); + m_buttonAbort = new wxButton( m_panel71, wxID_CANCEL, _("&Abort"), wxDefaultPosition, wxSize( 180,37 ), 0 ); m_buttonAbort->SetFont( wxFont( 14, 74, 90, 92, false, wxT("Tahoma") ) ); m_buttonAbort->Enable( false ); m_buttonAbort->Hide(); @@ -226,26 +229,28 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer91; bSizer91 = new wxBoxSizer( wxHORIZONTAL ); - m_panel11 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelTopLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer92; bSizer92 = new wxBoxSizer( wxVERTICAL ); - sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( m_panel11, wxID_ANY, _("Drag && drop") ), wxHORIZONTAL ); + sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( m_panelTopLeft, wxID_ANY, _("Drag && drop") ), wxHORIZONTAL ); + + m_directoryLeft = new wxComboBox( m_panelTopLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + sbSizer2->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); - m_comboBoxDirLeft = new wxComboBox( m_panel11, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - sbSizer2->Add( m_comboBoxDirLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); + m_dirPickerLeft = new wxDirPickerCtrl( m_panelTopLeft, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, wxDIRP_DIR_MUST_EXIST ); + m_dirPickerLeft->SetToolTip( _("Select a folder") ); - m_dirPickerLeft = new wxDirPickerCtrl( m_panel11, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, wxDIRP_DIR_MUST_EXIST ); sbSizer2->Add( m_dirPickerLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer92->Add( sbSizer2, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); - m_panel11->SetSizer( bSizer92 ); - m_panel11->Layout(); - bSizer92->Fit( m_panel11 ); - bSizer91->Add( m_panel11, 1, wxALIGN_CENTER_VERTICAL, 5 ); + m_panelTopLeft->SetSizer( bSizer92 ); + m_panelTopLeft->Layout(); + bSizer92->Fit( m_panelTopLeft ); + bSizer91->Add( m_panelTopLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); - m_panel13 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelTopMiddle = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer93; bSizer93 = new wxBoxSizer( wxVERTICAL ); @@ -254,7 +259,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerMiddle = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonSwap = new wxBitmapButton( m_panel13, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSwap = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); m_bpButtonSwap->SetToolTip( _("Swap sides") ); m_bpButtonSwap->SetToolTip( _("Swap sides") ); @@ -266,12 +271,12 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer93->Add( 0, 0, 0, 0, 5 ); - m_panel13->SetSizer( bSizer93 ); - m_panel13->Layout(); - bSizer93->Fit( m_panel13 ); - bSizer91->Add( m_panel13, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + m_panelTopMiddle->SetSizer( bSizer93 ); + m_panelTopMiddle->Layout(); + bSizer93->Fit( m_panelTopMiddle ); + bSizer91->Add( m_panelTopMiddle, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - m_panel12 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelTopRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer94; bSizer94 = new wxBoxSizer( wxVERTICAL ); @@ -284,7 +289,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer781->Add( 0, 3, 0, 0, 5 ); - m_bpButtonAddPair = new wxBitmapButton( m_panel12, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); + m_bpButtonAddPair = new wxBitmapButton( m_panelTopRight, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); m_bpButtonAddPair->SetToolTip( _("Add folder pair") ); m_bpButtonAddPair->SetToolTip( _("Add folder pair") ); @@ -294,22 +299,24 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer77->Add( bSizer781, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); wxStaticBoxSizer* sbSizer3; - sbSizer3 = new wxStaticBoxSizer( new wxStaticBox( m_panel12, wxID_ANY, _("Drag && drop") ), wxHORIZONTAL ); + sbSizer3 = new wxStaticBoxSizer( new wxStaticBox( m_panelTopRight, wxID_ANY, _("Drag && drop") ), wxHORIZONTAL ); + + m_directoryRight = new wxComboBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + sbSizer3->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); - m_comboBoxDirRight = new wxComboBox( m_panel12, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - sbSizer3->Add( m_comboBoxDirRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); + m_dirPickerRight = new wxDirPickerCtrl( m_panelTopRight, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, wxDIRP_DIR_MUST_EXIST ); + m_dirPickerRight->SetToolTip( _("Select a folder") ); - m_dirPickerRight = new wxDirPickerCtrl( m_panel12, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, wxDIRP_DIR_MUST_EXIST ); sbSizer3->Add( m_dirPickerRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer77->Add( sbSizer3, 1, wxRIGHT|wxLEFT, 3 ); bSizer94->Add( bSizer77, 0, wxEXPAND, 5 ); - m_panel12->SetSizer( bSizer94 ); - m_panel12->Layout(); - bSizer94->Fit( m_panel12 ); - bSizer91->Add( m_panel12, 1, wxALIGN_CENTER_VERTICAL, 5 ); + m_panelTopRight->SetSizer( bSizer94 ); + m_panelTopRight->Layout(); + bSizer94->Fit( m_panelTopRight ); + bSizer91->Add( m_panelTopRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); bSizer1->Add( bSizer91, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -337,11 +344,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer2; bSizer2 = new wxBoxSizer( wxHORIZONTAL ); - m_panel1 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer7; bSizer7 = new wxBoxSizer( wxVERTICAL ); - m_gridLeft = new CustomGridLeft( m_panel1, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_gridLeft = new CustomGridLeft( m_panelLeft, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); // Grid m_gridLeft->CreateGrid( 15, 4 ); @@ -367,16 +374,16 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_gridLeft->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTRE ); bSizer7->Add( m_gridLeft, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); - m_panel1->SetSizer( bSizer7 ); - m_panel1->Layout(); - bSizer7->Fit( m_panel1 ); - bSizer2->Add( m_panel1, 1, wxEXPAND, 5 ); + m_panelLeft->SetSizer( bSizer7 ); + m_panelLeft->Layout(); + bSizer7->Fit( m_panelLeft ); + bSizer2->Add( m_panelLeft, 1, wxEXPAND, 5 ); - m_panel3 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelMiddle = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer18; bSizer18 = new wxBoxSizer( wxVERTICAL ); - m_gridMiddle = new CustomGridMiddle( m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_gridMiddle = new CustomGridMiddle( m_panelMiddle, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); // Grid m_gridMiddle->CreateGrid( 15, 1 ); @@ -404,16 +411,16 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_gridMiddle->SetDefaultCellAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); bSizer18->Add( m_gridMiddle, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP|wxBOTTOM, 5 ); - m_panel3->SetSizer( bSizer18 ); - m_panel3->Layout(); - bSizer18->Fit( m_panel3 ); - bSizer2->Add( m_panel3, 0, wxRIGHT|wxLEFT|wxEXPAND, 5 ); + m_panelMiddle->SetSizer( bSizer18 ); + m_panelMiddle->Layout(); + bSizer18->Fit( m_panelMiddle ); + bSizer2->Add( m_panelMiddle, 0, wxRIGHT|wxLEFT|wxEXPAND, 5 ); - m_panel2 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panelRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer10; bSizer10 = new wxBoxSizer( wxVERTICAL ); - m_gridRight = new CustomGridRight( m_panel2, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_gridRight = new CustomGridRight( m_panelRight, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); // Grid m_gridRight->CreateGrid( 15, 4 ); @@ -439,10 +446,10 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_gridRight->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTRE ); bSizer10->Add( m_gridRight, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); - m_panel2->SetSizer( bSizer10 ); - m_panel2->Layout(); - bSizer10->Fit( m_panel2 ); - bSizer2->Add( m_panel2, 1, wxEXPAND, 5 ); + m_panelRight->SetSizer( bSizer10 ); + m_panelRight->Layout(); + bSizer10->Fit( m_panelRight ); + bSizer2->Add( m_panelRight, 1, wxEXPAND, 5 ); bSizer1->Add( bSizer2, 1, wxEXPAND, 5 ); @@ -623,11 +630,12 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const this->Connect( m_menuItemEnglish->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangEnglish ) ); this->Connect( m_menuItemSpanish->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangSpanish ) ); this->Connect( m_menuItemFrench->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangFrench ) ); - this->Connect( m_menuItemHungarian->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangHungarian ) ); this->Connect( m_menuItemItalian->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangItalian ) ); - this->Connect( m_menuItemPolish->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPolish ) ); + this->Connect( m_menuItemHungarian->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangHungarian ) ); this->Connect( m_menuItemDutch->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangDutch ) ); + this->Connect( m_menuItemPolish->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPolish ) ); this->Connect( m_menuItemPortuguese->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPortuguese ) ); + this->Connect( m_menuItemPortugueseBrazil->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPortugueseBrazil ) ); this->Connect( m_menuItemSlovenian->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangSlovenian ) ); this->Connect( m_menuItemJapanese->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangJapanese ) ); this->Connect( m_menuItemChineseSimple->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangChineseSimp ) ); @@ -645,13 +653,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_hyperlinkCfgFilter->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); m_checkBoxHideFilt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnHideFilteredButton ), NULL, this ); m_buttonSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSync ), NULL, this ); - m_comboBoxDirLeft->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); - m_comboBoxDirLeft->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( MainDialogGenerated::OnWriteDirManually ), NULL, this ); + m_directoryLeft->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); m_dirPickerLeft->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_bpButtonSwap->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapDirs ), NULL, this ); m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); - m_comboBoxDirRight->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); - m_comboBoxDirRight->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( MainDialogGenerated::OnWriteDirManually ), NULL, this ); + m_directoryRight->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); m_dirPickerRight->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_gridLeft->Connect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( MainDialogGenerated::OnLeftGridDoubleClick ), NULL, this ); m_gridLeft->Connect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( MainDialogGenerated::OnContextMenu ), NULL, this ); @@ -689,11 +695,12 @@ MainDialogGenerated::~MainDialogGenerated() this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangEnglish ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangSpanish ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangFrench ) ); - this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangHungarian ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangItalian ) ); - this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPolish ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangHungarian ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangDutch ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPolish ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPortuguese ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangPortugueseBrazil ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangSlovenian ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangJapanese ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuLangChineseSimp ) ); @@ -711,13 +718,11 @@ MainDialogGenerated::~MainDialogGenerated() m_hyperlinkCfgFilter->Disconnect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this ); m_checkBoxHideFilt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnHideFilteredButton ), NULL, this ); m_buttonSync->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSync ), NULL, this ); - m_comboBoxDirLeft->Disconnect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); - m_comboBoxDirLeft->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( MainDialogGenerated::OnWriteDirManually ), NULL, this ); + m_directoryLeft->Disconnect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); m_dirPickerLeft->Disconnect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_bpButtonSwap->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapDirs ), NULL, this ); m_bpButtonAddPair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); - m_comboBoxDirRight->Disconnect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); - m_comboBoxDirRight->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( MainDialogGenerated::OnWriteDirManually ), NULL, this ); + m_directoryRight->Disconnect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); m_dirPickerRight->Disconnect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_gridLeft->Disconnect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( MainDialogGenerated::OnLeftGridDoubleClick ), NULL, this ); m_gridLeft->Disconnect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( MainDialogGenerated::OnContextMenu ), NULL, this ); @@ -754,7 +759,9 @@ FolderPairGenerated::FolderPairGenerated( wxWindow* parent, wxWindowID id, const m_directoryLeft = new wxTextCtrl( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); sbSizer21->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); - m_dirPickerLeft = new wxDirPickerCtrl( m_panelLeft, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, wxDIRP_DIR_MUST_EXIST ); + m_dirPickerLeft = new wxDirPickerCtrl( m_panelLeft, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, 0 ); + m_dirPickerLeft->SetToolTip( _("Select a folder") ); + sbSizer21->Add( m_dirPickerLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_panelLeft->SetSizer( sbSizer21 ); @@ -785,7 +792,7 @@ FolderPairGenerated::FolderPairGenerated( wxWindow* parent, wxWindowID id, const m_panel21->SetSizer( bSizer96 ); m_panel21->Layout(); bSizer96->Fit( m_panel21 ); - bSizer95->Add( m_panel21, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + bSizer95->Add( m_panel21, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT|wxLEFT, 5 ); m_panel20->SetSizer( bSizer95 ); m_panel20->Layout(); @@ -809,7 +816,9 @@ FolderPairGenerated::FolderPairGenerated( wxWindow* parent, wxWindowID id, const m_directoryRight = new wxTextCtrl( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); sbSizer23->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); - m_dirPickerRight = new wxDirPickerCtrl( m_panelRight, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, wxDIRP_DIR_MUST_EXIST ); + m_dirPickerRight = new wxDirPickerCtrl( m_panelRight, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, 0 ); + m_dirPickerRight->SetToolTip( _("Select a folder") ); + sbSizer23->Add( m_dirPickerRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer105->Add( sbSizer23, 1, wxALIGN_CENTER_VERTICAL|wxLEFT, 3 ); @@ -822,15 +831,78 @@ FolderPairGenerated::FolderPairGenerated( wxWindow* parent, wxWindowID id, const this->SetSizer( bSizer74 ); this->Layout(); bSizer74->Fit( this ); - - // Connect Events - m_bpButtonRemovePair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FolderPairGenerated::OnRemoveFolderPair ), NULL, this ); } FolderPairGenerated::~FolderPairGenerated() { - // Disconnect Events - m_bpButtonRemovePair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FolderPairGenerated::OnRemoveFolderPair ), NULL, this ); +} + +BatchFolderPairGenerated::BatchFolderPairGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + wxStaticBoxSizer* sbSizer20; + sbSizer20 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxVERTICAL ); + + wxFlexGridSizer* fgSizer9; + fgSizer9 = new wxFlexGridSizer( 2, 2, 5, 5 ); + fgSizer9->AddGrowableCol( 1 ); + fgSizer9->SetFlexibleDirection( wxHORIZONTAL ); + fgSizer9->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText53 = new wxStaticText( this, wxID_ANY, _("Left:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText53->Wrap( -1 ); + m_staticText53->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); + + fgSizer9->Add( m_staticText53, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + + m_panelLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer114; + bSizer114 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryLeft = new wxTextCtrl( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer114->Add( m_directoryLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_dirPickerLeft = new wxDirPickerCtrl( m_panelLeft, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, 0 ); + m_dirPickerLeft->SetToolTip( _("Select a folder") ); + + bSizer114->Add( m_dirPickerLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_panelLeft->SetSizer( bSizer114 ); + m_panelLeft->Layout(); + bSizer114->Fit( m_panelLeft ); + fgSizer9->Add( m_panelLeft, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_staticText541 = new wxStaticText( this, wxID_ANY, _("Right:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText541->Wrap( -1 ); + m_staticText541->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); + + fgSizer9->Add( m_staticText541, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + + m_panelRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer115; + bSizer115 = new wxBoxSizer( wxHORIZONTAL ); + + m_directoryRight = new wxTextCtrl( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer115->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_dirPickerRight = new wxDirPickerCtrl( m_panelRight, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, 0 ); + m_dirPickerRight->SetToolTip( _("Select a folder") ); + + bSizer115->Add( m_dirPickerRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_panelRight->SetSizer( bSizer115 ); + m_panelRight->Layout(); + bSizer115->Fit( m_panelRight ); + fgSizer9->Add( m_panelRight, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + sbSizer20->Add( fgSizer9, 0, wxEXPAND, 5 ); + + this->SetSizer( sbSizer20 ); + this->Layout(); + sbSizer20->Fit( this ); +} + +BatchFolderPairGenerated::~BatchFolderPairGenerated() +{ } BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) @@ -891,13 +963,18 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer69->Add( m_staticText531, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_notebookSettings = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panelOverview = new wxPanel( m_notebookSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer67; bSizer67 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer120; + bSizer120 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer100; bSizer100 = new wxBoxSizer( wxVERTICAL ); - m_scrolledWindow6 = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_scrolledWindow6 = new wxScrolledWindow( m_panelOverview, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); m_scrolledWindow6->SetScrollRate( 5, 5 ); bSizerFolderPairs = new wxBoxSizer( wxVERTICAL ); @@ -919,15 +996,15 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer721 = new wxBoxSizer( wxVERTICAL ); wxStaticBoxSizer* sbSizer6; - sbSizer6 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Compare by...") ), wxVERTICAL ); + sbSizer6 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, _("Compare by...") ), wxVERTICAL ); - m_radioBtnSizeDate = new wxRadioButton( this, wxID_ANY, _("File size and date"), wxDefaultPosition, wxDefaultSize, 0 ); + m_radioBtnSizeDate = new wxRadioButton( m_panelOverview, wxID_ANY, _("File size and date"), wxDefaultPosition, wxDefaultSize, 0 ); m_radioBtnSizeDate->SetValue( true ); m_radioBtnSizeDate->SetToolTip( _("Files are found equal if\n - filesize\n - last write time and date\nare the same.") ); sbSizer6->Add( m_radioBtnSizeDate, 0, 0, 5 ); - m_radioBtnContent = new wxRadioButton( this, wxID_ANY, _("File content"), wxDefaultPosition, wxDefaultSize, 0 ); + m_radioBtnContent = new wxRadioButton( m_panelOverview, wxID_ANY, _("File content"), wxDefaultPosition, wxDefaultSize, 0 ); m_radioBtnContent->SetToolTip( _("Files are found equal if\n - file content\nis the same.") ); sbSizer6->Add( m_radioBtnContent, 0, wxTOP, 5 ); @@ -938,104 +1015,77 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer721->Add( 0, 10, 0, 0, 5 ); wxStaticBoxSizer* sbSizer25; - sbSizer25 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Error handling") ), wxVERTICAL ); + sbSizer25 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, _("Error handling") ), wxVERTICAL ); wxArrayString m_choiceHandleErrorChoices; - m_choiceHandleError = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceHandleErrorChoices, 0 ); + m_choiceHandleError = new wxChoice( m_panelOverview, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceHandleErrorChoices, 0 ); m_choiceHandleError->SetSelection( 0 ); sbSizer25->Add( m_choiceHandleError, 0, wxALL, 5 ); bSizer721->Add( sbSizer25, 1, wxEXPAND, 5 ); + bSizer71->Add( bSizer721, 0, 0, 5 ); - bSizer721->Add( 0, 10, 0, 0, 5 ); - - wxStaticBoxSizer* sbSizer24; - sbSizer24 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxVERTICAL ); - - m_checkBoxUseRecycler = new wxCheckBox( this, wxID_ANY, _("Use Recycle Bin"), wxDefaultPosition, wxDefaultSize, 0 ); - - m_checkBoxUseRecycler->SetToolTip( _("Use Recycle Bin when deleting or overwriting files during synchronization") ); - - sbSizer24->Add( m_checkBoxUseRecycler, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_checkBoxSilent = new wxCheckBox( this, wxID_ANY, _("Silent mode"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer71->Add( 10, 0, 1, wxEXPAND, 5 ); - m_checkBoxSilent->SetToolTip( _("Do not show graphical status and error messages but write to a logfile instead") ); + wxStaticBoxSizer* sbSizer24; + sbSizer24 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, wxEmptyString ), wxVERTICAL ); - sbSizer24->Add( m_checkBoxSilent, 0, wxALL, 5 ); - bSizer721->Add( sbSizer24, 0, wxEXPAND, 5 ); + sbSizer24->Add( 0, 0, 1, wxEXPAND, 5 ); - bSizer71->Add( bSizer721, 0, 0, 5 ); + m_checkBoxUseRecycler = new wxCheckBox( m_panelOverview, wxID_ANY, _("Use Recycle Bin"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer57->Add( bSizer71, 0, wxEXPAND, 5 ); - - - bSizer57->Add( 10, 0, 1, wxEXPAND, 5 ); + m_checkBoxUseRecycler->SetToolTip( _("Use Recycle Bin when deleting or overwriting files during synchronization") ); - wxStaticBoxSizer* sbSizer8; - sbSizer8 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Filter files") ), wxHORIZONTAL ); + sbSizer24->Add( m_checkBoxUseRecycler, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_bpButtonFilter = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW|wxFULL_REPAINT_ON_RESIZE ); - sbSizer8->Add( m_bpButtonFilter, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + m_checkBoxFilter = new wxCheckBox( m_panelOverview, wxID_ANY, _("Filter files"), wxDefaultPosition, wxDefaultSize, 0 ); - wxBoxSizer* bSizer101; - bSizer101 = new wxBoxSizer( wxVERTICAL ); + m_checkBoxFilter->SetToolTip( _("Enable filter to exclude files from synchronization") ); - wxBoxSizer* bSizer671; - bSizer671 = new wxBoxSizer( wxHORIZONTAL ); + sbSizer24->Add( m_checkBoxFilter, 0, wxALL, 5 ); - m_bitmap8 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - m_bitmap8->SetToolTip( _("Include") ); + m_checkBoxSilent = new wxCheckBox( m_panelOverview, wxID_ANY, _("Silent mode"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer671->Add( m_bitmap8, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - - m_textCtrlInclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 250,-1 ), wxTE_MULTILINE ); - bSizer671->Add( m_textCtrlInclude, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - bSizer101->Add( bSizer671, 0, 0, 5 ); + m_checkBoxSilent->SetToolTip( _("Do not display visual status information but write to a logfile instead") ); + sbSizer24->Add( m_checkBoxSilent, 0, wxALL, 5 ); - bSizer101->Add( 0, 10, 0, 0, 5 ); - wxBoxSizer* bSizer691; - bSizer691 = new wxBoxSizer( wxHORIZONTAL ); + sbSizer24->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bitmap9 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - m_bitmap9->SetToolTip( _("Exclude") ); + bSizer71->Add( sbSizer24, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer691->Add( m_bitmap9, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_textCtrlExclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 250,-1 ), wxTE_MULTILINE ); - bSizer691->Add( m_textCtrlExclude, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer71->Add( 150, 0, 0, 0, 5 ); - bSizer101->Add( bSizer691, 0, 0, 5 ); + bSizer57->Add( bSizer71, 0, wxEXPAND, 5 ); - sbSizer8->Add( bSizer101, 1, wxEXPAND, 5 ); - bSizer57->Add( sbSizer8, 0, 0, 5 ); + bSizer57->Add( 10, 0, 1, wxEXPAND, 5 ); bSizer100->Add( bSizer57, 0, 0, 5 ); - bSizer67->Add( bSizer100, 0, 0, 5 ); + bSizer120->Add( bSizer100, 1, 0, 5 ); - bSizer67->Add( 10, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer120->Add( 10, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); wxStaticBoxSizer* sbSizer61; - sbSizer61 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Configuration") ), wxVERTICAL ); + sbSizer61 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, _("Configuration") ), wxVERTICAL ); wxGridSizer* gSizer3; gSizer3 = new wxGridSizer( 1, 2, 0, 5 ); - m_staticText211 = new wxStaticText( this, wxID_ANY, _("Result"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText211 = new wxStaticText( m_panelOverview, wxID_ANY, _("Result"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText211->Wrap( -1 ); m_staticText211->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); gSizer3->Add( m_staticText211, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_staticText311 = new wxStaticText( this, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText311 = new wxStaticText( m_panelOverview, wxID_ANY, _("Action"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText311->Wrap( -1 ); m_staticText311->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); @@ -1043,60 +1093,159 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS sbSizer61->Add( gSizer3, 0, wxEXPAND, 5 ); - m_staticline3 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + m_staticline3 = new wxStaticLine( m_panelOverview, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); sbSizer61->Add( m_staticline3, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP|wxBOTTOM, 5 ); wxGridSizer* gSizer1; gSizer1 = new wxGridSizer( 5, 2, 0, 5 ); - m_bitmap13 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmap13 = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); m_bitmap13->SetToolTip( _("Files/folders that exist on left side only") ); gSizer1->Add( m_bitmap13, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButton5 = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButton5 = new wxBitmapButton( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); gSizer1->Add( m_bpButton5, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmap14 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmap14 = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); m_bitmap14->SetToolTip( _("Files/folders that exist on right side only") ); gSizer1->Add( m_bitmap14, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButton6 = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButton6 = new wxBitmapButton( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); gSizer1->Add( m_bpButton6, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmap15 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmap15 = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); m_bitmap15->SetToolTip( _("Files that exist on both sides, left one is newer") ); gSizer1->Add( m_bitmap15, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButton7 = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButton7 = new wxBitmapButton( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); gSizer1->Add( m_bpButton7, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmap16 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmap16 = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); m_bitmap16->SetToolTip( _("Files that exist on both sides, right one is newer") ); gSizer1->Add( m_bitmap16, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButton8 = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButton8 = new wxBitmapButton( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); gSizer1->Add( m_bpButton8, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmap17 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmap17 = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); m_bitmap17->SetToolTip( _("dummy") ); gSizer1->Add( m_bitmap17, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButton9 = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButton9 = new wxBitmapButton( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); gSizer1->Add( m_bpButton9, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); sbSizer61->Add( gSizer1, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - bSizer67->Add( sbSizer61, 0, 0, 5 ); + bSizer120->Add( sbSizer61, 0, 0, 5 ); + + bSizer67->Add( bSizer120, 1, wxALL, 10 ); + + m_panelOverview->SetSizer( bSizer67 ); + m_panelOverview->Layout(); + bSizer67->Fit( m_panelOverview ); + m_notebookSettings->AddPage( m_panelOverview, _("Overview"), true ); + m_panelFilter = new wxPanel( m_notebookSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer114; + bSizer114 = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sbSizer8; + sbSizer8 = new wxStaticBoxSizer( new wxStaticBox( m_panelFilter, wxID_ANY, wxEmptyString ), wxVERTICAL ); + + wxFlexGridSizer* fgSizer3; + fgSizer3 = new wxFlexGridSizer( 2, 2, 0, 0 ); + fgSizer3->AddGrowableCol( 1 ); + fgSizer3->AddGrowableRow( 1 ); + fgSizer3->SetFlexibleDirection( wxBOTH ); + fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + + fgSizer3->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticText15 = new wxStaticText( m_panelFilter, wxID_ANY, _("Include"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText15->Wrap( -1 ); + m_staticText15->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + + fgSizer3->Add( m_staticText15, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_bitmap8 = new wxStaticBitmap( m_panelFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + m_bitmap8->SetToolTip( _("Include") ); + + fgSizer3->Add( m_bitmap8, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_textCtrlInclude = new wxTextCtrl( m_panelFilter, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + fgSizer3->Add( m_textCtrlInclude, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + sbSizer8->Add( fgSizer3, 0, wxEXPAND, 5 ); + + wxFlexGridSizer* fgSizer4; + fgSizer4 = new wxFlexGridSizer( 2, 2, 0, 0 ); + fgSizer4->AddGrowableCol( 1 ); + fgSizer4->AddGrowableRow( 1 ); + fgSizer4->SetFlexibleDirection( wxBOTH ); + fgSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + + fgSizer4->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticText16 = new wxStaticText( m_panelFilter, wxID_ANY, _("Exclude"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText16->Wrap( -1 ); + m_staticText16->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + + fgSizer4->Add( m_staticText16, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_bitmap9 = new wxStaticBitmap( m_panelFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + m_bitmap9->SetToolTip( _("Exclude") ); - bSizer69->Add( bSizer67, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + fgSizer4->Add( m_bitmap9, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_staticline9 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer69->Add( m_staticline9, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + m_textCtrlExclude = new wxTextCtrl( m_panelFilter, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + fgSizer4->Add( m_textCtrlExclude, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + sbSizer8->Add( fgSizer4, 0, wxEXPAND, 5 ); + + bSizer114->Add( sbSizer8, 1, wxALL|wxEXPAND, 10 ); + + m_panelFilter->SetSizer( bSizer114 ); + m_panelFilter->Layout(); + bSizer114->Fit( m_panelFilter ); + m_notebookSettings->AddPage( m_panelFilter, _("Filter"), false ); + m_panelLogging = new wxPanel( m_notebookSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer117; + bSizer117 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer119; + bSizer119 = new wxBoxSizer( wxVERTICAL ); + + m_staticText120 = new wxStaticText( m_panelLogging, wxID_ANY, _("Select logfile directory:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText120->Wrap( -1 ); + bSizer119->Add( m_staticText120, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxStaticBoxSizer* sbSizer251; + sbSizer251 = new wxStaticBoxSizer( new wxStaticBox( m_panelLogging, wxID_ANY, _("Drag && drop") ), wxHORIZONTAL ); + + m_textCtrlLogfileDir = new wxTextCtrl( m_panelLogging, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + sbSizer251->Add( m_textCtrlLogfileDir, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_dirPickerLogfileDir = new wxDirPickerCtrl( m_panelLogging, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, 0 ); + m_dirPickerLogfileDir->SetToolTip( _("Select a folder") ); + + sbSizer251->Add( m_dirPickerLogfileDir, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer119->Add( sbSizer251, 1, wxEXPAND, 5 ); + + bSizer117->Add( bSizer119, 0, wxEXPAND|wxALL, 10 ); + + m_panelLogging->SetSizer( bSizer117 ); + m_panelLogging->Layout(); + bSizer117->Fit( m_panelLogging ); + m_notebookSettings->AddPage( m_panelLogging, _("Logging"), false ); + + bSizer69->Add( m_notebookSettings, 1, wxEXPAND, 5 ); wxBoxSizer* bSizer68; bSizer68 = new wxBoxSizer( wxHORIZONTAL ); @@ -1119,7 +1268,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer69->Add( bSizer68, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - bSizer54->Add( bSizer69, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + bSizer54->Add( bSizer69, 1, wxALIGN_CENTER_HORIZONTAL|wxALL|wxEXPAND, 5 ); this->SetSizer( bSizer54 ); this->Layout(); @@ -1133,7 +1282,8 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_radioBtnContent->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeCompareVar ), NULL, this ); m_choiceHandleError->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeErrorHandling ), NULL, this ); m_checkBoxUseRecycler->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnSelectRecycleBin ), NULL, this ); - m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnFilterButton ), NULL, this ); + m_checkBoxFilter->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCheckFilter ), NULL, this ); + m_checkBoxSilent->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCheckLogging ), NULL, this ); m_bpButton5->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnExLeftSideOnly ), NULL, this ); m_bpButton6->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnExRightSideOnly ), NULL, this ); m_bpButton7->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnLeftNewer ), NULL, this ); @@ -1152,7 +1302,8 @@ BatchDlgGenerated::~BatchDlgGenerated() m_radioBtnContent->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeCompareVar ), NULL, this ); m_choiceHandleError->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeErrorHandling ), NULL, this ); m_checkBoxUseRecycler->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnSelectRecycleBin ), NULL, this ); - m_bpButtonFilter->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnFilterButton ), NULL, this ); + m_checkBoxFilter->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCheckFilter ), NULL, this ); + m_checkBoxSilent->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCheckLogging ), NULL, this ); m_bpButton5->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnExLeftSideOnly ), NULL, this ); m_bpButton6->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnExRightSideOnly ), NULL, this ); m_bpButton7->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnLeftNewer ), NULL, this ); @@ -1163,53 +1314,6 @@ BatchDlgGenerated::~BatchDlgGenerated() m_button6->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCancel ), NULL, this ); } -BatchFolderPairGenerated::BatchFolderPairGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) -{ - wxStaticBoxSizer* sbSizer20; - sbSizer20 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxVERTICAL ); - - wxFlexGridSizer* fgSizer9; - fgSizer9 = new wxFlexGridSizer( 2, 2, 5, 5 ); - fgSizer9->AddGrowableCol( 1 ); - fgSizer9->SetFlexibleDirection( wxHORIZONTAL ); - fgSizer9->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - - m_staticText53 = new wxStaticText( this, wxID_ANY, _("Left folder:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText53->Wrap( -1 ); - m_staticText53->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); - - fgSizer9->Add( m_staticText53, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - - m_directoryLeft = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizer9->Add( m_directoryLeft, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - m_staticText541 = new wxStaticText( this, wxID_ANY, _("Right folder:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText541->Wrap( -1 ); - m_staticText541->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); - - fgSizer9->Add( m_staticText541, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - - m_directoryRight = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgSizer9->Add( m_directoryRight, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - sbSizer20->Add( fgSizer9, 0, wxEXPAND, 5 ); - - this->SetSizer( sbSizer20 ); - this->Layout(); - sbSizer20->Fit( this ); - - // Connect Events - m_directoryLeft->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BatchFolderPairGenerated::OnEnterLeftDir ), NULL, this ); - m_directoryRight->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BatchFolderPairGenerated::OnEnterRightDir ), NULL, this ); -} - -BatchFolderPairGenerated::~BatchFolderPairGenerated() -{ - // Disconnect Events - m_directoryLeft->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BatchFolderPairGenerated::OnEnterLeftDir ), NULL, this ); - m_directoryRight->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( BatchFolderPairGenerated::OnEnterRightDir ), NULL, this ); -} - CompareStatusGenerated::CompareStatusGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) { wxBoxSizer* bSizer40; @@ -1220,55 +1324,84 @@ CompareStatusGenerated::CompareStatusGenerated( wxWindow* parent, wxWindowID id, wxStaticBoxSizer* sbSizer10; sbSizer10 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxHORIZONTAL ); + wxBoxSizer* bSizer118; + bSizer118 = new wxBoxSizer( wxHORIZONTAL ); + m_staticText321 = new wxStaticText( this, wxID_ANY, _("Files/folders scanned:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText321->Wrap( -1 ); - m_staticText321->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + m_staticText321->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Arial") ) ); - sbSizer10->Add( m_staticText321, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 ); + bSizer118->Add( m_staticText321, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 5 ); m_staticTextScanned = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextScanned->Wrap( -1 ); - m_staticTextScanned->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticTextScanned->SetFont( wxFont( 9, 74, 90, 92, false, wxT("Arial") ) ); + + bSizer118->Add( m_staticTextScanned, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); - sbSizer10->Add( m_staticTextScanned, 0, wxALIGN_CENTER_VERTICAL, 5 ); + sbSizer10->Add( bSizer118, 0, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer42->Add( sbSizer10, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + bSizer42->Add( sbSizer10, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); sbSizer13 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Comparing content") ), wxVERTICAL ); - wxFlexGridSizer* fgSizer8; - fgSizer8 = new wxFlexGridSizer( 2, 2, 3, 0 ); - fgSizer8->SetFlexibleDirection( wxBOTH ); - fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + wxFlexGridSizer* fgSizer12; + fgSizer12 = new wxFlexGridSizer( 2, 4, 3, 5 ); + fgSizer12->SetFlexibleDirection( wxBOTH ); + fgSizer12->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); m_staticText46 = new wxStaticText( this, wxID_ANY, _("Files remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText46->Wrap( -1 ); - m_staticText46->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + m_staticText46->SetFont( wxFont( 8, 74, 90, 90, false, wxT("Arial") ) ); - fgSizer8->Add( m_staticText46, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + fgSizer12->Add( m_staticText46, 0, wxALIGN_BOTTOM, 5 ); - m_staticTextFilesToCompare = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextFilesToCompare->Wrap( -1 ); - m_staticTextFilesToCompare->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticTextFilesRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFilesRemaining->Wrap( -1 ); + m_staticTextFilesRemaining->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); - fgSizer8->Add( m_staticTextFilesToCompare, 0, wxALIGN_CENTER_VERTICAL, 5 ); + fgSizer12->Add( m_staticTextFilesRemaining, 0, wxALIGN_BOTTOM, 5 ); m_staticText32 = new wxStaticText( this, wxID_ANY, _("Data remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText32->Wrap( -1 ); - m_staticText32->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + m_staticText32->SetFont( wxFont( 8, 74, 90, 90, false, wxT("Arial") ) ); + + fgSizer12->Add( m_staticText32, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextDataRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextDataRemaining->Wrap( -1 ); + m_staticTextDataRemaining->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); - fgSizer8->Add( m_staticText32, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + fgSizer12->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM, 5 ); - m_staticTextDataToCompare = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextDataToCompare->Wrap( -1 ); - m_staticTextDataToCompare->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticText104 = new wxStaticText( this, wxID_ANY, _("Speed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText104->Wrap( -1 ); + m_staticText104->SetFont( wxFont( 8, 74, 90, 90, false, wxT("Arial") ) ); - fgSizer8->Add( m_staticTextDataToCompare, 0, wxALIGN_CENTER_VERTICAL, 5 ); + fgSizer12->Add( m_staticText104, 0, wxALIGN_BOTTOM, 5 ); - sbSizer13->Add( fgSizer8, 0, 0, 5 ); + m_staticTextSpeed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextSpeed->Wrap( -1 ); + m_staticTextSpeed->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); + + fgSizer12->Add( m_staticTextSpeed, 0, wxALIGN_BOTTOM, 5 ); + + m_staticText103 = new wxStaticText( this, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText103->Wrap( -1 ); + m_staticText103->SetFont( wxFont( 8, 74, 90, 90, false, wxT("Arial") ) ); + + fgSizer12->Add( m_staticText103, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextTimeRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeRemaining->Wrap( -1 ); + m_staticTextTimeRemaining->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); + + fgSizer12->Add( m_staticTextTimeRemaining, 0, wxALIGN_BOTTOM, 5 ); + + sbSizer13->Add( fgSizer12, 1, wxEXPAND, 5 ); bSizer42->Add( sbSizer13, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -1278,17 +1411,22 @@ CompareStatusGenerated::CompareStatusGenerated( wxWindow* parent, wxWindowID id, wxStaticBoxSizer* sbSizer131; sbSizer131 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxHORIZONTAL ); + wxBoxSizer* bSizer117; + bSizer117 = new wxBoxSizer( wxHORIZONTAL ); + m_staticText37 = new wxStaticText( this, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText37->Wrap( -1 ); - m_staticText37->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + m_staticText37->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Arial") ) ); - sbSizer131->Add( m_staticText37, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + bSizer117->Add( m_staticText37, 0, wxALIGN_BOTTOM, 5 ); m_staticTextTimeElapsed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextTimeElapsed->Wrap( -1 ); - m_staticTextTimeElapsed->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticTextTimeElapsed->SetFont( wxFont( 9, 74, 90, 92, false, wxT("Arial") ) ); - sbSizer131->Add( m_staticTextTimeElapsed, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer117->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM|wxLEFT, 5 ); + + sbSizer131->Add( bSizer117, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer42->Add( sbSizer131, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); @@ -1623,7 +1761,7 @@ SyncDlgGenerated::SyncDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr sbSizer6->Add( gSizer1, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - bSizer181->Add( sbSizer6, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_BOTTOM, 5 ); + bSizer181->Add( sbSizer6, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); bSizer7->Add( bSizer181, 0, wxALL, 5 ); @@ -1689,7 +1827,7 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, bSizer37 = new wxBoxSizer( wxHORIZONTAL ); m_animationControl1 = new wxAnimationCtrl(this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 45,45 )); - bSizer37->Add( m_animationControl1, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer37->Add( m_animationControl1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxTAB_TRAVERSAL ); m_panel8->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) ); @@ -1706,7 +1844,7 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, m_panel8->SetSizer( bSizer72 ); m_panel8->Layout(); bSizer72->Fit( m_panel8 ); - bSizer37->Add( m_panel8, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer37->Add( m_panel8, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); bSizer27->Add( bSizer37, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -1736,51 +1874,72 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, m_staticText21->Wrap( -1 ); m_staticText21->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); - bSizer31->Add( m_staticText21, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer31->Add( m_staticText21, 0, wxALIGN_BOTTOM, 5 ); bSizer31->Add( 0, 0, 1, wxEXPAND, 5 ); m_staticText55 = new wxStaticText( this, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText55->Wrap( -1 ); - m_staticText55->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); + m_staticText55->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); - bSizer31->Add( m_staticText55, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + bSizer31->Add( m_staticText55, 0, wxALIGN_BOTTOM, 5 ); m_staticTextTimeElapsed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextTimeElapsed->Wrap( -1 ); - m_staticTextTimeElapsed->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticTextTimeElapsed->SetFont( wxFont( 9, 74, 90, 92, false, wxT("Arial") ) ); - bSizer31->Add( m_staticTextTimeElapsed, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer31->Add( m_staticTextTimeElapsed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - bSizer27->Add( bSizer31, 0, wxEXPAND, 5 ); + bSizer27->Add( bSizer31, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); m_textCtrlInfo = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY ); m_textCtrlInfo->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ) ); - bSizer27->Add( m_textCtrlInfo, 3, wxALL|wxEXPAND, 5 ); + bSizer27->Add( m_textCtrlInfo, 3, wxEXPAND|wxALL, 5 ); m_gauge1 = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxSize( -1,20 ), wxGA_HORIZONTAL ); - bSizer27->Add( m_gauge1, 0, wxALL|wxEXPAND, 5 ); + bSizer27->Add( m_gauge1, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); bSizer28 = new wxBoxSizer( wxHORIZONTAL ); - wxBoxSizer* bSizer33; - bSizer33 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer111; + bSizer111 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer113; + bSizer113 = new wxBoxSizer( wxHORIZONTAL ); m_staticText25 = new wxStaticText( this, wxID_ANY, _("Files/folders remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText25->Wrap( -1 ); - m_staticText25->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); + m_staticText25->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); - bSizer33->Add( m_staticText25, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + bSizer113->Add( m_staticText25, 0, wxALIGN_BOTTOM, 5 ); m_staticTextRemainingObj = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); m_staticTextRemainingObj->Wrap( -1 ); - m_staticTextRemainingObj->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticTextRemainingObj->SetFont( wxFont( 9, 74, 90, 92, false, wxT("Arial") ) ); - bSizer33->Add( m_staticTextRemainingObj, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer113->Add( m_staticTextRemainingObj, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - bSizer28->Add( bSizer33, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer111->Add( bSizer113, 0, 0, 5 ); + + bSizerSpeed = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText108 = new wxStaticText( this, wxID_ANY, _("Speed:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText108->Wrap( -1 ); + m_staticText108->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); + + bSizerSpeed->Add( m_staticText108, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextSpeed = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextSpeed->Wrap( -1 ); + m_staticTextSpeed->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); + + bSizerSpeed->Add( m_staticTextSpeed, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + bSizer111->Add( bSizerSpeed, 0, wxTOP, 5 ); + + bSizer28->Add( bSizer111, 0, wxALIGN_CENTER_VERTICAL, 5 ); bSizer28->Add( 0, 0, 1, 0, 5 ); @@ -1794,35 +1953,55 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id, m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( 100,32 ), 0 ); m_buttonPause->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); - bSizer28->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + bSizer28->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); m_buttonAbort = new wxButton( this, wxID_CANCEL, _("&Abort"), wxDefaultPosition, wxSize( 100,32 ), 0 ); - m_buttonAbort->SetDefault(); m_buttonAbort->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); - bSizer28->Add( m_buttonAbort, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + bSizer28->Add( m_buttonAbort, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); bSizer28->Add( 0, 0, 1, 0, 5 ); - wxBoxSizer* bSizer32; - bSizer32 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer114; + bSizer114 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer116; + bSizer116 = new wxBoxSizer( wxHORIZONTAL ); m_staticText26 = new wxStaticText( this, wxID_ANY, _("Data remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText26->Wrap( -1 ); - m_staticText26->SetFont( wxFont( 10, 74, 93, 90, false, wxT("Tahoma") ) ); + m_staticText26->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); - bSizer32->Add( m_staticText26, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + bSizer116->Add( m_staticText26, 0, wxALIGN_BOTTOM, 5 ); m_staticTextDataRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextDataRemaining->Wrap( -1 ); - m_staticTextDataRemaining->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); + m_staticTextDataRemaining->SetFont( wxFont( 9, 74, 90, 92, false, wxT("Arial") ) ); + + bSizer116->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); - bSizer32->Add( m_staticTextDataRemaining, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + bSizer114->Add( bSizer116, 0, wxALIGN_RIGHT, 5 ); - bSizer28->Add( bSizer32, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizerRemTime = new wxBoxSizer( wxHORIZONTAL ); - bSizer27->Add( bSizer28, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + m_staticText106 = new wxStaticText( this, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText106->Wrap( -1 ); + m_staticText106->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); + + bSizerRemTime->Add( m_staticText106, 0, wxALIGN_BOTTOM, 5 ); + + m_staticTextTimeRemaining = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextTimeRemaining->Wrap( -1 ); + m_staticTextTimeRemaining->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Arial") ) ); + + bSizerRemTime->Add( m_staticTextTimeRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 ); + + bSizer114->Add( bSizerRemTime, 0, wxALIGN_RIGHT|wxTOP, 5 ); + + bSizer28->Add( bSizer114, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer27->Add( bSizer28, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 5 ); bSizer27->Add( 0, 5, 0, wxEXPAND, 5 ); @@ -2110,10 +2289,13 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer72->Add( 0, 5, 0, 0, 5 ); wxFlexGridSizer* fgSizer9; - fgSizer9 = new wxFlexGridSizer( 1, 2, 5, 20 ); + fgSizer9 = new wxFlexGridSizer( 1, 3, 5, 20 ); fgSizer9->SetFlexibleDirection( wxBOTH ); fgSizer9->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + m_bitmapFrench = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapFrench, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + m_staticText68 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Jean-François Hartmann"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText68->Wrap( -1 ); fgSizer9->Add( m_staticText68, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2122,6 +2304,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText69->Wrap( -1 ); fgSizer9->Add( m_staticText69, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapJapanese = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapJapanese, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_staticText70 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Tilt"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText70->Wrap( -1 ); fgSizer9->Add( m_staticText70, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2130,6 +2315,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText71->Wrap( -1 ); fgSizer9->Add( m_staticText71, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapDutch = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapDutch, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + m_staticText711 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("M.D. Vrakking"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText711->Wrap( -1 ); fgSizer9->Add( m_staticText711, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2138,6 +2326,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText712->Wrap( -1 ); fgSizer9->Add( m_staticText712, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapChineseSimple = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapChineseSimple, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_staticText91 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Misty Wu"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText91->Wrap( -1 ); fgSizer9->Add( m_staticText91, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2146,14 +2337,20 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText92->Wrap( -1 ); fgSizer9->Add( m_staticText92, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapPolish = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapPolish, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + m_staticText911 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Wojtek Pietruszewski"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText911->Wrap( -1 ); fgSizer9->Add( m_staticText911, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_staticText921 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Język Polski"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText921 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Polski"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText921->Wrap( -1 ); fgSizer9->Add( m_staticText921, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapPortuguese = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapPortuguese, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_staticText9211 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("QuestMark"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText9211->Wrap( -1 ); fgSizer9->Add( m_staticText9211, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2162,6 +2359,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText9212->Wrap( -1 ); fgSizer9->Add( m_staticText9212, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapItalian = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapItalian, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + m_staticText92121 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Emmo"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText92121->Wrap( -1 ); fgSizer9->Add( m_staticText92121, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2170,6 +2370,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText92122->Wrap( -1 ); fgSizer9->Add( m_staticText92122, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapSlovenian = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapSlovenian, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + m_staticText921221 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Matej Badalic"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText921221->Wrap( -1 ); fgSizer9->Add( m_staticText921221, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2178,14 +2381,20 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText921222->Wrap( -1 ); fgSizer9->Add( m_staticText921222, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapHungarian = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapHungarian, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + m_staticText682 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Demon"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText682->Wrap( -1 ); fgSizer9->Add( m_staticText682, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_staticText681 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Magyar Nyelv"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText681 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Magyar"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText681->Wrap( -1 ); fgSizer9->Add( m_staticText681, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapSpanish = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapSpanish, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); + m_staticText683 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("David Rodríguez"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText683->Wrap( -1 ); fgSizer9->Add( m_staticText683, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2194,6 +2403,17 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_staticText691->Wrap( -1 ); fgSizer9->Add( m_staticText691, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bitmapPortugueseBrazil = new wxStaticBitmap( m_scrolledWindow3, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,11 ), 0 ); + fgSizer9->Add( m_bitmapPortugueseBrazil, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText684 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Edison Aranha"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText684->Wrap( -1 ); + fgSizer9->Add( m_staticText684, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText685 = new wxStaticText( m_scrolledWindow3, wxID_ANY, _("Português do Brasil"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText685->Wrap( -1 ); + fgSizer9->Add( m_staticText685, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bSizer72->Add( fgSizer9, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); m_scrolledWindow3->SetSizer( bSizer72 ); @@ -2457,6 +2677,78 @@ WarningDlgGenerated::~WarningDlgGenerated() m_buttonAbort->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( WarningDlgGenerated::OnAbort ), NULL, this ); } +QuestionDlgGenerated::QuestionDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizer24; + bSizer24 = new wxBoxSizer( wxVERTICAL ); + + + bSizer24->Add( 0, 10, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer26; + bSizer26 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmap10 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + bSizer26->Add( m_bitmap10, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_textCtrl8 = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE|wxTE_READONLY ); + m_textCtrl8->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ) ); + + bSizer26->Add( m_textCtrl8, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + bSizer24->Add( bSizer26, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM, 5 ); + + m_checkBoxDontAskAgain = new wxCheckBox( this, wxID_ANY, _("Don't ask me again"), wxDefaultPosition, wxDefaultSize, 0 ); + + bSizer24->Add( m_checkBoxDontAskAgain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + + bSizer24->Add( 0, 10, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer25; + bSizer25 = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonYes = new wxButton( this, wxID_YES, _("&Yes"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonYes->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + + bSizer25->Add( m_buttonYes, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); + + m_buttonNo = new wxButton( this, wxID_NO, _("&No"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonNo->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + + bSizer25->Add( m_buttonNo, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_buttonCancel->SetFont( wxFont( 10, 74, 90, 90, false, wxT("Tahoma") ) ); + + bSizer25->Add( m_buttonCancel, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); + + + bSizer25->Add( 5, 0, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer24->Add( bSizer25, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + this->SetSizer( bSizer24 ); + this->Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( QuestionDlgGenerated::OnClose ) ); + m_buttonYes->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( QuestionDlgGenerated::OnYes ), NULL, this ); + m_buttonNo->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( QuestionDlgGenerated::OnNo ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( QuestionDlgGenerated::OnCancel ), NULL, this ); +} + +QuestionDlgGenerated::~QuestionDlgGenerated() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( QuestionDlgGenerated::OnClose ) ); + m_buttonYes->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( QuestionDlgGenerated::OnYes ), NULL, this ); + m_buttonNo->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( QuestionDlgGenerated::OnNo ), NULL, this ); + m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( QuestionDlgGenerated::OnCancel ), NULL, this ); +} + DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { this->SetSizeHints( wxDefaultSize, wxDefaultSize ); @@ -2583,9 +2875,6 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer21->Add( bSizer86, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM, 5 ); - - bSizer21->Add( 0, 0, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - wxBoxSizer* bSizer70; bSizer70 = new wxBoxSizer( wxHORIZONTAL ); @@ -2600,7 +2889,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer70->Add( m_bpButtonHelp, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - bSizer21->Add( bSizer70, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP, 5 ); + bSizer21->Add( bSizer70, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 10 ); bSizer21->Add( 0, 5, 0, 0, 5 ); @@ -2633,11 +2922,11 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticText85->Wrap( -1 ); bSizer52->Add( m_staticText85, 0, 0, 5 ); - m_staticText86 = new wxStaticText( m_panel13, wxID_ANY, _("4. Keep the number of entries small for best performance."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText86 = new wxStaticText( m_panel13, wxID_ANY, _("4. Keep the number of (different) entries small for best performance."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText86->Wrap( -1 ); bSizer52->Add( m_staticText86, 0, wxBOTTOM, 5 ); - bSizer69->Add( bSizer52, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + bSizer69->Add( bSizer52, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 10 ); wxStaticBoxSizer* sbSizer21; sbSizer21 = new wxStaticBoxSizer( new wxStaticBox( m_panel13, wxID_ANY, _("Example") ), wxVERTICAL ); @@ -2669,6 +2958,8 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w wxFlexGridSizer* fgSizer3; fgSizer3 = new wxFlexGridSizer( 2, 2, 0, 0 ); + fgSizer3->AddGrowableCol( 1 ); + fgSizer3->AddGrowableRow( 1 ); fgSizer3->SetFlexibleDirection( wxBOTH ); fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); @@ -2682,15 +2973,17 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w fgSizer3->Add( m_staticText15, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); m_bitmap8 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - fgSizer3->Add( m_bitmap8, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); + fgSizer3->Add( m_bitmap8, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_textCtrlInclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 400,-1 ), wxTE_MULTILINE ); - fgSizer3->Add( m_textCtrlInclude, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + m_textCtrlInclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + fgSizer3->Add( m_textCtrlInclude, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - sbSizer8->Add( fgSizer3, 0, 0, 5 ); + sbSizer8->Add( fgSizer3, 0, wxEXPAND, 5 ); wxFlexGridSizer* fgSizer4; fgSizer4 = new wxFlexGridSizer( 2, 2, 0, 0 ); + fgSizer4->AddGrowableCol( 1 ); + fgSizer4->AddGrowableRow( 1 ); fgSizer4->SetFlexibleDirection( wxBOTH ); fgSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); @@ -2704,14 +2997,14 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w fgSizer4->Add( m_staticText16, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); m_bitmap9 = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); - fgSizer4->Add( m_bitmap9, 0, wxTOP|wxBOTTOM|wxLEFT, 5 ); + fgSizer4->Add( m_bitmap9, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_textCtrlExclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 400,-1 ), wxTE_MULTILINE ); - fgSizer4->Add( m_textCtrlExclude, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + m_textCtrlExclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + fgSizer4->Add( m_textCtrlExclude, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - sbSizer8->Add( fgSizer4, 0, 0, 5 ); + sbSizer8->Add( fgSizer4, 0, wxEXPAND, 5 ); - bSizer21->Add( sbSizer8, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + bSizer21->Add( sbSizer8, 1, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); bSizer21->Add( 0, 0, 0, 0, 5 ); @@ -2889,7 +3182,7 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind wxBoxSizer* bSizer100; bSizer100 = new wxBoxSizer( wxHORIZONTAL ); - m_staticText99 = new wxStaticText( this, wxID_ANY, _("File Time Tolerance:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText99 = new wxStaticText( this, wxID_ANY, _("File Time tolerance (seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText99->Wrap( -1 ); m_staticText99->SetToolTip( _("File times that differ by up to the specified number of seconds are still handled as having same time.") ); diff --git a/ui/guiGenerated.h b/ui/guiGenerated.h index 394fa3e7..4bf190e1 100644 --- a/ui/guiGenerated.h +++ b/ui/guiGenerated.h @@ -42,11 +42,11 @@ class wxButtonWithImage; #include <wx/statline.h> #include <wx/frame.h> #include <wx/textctrl.h> +#include <wx/notebook.h> #include <wx/dialog.h> #include <wx/gauge.h> #include <wx/animate.h> #include <wx/treectrl.h> -#include <wx/notebook.h> #include <wx/checklst.h> #include <wx/spinctrl.h> @@ -62,26 +62,27 @@ class MainDialogGenerated : public wxFrame protected: wxMenuBar* m_menubar1; - wxMenu* m_menu1; + wxMenu* m_menuFile; wxMenuItem* m_menuItem10; wxMenuItem* m_menuItem11; - wxMenu* m_menu3; - wxMenu* m_menu31; + wxMenu* m_menuAdvanced; + wxMenu* m_menuLanguages; wxMenuItem* m_menuItemGerman; wxMenuItem* m_menuItemEnglish; wxMenuItem* m_menuItemSpanish; wxMenuItem* m_menuItemFrench; - wxMenuItem* m_menuItemHungarian; wxMenuItem* m_menuItemItalian; - wxMenuItem* m_menuItemPolish; + wxMenuItem* m_menuItemHungarian; wxMenuItem* m_menuItemDutch; + wxMenuItem* m_menuItemPolish; wxMenuItem* m_menuItemPortuguese; + wxMenuItem* m_menuItemPortugueseBrazil; wxMenuItem* m_menuItemSlovenian; wxMenuItem* m_menuItemJapanese; wxMenuItem* m_menuItemChineseSimple; wxMenuItem* m_menuItemGlobSett; wxMenuItem* m_menuItem7; - wxMenu* m_menu33; + wxMenu* m_menuHelp; wxMenuItem* m_menuItemAbout; wxBoxSizer* bSizer1; wxPanel* m_panel71; @@ -100,29 +101,29 @@ class MainDialogGenerated : public wxFrame wxButtonWithImage* m_buttonSync; - wxPanel* m_panel11; + wxPanel* m_panelTopLeft; wxStaticBoxSizer* sbSizer2; - wxComboBox* m_comboBoxDirLeft; + wxComboBox* m_directoryLeft; wxDirPickerCtrl* m_dirPickerLeft; - wxPanel* m_panel13; + wxPanel* m_panelTopMiddle; wxBoxSizer* bSizerMiddle; wxBitmapButton* m_bpButtonSwap; - wxPanel* m_panel12; + wxPanel* m_panelTopRight; wxBitmapButton* m_bpButtonAddPair; - wxComboBox* m_comboBoxDirRight; + wxComboBox* m_directoryRight; wxDirPickerCtrl* m_dirPickerRight; wxBoxSizer* bSizer106; wxStaticBitmap* m_bitmapShift; wxScrolledWindow* m_scrolledWindowFolderPairs; wxBoxSizer* bSizerFolderPairs; - wxPanel* m_panel1; + wxPanel* m_panelLeft; CustomGridLeft* m_gridLeft; - wxPanel* m_panel3; + wxPanel* m_panelMiddle; CustomGridMiddle* m_gridMiddle; - wxPanel* m_panel2; + wxPanel* m_panelRight; CustomGridRight* m_gridRight; wxBoxSizer* bSizer3; wxBoxSizer* bSizer58; @@ -165,11 +166,12 @@ class MainDialogGenerated : public wxFrame virtual void OnMenuLangEnglish( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangSpanish( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangFrench( wxCommandEvent& event ){ event.Skip(); } - virtual void OnMenuLangHungarian( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangItalian( wxCommandEvent& event ){ event.Skip(); } - virtual void OnMenuLangPolish( wxCommandEvent& event ){ event.Skip(); } + virtual void OnMenuLangHungarian( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangDutch( wxCommandEvent& event ){ event.Skip(); } + virtual void OnMenuLangPolish( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangPortuguese( wxCommandEvent& event ){ event.Skip(); } + virtual void OnMenuLangPortugueseBrazil( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangSlovenian( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangJapanese( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuLangChineseSimp( wxCommandEvent& event ){ event.Skip(); } @@ -186,7 +188,6 @@ class MainDialogGenerated : public wxFrame virtual void OnConfigureFilter( wxHyperlinkEvent& event ){ event.Skip(); } virtual void OnHideFilteredButton( wxCommandEvent& event ){ event.Skip(); } virtual void OnFolderHistoryKeyEvent( wxKeyEvent& event ){ event.Skip(); } - virtual void OnWriteDirManually( wxCommandEvent& event ){ event.Skip(); } virtual void OnDirSelected( wxFileDirPickerEvent& event ){ event.Skip(); } virtual void OnSwapDirs( wxCommandEvent& event ){ event.Skip(); } virtual void OnAddFolderPair( wxCommandEvent& event ){ event.Skip(); } @@ -229,10 +230,6 @@ class FolderPairGenerated : public wxPanel wxPanel* m_panel20; wxPanel* m_panel21; - - // Virtual event handlers, overide them in your derived class - virtual void OnRemoveFolderPair( wxCommandEvent& event ){ event.Skip(); } - public: wxPanel* m_panelLeft; @@ -249,6 +246,29 @@ class FolderPairGenerated : public wxPanel }; /////////////////////////////////////////////////////////////////////////////// +/// Class BatchFolderPairGenerated +/////////////////////////////////////////////////////////////////////////////// +class BatchFolderPairGenerated : public wxPanel +{ + private: + + protected: + wxStaticText* m_staticText53; + wxPanel* m_panelLeft; + wxStaticText* m_staticText541; + wxPanel* m_panelRight; + + public: + wxTextCtrl* m_directoryLeft; + wxDirPickerCtrl* m_dirPickerLeft; + wxTextCtrl* m_directoryRight; + wxDirPickerCtrl* m_dirPickerRight; + BatchFolderPairGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); + ~BatchFolderPairGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// /// Class BatchDlgGenerated /////////////////////////////////////////////////////////////////////////////// class BatchDlgGenerated : public wxDialog @@ -266,6 +286,8 @@ class BatchDlgGenerated : public wxDialog wxStaticLine* m_staticline10; wxStaticText* m_staticText531; + wxNotebook* m_notebookSettings; + wxPanel* m_panelOverview; wxScrolledWindow* m_scrolledWindow6; wxBoxSizer* bSizerFolderPairs; @@ -274,15 +296,13 @@ class BatchDlgGenerated : public wxDialog wxChoice* m_choiceHandleError; + wxCheckBox* m_checkBoxUseRecycler; + wxCheckBox* m_checkBoxFilter; wxCheckBox* m_checkBoxSilent; - wxBitmapButton* m_bpButtonFilter; - wxStaticBitmap* m_bitmap8; - wxTextCtrl* m_textCtrlInclude; - wxStaticBitmap* m_bitmap9; - wxTextCtrl* m_textCtrlExclude; + wxStaticText* m_staticText211; wxStaticText* m_staticText311; @@ -297,7 +317,19 @@ class BatchDlgGenerated : public wxDialog wxBitmapButton* m_bpButton8; wxStaticBitmap* m_bitmap17; wxBitmapButton* m_bpButton9; - wxStaticLine* m_staticline9; + wxPanel* m_panelFilter; + + wxStaticText* m_staticText15; + wxStaticBitmap* m_bitmap8; + wxTextCtrl* m_textCtrlInclude; + + wxStaticText* m_staticText16; + wxStaticBitmap* m_bitmap9; + wxTextCtrl* m_textCtrlExclude; + wxPanel* m_panelLogging; + wxStaticText* m_staticText120; + wxTextCtrl* m_textCtrlLogfileDir; + wxDirPickerCtrl* m_dirPickerLogfileDir; wxButton* m_buttonSave; wxButton* m_buttonLoad; wxButton* m_button6; @@ -307,7 +339,8 @@ class BatchDlgGenerated : public wxDialog virtual void OnChangeCompareVar( wxCommandEvent& event ){ event.Skip(); } virtual void OnChangeErrorHandling( wxCommandEvent& event ){ event.Skip(); } virtual void OnSelectRecycleBin( wxCommandEvent& event ){ event.Skip(); } - virtual void OnFilterButton( wxCommandEvent& event ){ event.Skip(); } + virtual void OnCheckFilter( wxCommandEvent& event ){ event.Skip(); } + virtual void OnCheckLogging( wxCommandEvent& event ){ event.Skip(); } virtual void OnExLeftSideOnly( wxCommandEvent& event ){ event.Skip(); } virtual void OnExRightSideOnly( wxCommandEvent& event ){ event.Skip(); } virtual void OnLeftNewer( wxCommandEvent& event ){ event.Skip(); } @@ -319,36 +352,12 @@ class BatchDlgGenerated : public wxDialog public: - BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Create a batch job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE ); + BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Create a batch job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~BatchDlgGenerated(); }; /////////////////////////////////////////////////////////////////////////////// -/// Class BatchFolderPairGenerated -/////////////////////////////////////////////////////////////////////////////// -class BatchFolderPairGenerated : public wxPanel -{ - private: - - protected: - wxStaticText* m_staticText53; - wxStaticText* m_staticText541; - - // Virtual event handlers, overide them in your derived class - virtual void OnEnterLeftDir( wxCommandEvent& event ){ event.Skip(); } - virtual void OnEnterRightDir( wxCommandEvent& event ){ event.Skip(); } - - - public: - wxTextCtrl* m_directoryLeft; - wxTextCtrl* m_directoryRight; - BatchFolderPairGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); - ~BatchFolderPairGenerated(); - -}; - -/////////////////////////////////////////////////////////////////////////////// /// Class CompareStatusGenerated /////////////////////////////////////////////////////////////////////////////// class CompareStatusGenerated : public wxPanel @@ -362,9 +371,13 @@ class CompareStatusGenerated : public wxPanel wxStaticBoxSizer* sbSizer13; wxStaticText* m_staticText46; - wxStaticText* m_staticTextFilesToCompare; + wxStaticText* m_staticTextFilesRemaining; wxStaticText* m_staticText32; - wxStaticText* m_staticTextDataToCompare; + wxStaticText* m_staticTextDataRemaining; + wxStaticText* m_staticText104; + wxStaticText* m_staticTextSpeed; + wxStaticText* m_staticText103; + wxStaticText* m_staticTextTimeRemaining; wxStaticText* m_staticText37; wxStaticText* m_staticTextTimeElapsed; @@ -482,6 +495,9 @@ class SyncStatusDlgGenerated : public wxDialog wxBoxSizer* bSizer28; wxStaticText* m_staticText25; wxStaticText* m_staticTextRemainingObj; + wxBoxSizer* bSizerSpeed; + wxStaticText* m_staticText108; + wxStaticText* m_staticTextSpeed; wxButton* m_buttonOK; wxButton* m_buttonPause; @@ -489,6 +505,9 @@ class SyncStatusDlgGenerated : public wxDialog wxStaticText* m_staticText26; wxStaticText* m_staticTextDataRemaining; + wxBoxSizer* bSizerRemTime; + wxStaticText* m_staticText106; + wxStaticText* m_staticTextTimeRemaining; // Virtual event handlers, overide them in your derived class @@ -500,7 +519,7 @@ class SyncStatusDlgGenerated : public wxDialog public: wxGauge* m_gauge1; - SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 614,371 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 638,376 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~SyncStatusDlgGenerated(); }; @@ -576,26 +595,39 @@ class AboutDlgGenerated : public wxDialog wxScrolledWindow* m_scrolledWindow3; wxStaticText* m_staticText54; + wxStaticBitmap* m_bitmapFrench; wxStaticText* m_staticText68; wxStaticText* m_staticText69; + wxStaticBitmap* m_bitmapJapanese; wxStaticText* m_staticText70; wxStaticText* m_staticText71; + wxStaticBitmap* m_bitmapDutch; wxStaticText* m_staticText711; wxStaticText* m_staticText712; + wxStaticBitmap* m_bitmapChineseSimple; wxStaticText* m_staticText91; wxStaticText* m_staticText92; + wxStaticBitmap* m_bitmapPolish; wxStaticText* m_staticText911; wxStaticText* m_staticText921; + wxStaticBitmap* m_bitmapPortuguese; wxStaticText* m_staticText9211; wxStaticText* m_staticText9212; + wxStaticBitmap* m_bitmapItalian; wxStaticText* m_staticText92121; wxStaticText* m_staticText92122; + wxStaticBitmap* m_bitmapSlovenian; wxStaticText* m_staticText921221; wxStaticText* m_staticText921222; + wxStaticBitmap* m_bitmapHungarian; wxStaticText* m_staticText682; wxStaticText* m_staticText681; + wxStaticBitmap* m_bitmapSpanish; wxStaticText* m_staticText683; wxStaticText* m_staticText691; + wxStaticBitmap* m_bitmapPortugueseBrazil; + wxStaticText* m_staticText684; + wxStaticText* m_staticText685; wxStaticLine* m_staticline3; wxPanel* m_panel22; wxStaticText* m_staticText131; @@ -687,6 +719,37 @@ class WarningDlgGenerated : public wxDialog }; /////////////////////////////////////////////////////////////////////////////// +/// Class QuestionDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class QuestionDlgGenerated : public wxDialog +{ + private: + + protected: + + wxStaticBitmap* m_bitmap10; + wxTextCtrl* m_textCtrl8; + wxCheckBox* m_checkBoxDontAskAgain; + + wxButton* m_buttonYes; + wxButton* m_buttonNo; + wxButton* m_buttonCancel; + + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ){ event.Skip(); } + virtual void OnYes( wxCommandEvent& event ){ event.Skip(); } + virtual void OnNo( wxCommandEvent& event ){ event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ){ event.Skip(); } + + + public: + QuestionDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Question"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 391,237 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~QuestionDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// /// Class DeleteDlgGenerated /////////////////////////////////////////////////////////////////////////////// class DeleteDlgGenerated : public wxDialog @@ -732,7 +795,6 @@ class FilterDlgGenerated : public wxDialog wxPanel* m_panel8; wxStaticText* m_staticText56; - wxStaticText* m_staticText44; wxBitmapButton* m_bpButtonHelp; @@ -768,7 +830,7 @@ class FilterDlgGenerated : public wxDialog public: - FilterDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Configure filter"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE ); + FilterDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Configure filter"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); ~FilterDlgGenerated(); }; diff --git a/ui/guiStatusHandler.cpp b/ui/guiStatusHandler.cpp new file mode 100644 index 00000000..74205dba --- /dev/null +++ b/ui/guiStatusHandler.cpp @@ -0,0 +1,402 @@ +#include "guiStatusHandler.h" +#include "../library/customButton.h" +#include "smallDialogs.h" +#include "../library/globalFunctions.h" +#include "mainDialog.h" + + +CompareStatusHandler::CompareStatusHandler(MainDialog* dlg) : + mainDialog(dlg), + ignoreErrors(false), + currentProcess(StatusHandler::PROCESS_NONE) +{ + //prevent user input during "compare", do not disable maindialog since abort-button would also be disabled + //it's not nice, but works + mainDialog->m_radioBtnSizeDate->Disable(); + mainDialog->m_radioBtnContent->Disable(); + mainDialog->m_bpButtonFilter->Disable(); + mainDialog->m_hyperlinkCfgFilter->Disable(); + mainDialog->m_checkBoxHideFilt->Disable(); + mainDialog->m_buttonSync->Disable(); + mainDialog->m_dirPickerLeft->Disable(); + mainDialog->m_dirPickerRight->Disable(); + mainDialog->m_bpButtonSwap->Disable(); + mainDialog->m_bpButtonLeftOnly->Disable(); + mainDialog->m_bpButtonLeftNewer->Disable(); + mainDialog->m_bpButtonEqual->Disable(); + mainDialog->m_bpButtonDifferent->Disable(); + mainDialog->m_bpButtonRightNewer->Disable(); + mainDialog->m_bpButtonRightOnly->Disable(); + mainDialog->m_panelLeft->Disable(); + mainDialog->m_panelMiddle->Disable(); + mainDialog->m_panelRight->Disable(); + mainDialog->m_panelTopLeft->Disable(); + mainDialog->m_panelTopMiddle->Disable(); + mainDialog->m_panelTopRight->Disable(); + mainDialog->m_bpButtonSave->Disable(); + mainDialog->m_bpButtonLoad->Disable(); + mainDialog->m_choiceHistory->Disable(); + mainDialog->m_bpButton10->Disable(); + mainDialog->m_bpButton14->Disable(); + mainDialog->m_scrolledWindowFolderPairs->Disable(); + mainDialog->m_menubar1->EnableTop(0, false); + mainDialog->m_menubar1->EnableTop(1, false); + mainDialog->m_menubar1->EnableTop(2, false); + + //show abort button + mainDialog->m_buttonAbort->Enable(); + mainDialog->m_buttonAbort->Show(); + mainDialog->m_buttonCompare->Disable(); + mainDialog->m_buttonCompare->Hide(); + mainDialog->m_buttonAbort->SetFocus(); + + //display status panel during compare + mainDialog->compareStatus->init(); //clear old values + mainDialog->compareStatus->Show(); + + mainDialog->bSizer1->Layout(); //both sizers need to recalculate! + mainDialog->bSizer6->Layout(); //adapt layout for wxBitmapWithImage + mainDialog->Refresh(); +} + + +CompareStatusHandler::~CompareStatusHandler() +{ + updateUiNow(); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks + + //reenable complete main dialog + mainDialog->m_radioBtnSizeDate->Enable(); + mainDialog->m_radioBtnContent->Enable(); + mainDialog->m_bpButtonFilter->Enable(); + mainDialog->m_hyperlinkCfgFilter->Enable(); + mainDialog->m_checkBoxHideFilt->Enable(); + mainDialog->m_buttonSync->Enable(); + mainDialog->m_dirPickerLeft->Enable(); + mainDialog->m_dirPickerRight->Enable(); + mainDialog->m_bpButtonSwap->Enable(); + mainDialog->m_bpButtonLeftOnly->Enable(); + mainDialog->m_bpButtonLeftNewer->Enable(); + mainDialog->m_bpButtonEqual->Enable(); + mainDialog->m_bpButtonDifferent->Enable(); + mainDialog->m_bpButtonRightNewer->Enable(); + mainDialog->m_bpButtonRightOnly->Enable(); + mainDialog->m_panelLeft->Enable(); + mainDialog->m_panelMiddle->Enable(); + mainDialog->m_panelRight->Enable(); + mainDialog->m_panelTopLeft->Enable(); + mainDialog->m_panelTopMiddle->Enable(); + mainDialog->m_panelTopRight->Enable(); + mainDialog->m_bpButtonSave->Enable(); + mainDialog->m_bpButtonLoad->Enable(); + mainDialog->m_choiceHistory->Enable(); + mainDialog->m_bpButton10->Enable(); + mainDialog->m_bpButton14->Enable(); + mainDialog->m_scrolledWindowFolderPairs->Enable(); + mainDialog->m_menubar1->EnableTop(0, true); + mainDialog->m_menubar1->EnableTop(1, true); + mainDialog->m_menubar1->EnableTop(2, true); + + if (abortRequested) + mainDialog->pushStatusInformation(_("Operation aborted!")); + + mainDialog->m_buttonAbort->Disable(); + mainDialog->m_buttonAbort->Hide(); + mainDialog->m_buttonCompare->Enable(); //enable compare button + mainDialog->m_buttonCompare->Show(); + + //hide status panel from main window + mainDialog->compareStatus->Hide(); + + mainDialog->bSizer6->Layout(); //adapt layout for wxBitmapWithImage + + mainDialog->Layout(); + mainDialog->Refresh(); +} + + +inline +void CompareStatusHandler::updateStatusText(const Zstring& text) +{ + mainDialog->compareStatus->setStatusText_NoUpdate(text); +} + + +void CompareStatusHandler::initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID) +{ + currentProcess = processID; + + if (currentProcess == StatusHandler::PROCESS_SCANNING) + ; + else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) + { + mainDialog->compareStatus->switchToCompareBytewise(objectsTotal, dataTotal); + mainDialog->Layout(); + } + + else assert(false); +} + + +inline +void CompareStatusHandler::updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) +{ + if (currentProcess == StatusHandler::PROCESS_SCANNING) + mainDialog->compareStatus->incScannedObjects_NoUpdate(objectsProcessed); + else if (currentProcess == StatusHandler::PROCESS_COMPARING_CONTENT) + mainDialog->compareStatus->incProcessedCmpData_NoUpdate(objectsProcessed, dataProcessed); + else assert(false); +} + + +ErrorHandler::Response CompareStatusHandler::reportError(const Zstring& text) +{ + if (ignoreErrors) + return ErrorHandler::IGNORE_ERROR; + + mainDialog->compareStatus->updateStatusPanelNow(); + + bool ignoreNextErrors = false; + wxString errorMessage = wxString(text.c_str()) + wxT("\n\n") + _("Ignore this error, retry or abort?"); + ErrorDlg* errorDlg = new ErrorDlg(mainDialog, + ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, + errorMessage, ignoreNextErrors); + int rv = errorDlg->ShowModal(); + switch (rv) + { + case ErrorDlg::BUTTON_IGNORE: + ignoreErrors = ignoreNextErrors; + return ErrorHandler::IGNORE_ERROR; + + case ErrorDlg::BUTTON_RETRY: + return ErrorHandler::RETRY; + + case ErrorDlg::BUTTON_ABORT: + abortThisProcess(); + } + + assert(false); + return ErrorHandler::IGNORE_ERROR; //dummy return value +} + + +void CompareStatusHandler::reportFatalError(const Zstring& errorMessage) +{ + mainDialog->compareStatus->updateStatusPanelNow(); + + bool dummy = false; + ErrorDlg* errorDlg = new ErrorDlg(mainDialog, + ErrorDlg::BUTTON_ABORT, + errorMessage.c_str(), dummy); + errorDlg->ShowModal(); + abortThisProcess(); +} + + +void CompareStatusHandler::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) +{ + if (ignoreErrors) //if errors are ignored, then warnings should also + return; + + mainDialog->compareStatus->updateStatusPanelNow(); + + //show popup and ask user how to handle warning + bool dontWarnAgain = false; + WarningDlg* warningDlg = new WarningDlg(mainDialog, + WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, + warningMessage.c_str(), + dontWarnAgain); + switch (warningDlg->ShowModal()) + { + case WarningDlg::BUTTON_ABORT: + abortThisProcess(); + + case WarningDlg::BUTTON_IGNORE: + dontShowAgain = dontWarnAgain; + return; + } + + assert(false); +} + + +inline +void CompareStatusHandler::forceUiRefresh() +{ + mainDialog->compareStatus->updateStatusPanelNow(); +} + + +void CompareStatusHandler::abortThisProcess() +{ + abortRequested = true; + throw FreeFileSync::AbortThisProcess(); //abort can be triggered by syncStatusFrame +} +//######################################################################################################## + + +SyncStatusHandler::SyncStatusHandler(wxWindow* dlg, bool ignoreAllErrors) : + ignoreErrors(ignoreAllErrors) +{ + syncStatusFrame = new SyncStatus(this, dlg); + syncStatusFrame->Show(); + updateUiNow(); +} + + +SyncStatusHandler::~SyncStatusHandler() +{ + //print the results list + unsigned int failedItems = unhandledErrors.GetCount(); + wxString result; + if (failedItems) + { + result = wxString(_("Warning: Synchronization failed for %x item(s):")) + wxT("\n\n"); + result.Replace(wxT("%x"), globalFunctions::numberToWxString(failedItems), false); + + for (unsigned int j = 0; j < failedItems; ++j) + { //remove linebreaks + wxString errorMessage = unhandledErrors[j]; + for (wxString::iterator i = errorMessage.begin(); i != errorMessage.end(); ++i) + if (*i == wxChar('\n')) + *i = wxChar(' '); + + result += errorMessage + wxT("\n"); + } + result+= wxT("\n"); + } + + //notify to syncStatusFrame that current process has ended + if (abortRequested) + { + result+= wxString(_("Synchronization aborted!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); + syncStatusFrame->setStatusText_NoUpdate(result.c_str()); + syncStatusFrame->processHasFinished(SyncStatus::ABORTED); //enable okay and close events + } + else if (failedItems) + { + result+= wxString(_("Synchronization completed with errors!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); + syncStatusFrame->setStatusText_NoUpdate(result.c_str()); + syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_ERROR); + } + else + { + result+= _("Synchronization completed successfully!"); + syncStatusFrame->setStatusText_NoUpdate(result.c_str()); + syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS); + } +} + + +inline +void SyncStatusHandler::updateStatusText(const Zstring& text) +{ + syncStatusFrame->setStatusText_NoUpdate(text); +} + + +void SyncStatusHandler::initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID) +{ + assert (processID == StatusHandler::PROCESS_SYNCHRONIZING); + + syncStatusFrame->resetGauge(objectsTotal, dataTotal); + syncStatusFrame->setCurrentStatus(SyncStatus::SYNCHRONIZING); +} + + +inline +void SyncStatusHandler::updateProcessedData(int objectsProcessed, wxLongLong dataProcessed) +{ + syncStatusFrame->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed); +} + + +ErrorHandler::Response SyncStatusHandler::reportError(const Zstring& text) +{ + //add current time before error message + wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + text.c_str(); + + if (ignoreErrors) + { + unhandledErrors.Add(errorWithTime); + return ErrorHandler::IGNORE_ERROR; + } + + syncStatusFrame->updateStatusDialogNow(); + + bool ignoreNextErrors = false; + ErrorDlg* errorDlg = new ErrorDlg(syncStatusFrame, + ErrorDlg::BUTTON_IGNORE | ErrorDlg::BUTTON_RETRY | ErrorDlg::BUTTON_ABORT, + wxString(text) + wxT("\n\n") + _("Ignore this error, retry or abort synchronization?"), + ignoreNextErrors); + int rv = errorDlg->ShowModal(); + switch (rv) + { + case ErrorDlg::BUTTON_IGNORE: + ignoreErrors = ignoreNextErrors; + unhandledErrors.Add(errorWithTime); + return ErrorHandler::IGNORE_ERROR; + + case ErrorDlg::BUTTON_RETRY: + return ErrorHandler::RETRY; + + case ErrorDlg::BUTTON_ABORT: + unhandledErrors.Add(errorWithTime); + abortThisProcess(); + } + + assert (false); + unhandledErrors.Add(errorWithTime); + return ErrorHandler::IGNORE_ERROR; +} + + +void SyncStatusHandler::reportFatalError(const Zstring& errorMessage) +{ //add current time before error message + wxString errorWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + errorMessage.c_str(); + + unhandledErrors.Add(errorWithTime); + abortThisProcess(); +} + + +void SyncStatusHandler::reportWarning(const Zstring& warningMessage, bool& dontShowAgain) +{ //add current time before warning message + wxString warningWithTime = wxString(wxT("[")) + wxDateTime::Now().FormatTime() + wxT("] ") + warningMessage.c_str(); + + if (ignoreErrors) //if errors are ignored, then warnings should also + return; //no unhandled error situation! + + syncStatusFrame->updateStatusDialogNow(); + + //show popup and ask user how to handle warning + bool dontWarnAgain = false; + WarningDlg* warningDlg = new WarningDlg(syncStatusFrame, + WarningDlg::BUTTON_IGNORE | WarningDlg::BUTTON_ABORT, + warningMessage.c_str(), + dontWarnAgain); + switch (warningDlg->ShowModal()) + { + case WarningDlg::BUTTON_IGNORE: //no unhandled error situation! + dontShowAgain = dontWarnAgain; + return; + + case WarningDlg::BUTTON_ABORT: + unhandledErrors.Add(warningWithTime); + abortThisProcess(); + } + + assert(false); +} + + +void SyncStatusHandler::forceUiRefresh() +{ + syncStatusFrame->updateStatusDialogNow(); +} + + +void SyncStatusHandler::abortThisProcess() +{ + abortRequested = true; + throw FreeFileSync::AbortThisProcess(); //abort can be triggered by syncStatusFrame +} diff --git a/ui/guiStatusHandler.h b/ui/guiStatusHandler.h new file mode 100644 index 00000000..51a87c98 --- /dev/null +++ b/ui/guiStatusHandler.h @@ -0,0 +1,60 @@ +#ifndef GUISTATUSHANDLER_H_INCLUDED +#define GUISTATUSHANDLER_H_INCLUDED + +#include "../library/statusHandler.h" +#include <wx/arrstr.h> + +class SyncStatus; +class MainDialog; +class wxWindow; + +//classes handling sync and compare error as well as status information +class CompareStatusHandler : public StatusHandler +{ +public: + CompareStatusHandler(MainDialog* dlg); + ~CompareStatusHandler(); + + virtual void updateStatusText(const Zstring& text); + virtual void initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID); + virtual void updateProcessedData(int objectsProcessed, wxLongLong dataProcessed); + virtual void forceUiRefresh(); + + virtual ErrorHandler::Response reportError(const Zstring& text); + virtual void reportFatalError(const Zstring& errorMessage); + virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); + +private: + virtual void abortThisProcess(); + + MainDialog* mainDialog; + bool ignoreErrors; + Process currentProcess; +}; + + +class SyncStatusHandler : public StatusHandler +{ +public: + SyncStatusHandler(wxWindow* dlg, bool ignoreAllErrors); + ~SyncStatusHandler(); + + virtual void updateStatusText(const Zstring& text); + virtual void initNewProcess(int objectsTotal, wxLongLong dataTotal, Process processID); + virtual void updateProcessedData(int objectsProcessed, wxLongLong dataProcessed); + virtual void forceUiRefresh(); + + virtual ErrorHandler::Response reportError(const Zstring& text); + virtual void reportFatalError(const Zstring& errorMessage); + virtual void reportWarning(const Zstring& warningMessage, bool& dontShowAgain); + +private: + virtual void abortThisProcess(); + + SyncStatus* syncStatusFrame; + bool ignoreErrors; + wxArrayString unhandledErrors; //list of non-resolved errors +}; + + +#endif // GUISTATUSHANDLER_H_INCLUDED diff --git a/ui/sorting.h b/ui/sorting.h new file mode 100644 index 00000000..22b9c39b --- /dev/null +++ b/ui/sorting.h @@ -0,0 +1,305 @@ +#ifndef SORTING_H_INCLUDED +#define SORTING_H_INCLUDED + +#include "../structures.h" +#include "../library/resources.h" +#include "../library/globalFunctions.h" + + +namespace FreeFileSync +{ + enum SideToSort + { + SORT_ON_LEFT, + SORT_ON_RIGHT, + }; + + enum SortDirection + { + ASCENDING, + DESCENDING, + }; + + + template <SortDirection sortAscending> + inline + bool stringSmallerThan(const wxChar* stringA, const wxChar* stringB) + { +#ifdef FFS_WIN //case-insensitive comparison! + return sortAscending == ASCENDING ? + FreeFileSync::compareStringsWin32(stringA, stringB) < 0 : //way faster than wxString::CmpNoCase() in windows build!!! + FreeFileSync::compareStringsWin32(stringA, stringB) > 0; +#else + while (*stringA == *stringB) + { + if (*stringA == wxChar(0)) //strings are equal + return false; + + ++stringA; + ++stringB; + } + return sortAscending == ASCENDING ? *stringA < *stringB : *stringA > *stringB; //wxChar(0) is handled correctly +#endif + } + + + inline + int compareString(const wxChar* stringA, const wxChar* stringB, const int lengthA, const int lengthB) + { +#ifdef FFS_WIN //case-insensitive comparison! + return FreeFileSync::compareStringsWin32(stringA, stringB, lengthA, lengthB); //way faster than wxString::CmpNoCase() in the windows build!!! +#else + for (int i = 0; i < std::min(lengthA, lengthB); ++i) + { + if (stringA[i] != stringB[i]) + return stringA[i] - stringB[i]; + } + return lengthA - lengthB; +#endif + } + + + template <SortDirection sortAscending, SideToSort side> + inline + bool sortByFileName(const FileCompareLine& a, const FileCompareLine& b) + { + const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; + const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; + + //presort types: first files, then directories then empty rows + if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + return false; //empty rows always last + else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + return true; //empty rows always last + + + if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); + else + return false; + } + else + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return true; + else + { + const wxChar* stringA = descrLineA->relativeName.c_str(); + const wxChar* stringB = descrLineB->relativeName.c_str(); + + size_t pos = descrLineA->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end + if (pos != std::string::npos) + stringA += pos + 1; + + pos = descrLineB->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end + if (pos != std::string::npos) + stringB += pos + 1; + + return stringSmallerThan<sortAscending>(stringA, stringB); + } + } + } + + + template <SortDirection sortAscending, SideToSort side> + bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b) + { + const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; + const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; + + //extract relative name and filename + const wxChar* const relStringA = descrLineA->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly + const wxChar* fileStringA = relStringA; + int relLengthA = 0; + int fileLengthA = 0; + + if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) + relLengthA = descrLineA->relativeName.length(); + else if (descrLineA->objType == FileDescrLine::TYPE_FILE) + { + relLengthA = descrLineA->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end + if (relLengthA == wxNOT_FOUND) + { + relLengthA = 0; + fileLengthA = descrLineA->relativeName.length(); + } + else + { + fileStringA += relLengthA + 1; + fileLengthA = descrLineA->relativeName.length() - (relLengthA + 1); + } + } + else + return false; //empty rows should be on end of list + + + const wxChar* const relStringB = descrLineB->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly + const wxChar* fileStringB = relStringB; + int relLengthB = 0; + int fileLengthB = 0; + + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + relLengthB = descrLineB->relativeName.length(); + else if (descrLineB->objType == FileDescrLine::TYPE_FILE) + { + relLengthB = descrLineB->relativeName.findFromEnd(GlobalResources::FILE_NAME_SEPARATOR); //start search beginning from end + if (relLengthB == wxNOT_FOUND) + { + relLengthB = 0; + fileLengthB = descrLineB->relativeName.length(); + } + else + { + fileStringB += relLengthB + 1; + fileLengthB = descrLineB->relativeName.length() - (relLengthB + 1); + } + } + else + return true; //empty rows should be on end of list + + //compare relative names without filenames first + const int rv = compareString(relStringA, relStringB, relLengthA, relLengthB); + if (rv != 0) + return sortAscending == ASCENDING ? rv < 0 : rv > 0; + else //compare the filenames + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) //directories shall appear before files + return false; + else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) + return true; + + return sortAscending == ASCENDING ? + compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) < 0 : + compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) > 0; + } + } + + + template <SortDirection sortAscending, SideToSort side> + inline + bool sortByFullName(const FileCompareLine& a, const FileCompareLine& b) + { + const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; + const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; + + //presort types: first files, then directories then empty rows + if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + return false; //empty rows always last + else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + return true; //empty rows always last + else +#ifdef FFS_WIN //case-insensitive comparison! + return sortAscending == ASCENDING ? + FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) < 0 : //way faster than wxString::CmpNoCase() in windows build!!! + FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) > 0; +#else + return sortAscending == ASCENDING ? + descrLineA->fullName.Cmp(descrLineB->fullName) < 0 : + descrLineA->fullName.Cmp(descrLineB->fullName) > 0; +#endif + } + + + template <SortDirection sortAscending, SideToSort side> + inline + bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b) + { + const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; + const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; + + //presort types: first files, then directories then empty rows + if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + return false; //empty rows always last + else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + return true; //empty rows always last + + + if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); + else + return false; + } + else + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return true; + else + return sortAscending == ASCENDING ? + descrLineA->fileSize > descrLineB->fileSize : //sortAscending shall result in list beginning with largest files first + descrLineA->fileSize < descrLineB->fileSize; + } + } + + + template <SortDirection sortAscending, SideToSort side> + inline + bool sortByDate(const FileCompareLine& a, const FileCompareLine& b) + { + const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; + const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; + + //presort types: first files, then directories then empty rows + if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + return false; //empty rows always last + else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + return true; //empty rows always last + + if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return stringSmallerThan<sortAscending>(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); + else + return false; + } + else + { + if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + return true; + else + return sortAscending == ASCENDING ? + descrLineA->lastWriteTimeRaw > descrLineB->lastWriteTimeRaw : + descrLineA->lastWriteTimeRaw < descrLineB->lastWriteTimeRaw; + } + } + + + template <SortDirection sortAscending> + inline + bool sortByCmpResult(const FileCompareLine& a, const FileCompareLine& b) + { + //presort result: equal shall appear at end of list + if (a.cmpResult == FILE_EQUAL) + return false; + if (b.cmpResult == FILE_EQUAL) + return true; + + return sortAscending == ASCENDING ? + a.cmpResult < b.cmpResult : + a.cmpResult > b.cmpResult; + } + + + template <SortDirection sortAscending, SideToSort side> + inline + bool sortByDirectory(const FolderCompareLine& a, const FolderCompareLine& b) + { + const Zstring* const dirNameA = side == SORT_ON_LEFT ? &a.syncPair.leftDirectory : &a.syncPair.rightDirectory; + const Zstring* const dirNameB = side == SORT_ON_LEFT ? &b.syncPair.leftDirectory : &b.syncPair.rightDirectory; + +#ifdef FFS_WIN //case-insensitive comparison! + return sortAscending == ASCENDING ? + FreeFileSync::compareStringsWin32(dirNameA->c_str(), dirNameB->c_str()) < 0 : //way faster than wxString::CmpNoCase() in windows build!!! + FreeFileSync::compareStringsWin32(dirNameA->c_str(), dirNameB->c_str()) > 0; +#elif defined FFS_LINUX + return sortAscending == ASCENDING ? + dirNameA->Cmp(*dirNameB) < 0 : + dirNameA->Cmp(*dirNameB) > 0; +#endif + } +} + +#endif // SORTING_H_INCLUDED diff --git a/version/version.h b/version/version.h index a4eaf175..a0ec5908 100644 --- a/version/version.h +++ b/version/version.h @@ -2,5 +2,5 @@ namespace FreeFileSync { - const wxString currentVersion = wxT("1.17"); + static const wxString currentVersion = wxT("1.18"); //internal linkage! } |