summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:26:50 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:26:50 +0200
commit669df123648aaa6aeccc70206b5417bc48b4e9ae (patch)
tree463c107a8d6405020bb304f7a7253e6b64afeee0
parent5.18 (diff)
downloadFreeFileSync-669df123648aaa6aeccc70206b5417bc48b4e9ae.tar.gz
FreeFileSync-669df123648aaa6aeccc70206b5417bc48b4e9ae.tar.bz2
FreeFileSync-669df123648aaa6aeccc70206b5417bc48b4e9ae.zip
5.19
-rw-r--r--Application.cpp315
-rw-r--r--BUILD/Changelog.txt26
-rw-r--r--BUILD/FreeFileSync.chmbin432107 -> 434207 bytes
-rw-r--r--BUILD/Help/FreeFileSync.hhc4
-rw-r--r--BUILD/Help/FreeFileSync.hhp2
-rw-r--r--BUILD/Help/html/Command line.html138
-rw-r--r--BUILD/Help/html/Links.html4
-rw-r--r--BUILD/Help/html/RealtimeSync.html90
-rw-r--r--BUILD/Help/html/Run as Service.html34
-rw-r--r--BUILD/Help/html/Schedule a Batch Job.html61
-rw-r--r--BUILD/Help/html/Variable Drive Letters.html68
-rw-r--r--BUILD/Help/img/RealtimeSync.pngbin31170 -> 31399 bytes
-rw-r--r--BUILD/Help/img/ScheduleBatch.pngbin23029 -> 23027 bytes
-rw-r--r--BUILD/Help/img/WatchUsbInsert.pngbin30567 -> 30701 bytes
-rw-r--r--BUILD/Help/img/create_shortcut.pngbin16086 -> 16019 bytes
-rw-r--r--BUILD/Help/img/schedule_realtimesync.pngbin29072 -> 29315 bytes
-rw-r--r--BUILD/Help/img/shortcut_properties.pngbin29921 -> 30148 bytes
-rw-r--r--BUILD/Help/img/win7scheduler.pngbin28955 -> 28814 bytes
-rw-r--r--BUILD/Languages/arabic.lng4
-rw-r--r--BUILD/Languages/croatian.lng341
-rw-r--r--BUILD/Languages/dutch.lng264
-rw-r--r--BUILD/Languages/english_uk.lng164
-rw-r--r--BUILD/Languages/german.lng163
-rw-r--r--BUILD/Languages/italian.lng291
-rw-r--r--BUILD/Languages/lithuanian.lng189
-rw-r--r--BUILD/Languages/slovenian.lng186
-rw-r--r--BUILD/Languages/spanish.lng185
-rw-r--r--BUILD/Resources.zipbin268137 -> 268644 bytes
-rw-r--r--FreeFileSync.vcxproj32
-rw-r--r--RealtimeSync/RealtimeSync.vcxproj26
-rw-r--r--RealtimeSync/application.cpp27
-rw-r--r--RealtimeSync/gui_generated.cpp6
-rw-r--r--RealtimeSync/gui_generated.h2
-rw-r--r--RealtimeSync/main_dlg.cpp88
-rw-r--r--RealtimeSync/main_dlg.h17
-rw-r--r--RealtimeSync/makefile2
-rw-r--r--RealtimeSync/monitor.cpp (renamed from RealtimeSync/watcher.cpp)187
-rw-r--r--RealtimeSync/monitor.h38
-rw-r--r--RealtimeSync/tray_menu.cpp440
-rw-r--r--RealtimeSync/tray_menu.h10
-rw-r--r--RealtimeSync/watcher.h54
-rw-r--r--RealtimeSync/xml_ffs.cpp14
-rw-r--r--RealtimeSync/xml_proc.h8
-rw-r--r--algorithm.cpp48
-rw-r--r--comparison.cpp33
-rw-r--r--file_hierarchy.cpp33
-rw-r--r--file_hierarchy.h18
-rw-r--r--lib/ShadowCopy/Shadow_Server2003.vcxproj8
-rw-r--r--lib/ShadowCopy/Shadow_Windows7.vcxproj8
-rw-r--r--lib/ShadowCopy/Shadow_XP.vcxproj8
-rw-r--r--lib/Thumbnail/Thumbnail.vcxproj8
-rw-r--r--lib/db_file.cpp10
-rw-r--r--lib/dir_exist_async.h2
-rw-r--r--lib/dir_lock.cpp39
-rw-r--r--lib/generate_logfile.h4
-rw-r--r--lib/icon_buffer.cpp4
-rw-r--r--lib/parallel_scan.cpp8
-rw-r--r--lib/parse_lng.h7
-rw-r--r--lib/perf_check.cpp76
-rw-r--r--lib/perf_check.h30
-rw-r--r--lib/process_xml.cpp45
-rw-r--r--lib/process_xml.h10
-rw-r--r--lib/resolve_path.cpp21
-rw-r--r--lib/shadow.cpp11
-rw-r--r--lib/status_handler.cpp11
-rw-r--r--lib/status_handler.h15
-rw-r--r--lib/versioning.cpp7
-rw-r--r--process_callback.h4
-rw-r--r--structures.cpp36
-rw-r--r--structures.h20
-rw-r--r--synchronization.cpp99
-rw-r--r--ui/Taskbar_Seven/Taskbar_Seven.vcxproj8
-rw-r--r--ui/batch_status_handler.cpp15
-rw-r--r--ui/check_version.cpp4
-rw-r--r--ui/custom_grid.cpp12
-rw-r--r--ui/dir_name.cpp2
-rw-r--r--ui/gui_generated.cpp654
-rw-r--r--ui/gui_generated.h153
-rw-r--r--ui/gui_status_handler.cpp12
-rw-r--r--ui/gui_status_handler.h3
-rw-r--r--ui/main_dlg.cpp288
-rw-r--r--ui/main_dlg.h18
-rw-r--r--ui/msg_popup.h9
-rw-r--r--ui/progress_indicator.cpp979
-rw-r--r--ui/progress_indicator.h8
-rw-r--r--ui/small_dlgs.cpp15
-rw-r--r--ui/switch_to_gui.h2
-rw-r--r--ui/sync_cfg.cpp48
-rw-r--r--ui/taskbar.cpp10
-rw-r--r--ui/taskbar.h4
-rw-r--r--ui/tray_icon.cpp224
-rw-r--r--ui/tray_icon.h38
-rw-r--r--ui/tree_view.cpp6
-rw-r--r--version/version.h2
-rw-r--r--wx+/dc.h127
-rw-r--r--wx+/graph.cpp495
-rw-r--r--wx+/graph.h137
-rw-r--r--wx+/grid.cpp59
-rw-r--r--wx+/no_flicker.h17
-rw-r--r--wx+/rtl.h54
-rw-r--r--wx+/shell_execute.h11
-rw-r--r--wx+/string_conv.h1
-rw-r--r--zen/FindFilePlus/FindFilePlus.vcxproj4
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp38
-rw-r--r--zen/basic_math.h9
-rw-r--r--zen/dir_watcher.cpp12
-rw-r--r--zen/dir_watcher.h4
-rw-r--r--zen/file_error.h1
-rw-r--r--zen/file_handling.cpp90
-rw-r--r--zen/file_handling.h9
-rw-r--r--zen/file_io.cpp26
-rw-r--r--zen/file_io.h4
-rw-r--r--zen/format_unit.cpp50
-rw-r--r--zen/format_unit.h13
-rw-r--r--zen/i18n.h1
-rw-r--r--zen/long_path_prefix.h5
-rw-r--r--zen/optional.h1
-rw-r--r--zen/serialize.h6
-rw-r--r--zen/stl_tools.h3
-rw-r--r--zen/string_base.h61
-rw-r--r--zen/sys_error.h18
-rw-r--r--zen/thread.h3
-rw-r--r--zen/type_tools.h4
-rw-r--r--zen/type_traits.h15
-rw-r--r--zen/win.h1
-rw-r--r--zen/zstring.cpp8
-rw-r--r--zen/zstring.h10
127 files changed, 4655 insertions, 3259 deletions
diff --git a/Application.cpp b/Application.cpp
index 7f89efcc..54258cd9 100644
--- a/Application.cpp
+++ b/Application.cpp
@@ -41,18 +41,21 @@ IMPLEMENT_APP(Application)
namespace
{
+/*
boost::thread::id mainThreadId = boost::this_thread::get_id();
void onTerminationRequested()
{
- std::wstring msg = boost::this_thread::get_id() == mainThreadId ?
- L"Termination requested in main thread!\n\n" :
- L"Termination requested in worker thread!\n\n";
- msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync";
+std::wstring msg = boost::this_thread::get_id() == mainThreadId ?
+ L"Termination requested in main thread!\n\n" :
+ L"Termination requested in worker thread!\n\n";
+msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync";
- wxSafeShowMessage(_("An exception occurred"), msg);
- std::abort();
+wxSafeShowMessage(_("An exception occurred"), msg);
+std::abort();
}
+*/
+
#ifdef _MSC_VER
void crtInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { assert(false); }
#endif
@@ -118,7 +121,8 @@ const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType();
bool Application::OnInit()
{
- std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads
+ //-> this seems rather useless:
+ //std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads
#ifdef ZEN_WIN
#ifdef _MSC_VER
@@ -262,9 +266,10 @@ void Application::onQueryEndSession(wxEvent& event)
}
-void runGuiMode(const xmlAccess::XmlGuiConfig& guiCfg);
-void runGuiMode(const std::vector<Zstring>& cfgFileName);
-void runBatchMode(const Zstring& filename, FfsReturnCode& returnCode);
+void runGuiMode();
+void runGuiMode(const XmlGuiConfig& guiCfg, const std::vector<Zstring>& referenceFiles);
+void runBatchMode(const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode);
+void showSyntaxHelp();
void Application::launch(const std::vector<Zstring>& commandArgs)
@@ -279,131 +284,247 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
//tentatively set program language to OS default until GlobalSettings.xml is read later
setLanguage(xmlAccess::XmlGlobalSettings().programLanguage); //throw FileError
}
- catch (const FileError&) {} //no messagebox: consider batch job!
+ catch (const FileError&) { assert(false); } //no messagebox: consider batch job!
- if (commandArgs.empty())
- runGuiMode(commandArgs);
- else
+
+ //parse command line arguments
+ std::vector<Zstring> leftDirs;
+ std::vector<Zstring> rightDirs;
+ std::vector<std::pair<Zstring, XmlType>> configFiles; //XmlType: batch or GUI files only
{
- const bool gotDirNames = std::any_of(commandArgs.begin(), commandArgs.end(), [](const Zstring& dirname) { return dirExists(dirname); });
- if (gotDirNames) //mode 1: create temp configuration based on directory names passed
- {
- XmlGuiConfig guiCfg;
- guiCfg.mainCfg.syncCfg.directionCfg.var = DirectionConfig::MIRROR;
+ const Zchar optionLeftDir [] = Zstr("-leftdir");
+ const Zchar optionRightDir[] = Zstr("-rightdir");
- for (auto it = commandArgs.begin(); it != commandArgs.end(); ++it)
+ auto syntaxHelpRequested = [](const Zstring& arg)
+ {
+ auto it = std::find_if(arg.begin(), arg.end(), [](Zchar c) { return c != Zchar('/') && c != Zchar('-'); });
+ const Zstring argTmp(it, arg.end());
+ return argTmp == Zstr("help") ||
+ argTmp == Zstr("h") ||
+ argTmp == Zstr("?");
+ };
+
+ for (auto it = commandArgs.begin(); it != commandArgs.end(); ++it)
+ if (syntaxHelpRequested(*it))
+ return showSyntaxHelp();
+ else if (*it == optionLeftDir)
{
- size_t index = it - commandArgs.begin();
-
- FolderPairEnh* fp = nullptr;
- if (index < 2)
- fp = &guiCfg.mainCfg.firstPair;
- else
+ if (++it == commandArgs.end())
{
- guiCfg.mainCfg.additionalPairs.resize((index - 2) / 2 + 1);
- fp = &guiCfg.mainCfg.additionalPairs.back();
+ wxMessageBox(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo<std::wstring>(optionLeftDir)), L"FreeFileSync - " + _("Syntax error"), wxOK | wxICON_ERROR);
+ return;
}
-
- if (index % 2 == 0)
- fp->leftDirectory = *it;
- else
- fp->rightDirectory = *it;
+ leftDirs.push_back(*it);
}
-
- runGuiMode(guiCfg);
- }
- else //mode 2: try to set config/batch-filename set by %1 parameter
- {
- std::vector<Zstring> argsTmp = commandArgs;
-
- for (auto it = argsTmp.begin(); it != argsTmp.end(); ++it)
+ else if (*it == optionRightDir)
{
- const Zstring& filename = *it;
-
+ if (++it == commandArgs.end())
+ {
+ wxMessageBox(replaceCpy(_("A directory path is expected after %x."), L"%x", utfCvrtTo<std::wstring>(optionRightDir)), L"FreeFileSync - " + _("Syntax error"), wxOK | wxICON_ERROR);
+ return;
+ }
+ rightDirs.push_back(*it);
+ }
+ else
+ {
+ Zstring filename = *it;
if (!fileExists(filename)) //...be a little tolerant
{
if (fileExists(filename + Zstr(".ffs_batch")))
- *it += Zstr(".ffs_batch");
+ filename += Zstr(".ffs_batch");
else if (fileExists(filename + Zstr(".ffs_gui")))
- *it += Zstr(".ffs_gui");
+ filename += Zstr(".ffs_gui");
else
{
- wxMessageBox(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
return;
}
}
+
+ switch (getXmlType(filename)) //throw()
+ {
+ case XML_TYPE_GLOBAL:
+ case XML_TYPE_OTHER:
+ wxMessageBox(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
+ return;
+
+ case XML_TYPE_GUI:
+ configFiles.push_back(std::make_pair(filename, XML_TYPE_GUI));
+ break;
+ case XML_TYPE_BATCH:
+ configFiles.push_back(std::make_pair(filename, XML_TYPE_BATCH));
+ break;
+ }
}
+ }
+
+ if (leftDirs.size() != rightDirs.size())
+ {
+ wxMessageBox(_("Unequal number of left and right directories specified."), L"FreeFileSync - " + _("Syntax error"), wxOK | wxICON_ERROR);
+ return;
+ }
+
+ auto hasNonDefaultConfig = [](const FolderPairEnh& fp)
+ {
+ return !(fp == FolderPairEnh(fp.leftDirectory,
+ fp.rightDirectory,
+ nullptr, nullptr, FilterConfig()));
+ };
- switch (getMergeType(argsTmp)) //throw ()
+ auto replaceDirectories = [&](MainConfiguration& mainCfg)
+ {
+ if (!leftDirs.empty())
+ {
+ //check if config at folder-pair level is present: this probably doesn't make sense when replacing/adding the user-specified directories
+ if (hasNonDefaultConfig(mainCfg.firstPair) || std::any_of(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), hasNonDefaultConfig))
{
- case MERGE_BATCH: //pure batch config files
- if (argsTmp.size() == 1)
- runBatchMode(argsTmp[0], returnCode);
- else
- runGuiMode(argsTmp);
- break;
+ wxMessageBox(_("The config file must not contain settings at directory pair level when directories are set via command line."), L"FreeFileSync - " + _("Syntax error"), wxOK | wxICON_ERROR);
+ return false;
+ }
- case MERGE_GUI: //pure gui config files
- case MERGE_GUI_BATCH: //gui and batch files
- runGuiMode(argsTmp);
- break;
+ mainCfg.additionalPairs.clear();
+ for (size_t i = 0; i < leftDirs.size(); ++i)
+ if (i == 0)
+ {
+ mainCfg.firstPair.leftDirectory = leftDirs [0];
+ mainCfg.firstPair.rightDirectory = rightDirs[0];
+ }
+ else
+ mainCfg.additionalPairs.push_back(FolderPairEnh(leftDirs [i],
+ rightDirs[i],
+ nullptr, nullptr, FilterConfig()));
+ }
+ return true;
+ };
- case MERGE_OTHER: //= none or unknown;
- //argsTmp are not empty and contain at least one non-gui/non-batch config file: find it!
- std::find_if(argsTmp.begin(), argsTmp.end(),
- [](const Zstring& filename) -> bool
- {
- switch (getXmlType(filename)) //throw()
- {
- case XML_TYPE_GLOBAL:
- case XML_TYPE_OTHER:
- wxMessageBox(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)), _("Error"), wxOK | wxICON_ERROR);
- return true;
-
- case XML_TYPE_GUI:
- case XML_TYPE_BATCH:
- break;
- }
- return false;
- });
- break;
+ //distinguish sync scenarios:
+ //---------------------------
+ if (configFiles.empty())
+ {
+ //gui mode: default startup
+ if (leftDirs.empty())
+ runGuiMode();
+ //gui mode: default config with given directories
+ else
+ {
+ XmlGuiConfig guiCfg;
+ guiCfg.mainCfg.syncCfg.directionCfg.var = DirectionConfig::MIRROR;
+
+ if (!replaceDirectories(guiCfg.mainCfg)) return;
+ runGuiMode(guiCfg, std::vector<Zstring>());
+ }
+ }
+ else if (configFiles.size() == 1)
+ {
+ const Zstring filename = configFiles[0].first;
+
+ //batch mode
+ if (configFiles[0].second == XML_TYPE_BATCH)
+ {
+ XmlBatchConfig batchCfg;
+ try
+ {
+ readConfig(filename, batchCfg);
+ }
+ catch (const xmlAccess::FfsXmlError& e)
+ {
+ wxMessageBox(e.toString(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR); //batch mode: break on errors AND even warnings!
+ raiseReturnCode(returnCode, FFS_RC_ABORTED);
+ return;
+ }
+ if (!replaceDirectories(batchCfg.mainCfg)) return;
+ runBatchMode(batchCfg, filename, returnCode);
+ }
+ //GUI mode: single config
+ else
+ {
+ XmlGuiConfig guiCfg;
+ try
+ {
+ readConfig(filename, guiCfg);
+ }
+ catch (const xmlAccess::FfsXmlError& e)
+ {
+ if (e.getSeverity() == FfsXmlError::WARNING)
+ wxMessageBox(e.toString(), L"FreeFileSync - " + _("Warning"), wxOK | wxICON_WARNING);
+ //what about simulating changed config on parsing errors????
+ else
+ {
+ wxMessageBox(e.toString(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
+ return;
+ }
+ }
+ if (!replaceDirectories(guiCfg.mainCfg)) return;
+ //what about simulating changed config due to directory replacement?
+ //-> propably fine to not show as changed on GUI and not ask user to save on exit!
+
+ runGuiMode(guiCfg, { filename }); //caveat: guiCfg and filename do not match if directories were set/replaced via command line!
+ }
+ }
+ //gui mode: merged configs
+ else
+ {
+ if (!leftDirs.empty())
+ {
+ wxMessageBox(_("Directories cannot be set for more than one configuration file."), L"FreeFileSync - " + _("Syntax error"), wxOK | wxICON_ERROR);
+ return;
+ }
+
+ std::vector<Zstring> filenames;
+ for (const auto& item : configFiles)
+ filenames.push_back(item.first);
+
+ XmlGuiConfig guiCfg; //structure to receive gui settings with default values
+ try
+ {
+ readAnyConfig(filenames, guiCfg); //throw FfsXmlError
+ }
+ catch (const FfsXmlError& e)
+ {
+ if (e.getSeverity() == FfsXmlError::WARNING)
+ wxMessageBox(e.toString(), L"FreeFileSync - " + _("Warning"), wxOK | wxICON_WARNING);
+ //what about simulating changed config on parsing errors????
+ else
+ {
+ wxMessageBox(e.toString(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
+ return;
}
}
+ runGuiMode(guiCfg, filenames);
}
}
-void runGuiMode(const xmlAccess::XmlGuiConfig& guiCfg)
+void runGuiMode() { MainDialog::create(); }
+
+
+void runGuiMode(const xmlAccess::XmlGuiConfig& guiCfg,
+ const std::vector<Zstring>& referenceFiles)
{
- MainDialog::create(guiCfg, true);
+ MainDialog::create(guiCfg, referenceFiles, nullptr, true); //startComparison == true!
}
-void runGuiMode(const std::vector<Zstring>& cfgFileNames)
+void showSyntaxHelp()
{
- MainDialog::create(cfgFileNames);
+ wxMessageBox(_("Syntax:") + L"\n" +
+ L"FreeFileSync [" + _("config files") + L"]\n[-leftdir " + _("directory") + L"] [-rightdir " + _("directory") + L"]" + L"\n" +
+ L"\n" +
+ _("config files") + L"\n" +
+ _("Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.") + L"\n\n"
+
+ L"-leftdir " + _("directory") + L" -rightdir " + _("directory") + L"\n" +
+ _("Any number of alternative directories for at most one config file."),
+ L"FreeFileSync - " + _("Command line"), wxOK | wxICON_INFORMATION);
}
-void runBatchMode(const Zstring& filename, FfsReturnCode& returnCode)
+void runBatchMode(const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode)
{
- //load XML settings
- XmlBatchConfig batchCfg;
- try
- {
- readConfig(filename, batchCfg);
- }
- catch (const xmlAccess::FfsXmlError& e)
- {
- wxMessageBox(e.toString(), _("Error"), wxOK | wxICON_ERROR); //batch mode: break on errors AND even warnings!
- raiseReturnCode(returnCode, FFS_RC_ABORTED);
- return;
- }
-
auto notifyError = [&](const std::wstring& msg)
{
if (batchCfg.handleError == ON_ERROR_POPUP)
- wxMessageBox(msg.c_str(), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(msg.c_str(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
else //"exit" or "ignore"
logError(utfCvrtTo<std::string>(msg));
@@ -445,11 +566,11 @@ void runBatchMode(const Zstring& filename, FfsReturnCode& returnCode)
const TimeComp timeStamp = localTime();
- const SwitchToGui switchBatchToGui(filename, batchCfg, globalCfg); //prepare potential operational switch
+ const SwitchToGui switchBatchToGui(referenceFile, batchCfg, globalCfg); //prepare potential operational switch
//class handling status updates and error messages
BatchStatusHandler statusHandler(batchCfg.showProgress, //throw BatchAbortProcess
- extractJobName(filename),
+ extractJobName(referenceFile),
timeStamp,
batchCfg.logFileDirectory,
batchCfg.logfilesCountLimit,
diff --git a/BUILD/Changelog.txt b/BUILD/Changelog.txt
index b3ffe191..d8e3ad35 100644
--- a/BUILD/Changelog.txt
+++ b/BUILD/Changelog.txt
@@ -1,6 +1,29 @@
+FreeFileSync 5.19
+-----------------
+Redesigned progress dialog including new items graph
+New command line syntax: set directory names of a .ffs_gui/.ffs_batch externally
+Explicit button on progress dialog to minimize to systray
+Fixed progress graph labels being truncated (Debian, Ubuntu, openSUSE)
+Resolved main dialog z-order issues during sync (OS X)
+Reduced progress dialog layout twitching
+Further improved comparison speed by 10%
+Use proper config file path in file picker dialog (OS X)
+Never interrupt when updating a file with fail-safe file copy after target was deleted
+Prevent crash when closing progress dialog while paused (OS X)
+Support external command lines starting with whitespace (Windows)
+Show warning before starting external applications for more than 10 items
+Start external applications synchronously if needed to avoid running out of system resources
+Don't show hidden progress dialog when showing an error message in silent batch mode (OS X)
+Correctly show file names containing ampersand characters in progress dialog
+Adapt size of results dialog to fit contents
+Correctly execute file move before parent directory will be deleted
+Show a blinking system tray icon on errors instead of a modal dialog in RealtimeSync
+Added installation size for Windows' Add/Remove Programs
+
+
FreeFileSync 5.18
-----------------
-Workaround boost 1.54 bug "Procedure Entry Point Not Found in Kernel32.dll" (Windows XP)
+Work around boost 1.54 bug "The procedure entry point GetTickCount64 could not be located in the dynamic link library KERNEL32.dll" (Windows XP)
FreeFileSync 5.17
@@ -30,6 +53,7 @@ Improved sync progress dialog layout
Suppress dubious wxWidgets error message "locale 'es_AR' can not be set". (OS X)
Don't show busy cursor on synchronization results dialog
Log error message upon retry as type info only
+Updated translation files
FreeFileSync 5.16
diff --git a/BUILD/FreeFileSync.chm b/BUILD/FreeFileSync.chm
index ba059751..d6009619 100644
--- a/BUILD/FreeFileSync.chm
+++ b/BUILD/FreeFileSync.chm
Binary files differ
diff --git a/BUILD/Help/FreeFileSync.hhc b/BUILD/Help/FreeFileSync.hhc
index bfcd35f9..d3c318d1 100644
--- a/BUILD/Help/FreeFileSync.hhc
+++ b/BUILD/Help/FreeFileSync.hhc
@@ -20,8 +20,8 @@
<param name="Local" value="html\FreeFileSync.html">
</OBJECT>
<LI> <OBJECT type="text/sitemap">
- <param name="Name" value="Batch scripting">
- <param name="Local" value="html\Batch scripting.html">
+ <param name="Name" value="Command line">
+ <param name="Local" value="html\Command line.html">
</OBJECT>
<LI> <OBJECT type="text/sitemap">
<param name="Name" value="Comparison settings">
diff --git a/BUILD/Help/FreeFileSync.hhp b/BUILD/Help/FreeFileSync.hhp
index 38c789d9..713fab68 100644
--- a/BUILD/Help/FreeFileSync.hhp
+++ b/BUILD/Help/FreeFileSync.hhp
@@ -12,7 +12,7 @@ Title=FreeFileSync - Help
[FILES]
html\FreeFileSync.html
html\Versioning.html
-html\Batch scripting.html
+html\Command line.html
html\Expert settings.html
html\Comparison settings.html
html\Daylight Saving time.html
diff --git a/BUILD/Help/html/Command line.html b/BUILD/Help/html/Command line.html
new file mode 100644
index 00000000..3768d036
--- /dev/null
+++ b/BUILD/Help/html/Command line.html
@@ -0,0 +1,138 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252">
+ <TITLE></TITLE>
+ <META NAME="GENERATOR" CONTENT="LibreOffice 4.0.4.2 (Windows)">
+ <META NAME="CREATED" CONTENT="20091206;16574000">
+ <META NAME="CHANGED" CONTENT="20130722;18174509">
+ <META NAME="Info 1" CONTENT="">
+ <META NAME="Info 2" CONTENT="">
+ <META NAME="Info 3" CONTENT="">
+ <META NAME="Info 4" CONTENT="">
+ <STYLE TYPE="text/css">
+ <!--
+ @page { margin: 2cm }
+ P { margin-bottom: 0.21cm }
+ H2.cjk { font-family: "SimSun" }
+ H2.ctl { font-family: "Mangal" }
+ -->
+ </STYLE>
+</HEAD>
+<BODY LANG="en-US" DIR="LTR">
+<H2 CLASS="western"><FONT FACE="Tahoma, sans-serif"><FONT SIZE=4 STYLE="font-size: 15pt">Command
+line</FONT></FONT></H2>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">FreeFileSync
+enables additional synchronization scenarios via a command line interface. To get a syntax overview, open the console, go to the directory where FreeFileSync is installed and type:</FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen4" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><FONT FACE="Courier New, monospace">FreeFileSync
+ -h</FONT></FONT></P>
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>1.
+Run a FreeFileSync batch job</B></FONT></P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">In order to start synchronization in batch mode, supply the path of a
+<FONT FACE="Courier New, monospace">*.ffs_batch</FONT>
+configuration as the first argument for FreeFileSync.exe:</FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen1" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><FONT FACE="Courier New, monospace">FreeFileSync
+ &quot;D:\Backup </FONT><FONT FACE="Courier New, monospace">Projects</FONT><FONT FACE="Courier New, monospace">.ffs_batch&quot;</FONT></FONT></P>
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">After
+synchronization one of the following status codes is returned:</FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen6" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>Return
+ Codes</B></SPAN></FONT></SPAN><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><BR>0
+ - Synchronization completed successfully<BR>1 - Synchronization
+ completed with warnings<BR>2 - Synchronization completed with
+ errors<BR>3 - Synchronization was aborted</SPAN></FONT></SPAN>
+ </P>
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-bottom: 0cm; font-weight: normal"><FONT FACE="Tahoma, sans-serif">You
+can evaluate these codes from a script (e.g. a <FONT FACE="Courier New, monospace">*.cmd</FONT>
+or <FONT FACE="Courier New, monospace">*.bat</FONT> file on Windows)
+and check if synchronization completed successfully:</FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen6" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><FONT FACE="Courier New, monospace">&quot;C:\Program
+ Files\FreeFileSync\FreeFileSync.exe&quot; &quot;D:\Backup
+ </FONT><FONT FACE="Courier New, monospace">Projects</FONT><FONT FACE="Courier New, monospace">.ffs_batch&quot;<BR>if
+ errorlevel 1 (<BR>&nbsp;&nbsp;</FONT><FONT COLOR="#808080"><FONT FACE="Courier New, monospace"><I><B>::if
+ return code is 1 or greater, something went wrong, add special
+ treatment here<BR>&nbsp;&nbsp;</B></I></FONT></FONT><SPAN STYLE="font-variant: normal"><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">echo
+ Errors occurred during synchronization...</SPAN></SPAN></FONT></FONT></SPAN><FONT COLOR="#808080"><FONT FACE="Courier New, monospace"><I><B><BR>&nbsp;&nbsp;</B></I></FONT></FONT><FONT FACE="Courier New, monospace">pause<BR>)</FONT></FONT></P>
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Instead
+of displaying &quot;An error occurred!&quot; you can also send an
+email notification (using a third party tool).</FONT>
+</P>
+<P STYLE="margin-bottom: 0cm"><BR>
+</P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen3" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
+<P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
+ <FONT FACE="Tahoma, sans-serif"><B>Attention<BR></B>Make
+ sure your script is not blocked by a popup dialog. Consider the
+ following options when setting up a FreeFileSync batch job:</FONT>
+ </P>
+
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
+ <FONT FACE="Tahoma, sans-serif">Disable
+ checkbox <B>Show
+ progress dialog</B> or have <B>On completion</B>
+ automatically close the results dialog after synchronization.</FONT>
+ </P>
+
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
+ <FONT FACE="Tahoma, sans-serif">Set error handling to <B>Exit instantly</B> or <B>Ignore errors</B>.</FONT>
+ </P>
+
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>2.
+Start a FreeFileSync GUI configuration</B></FONT></P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">If you
+pass a <FONT FACE="Courier New, monospace">*.ffs_gui</FONT>
+file, FreeFileSync will start in GUI mode and immediately start
+comparison (but only if all directories exist):</FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen2" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Courier New, monospace">FreeFileSync
+ &quot;D:\Manual Backup.ffs_gui&quot;</FONT></P>
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>3.
+Customize an existing configuration</B></FONT></P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">You can
+replace the directories of a given <FONT FACE="Courier New, monospace">*.ffs_gui
+</FONT>or <FONT FACE="Courier New, monospace">*.ffs_batch
+</FONT>configuration by using the <FONT FACE="Courier New, monospace">-leftdir</FONT>
+and <FONT FACE="Courier New, monospace">-rightdir</FONT> parameters:</FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen5" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
+ <FONT FACE="Courier New, monospace">FreeFileSync &quot;D:\Manual Backup.ffs_gui&quot; -leftdir C:\NewSource -rightdir D:\NewTarget</FONT></P>
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>4.
+Merge multiple configurations</B></FONT></P>
+<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">When
+more than one configuration file is provided, FreeFileSync will merge
+everything into a single configuration with multiple folder pairs and
+start in GUI mode:</FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen7" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
+ <FONT FACE="Courier New, monospace">FreeFileSync &quot;D:\Manual Backup.ffs_gui&quot; &quot;D:\Backup Projects.ffs_batch&quot;</FONT></P>
+</SPAN><BR CLEAR=LEFT><BR>
+</P>
+<P STYLE="margin-bottom: 0cm"><BR>
+</P>
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/BUILD/Help/html/Links.html b/BUILD/Help/html/Links.html
index 125a8f2b..38c46bf3 100644
--- a/BUILD/Help/html/Links.html
+++ b/BUILD/Help/html/Links.html
@@ -26,8 +26,8 @@ Links</FONT></FONT></H2>
feedback, suggestions and
bug-reports</SPAN></FONT><FONT FACE="Tahoma, sans-serif"><B><BR></B></FONT><A HREF="http://sourceforge.net/projects/freefilesync"><FONT FACE="Tahoma, sans-serif">http://sourceforge.net/projects/freefilesync</FONT></A></P>
<P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
- <FONT FACE="Tahoma, sans-serif"><B>If you like FreeFileSync consider
- supporting the project by a donation:<BR></B></FONT><A HREF="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=zenju@gmx.de&amp;no_shipping=1&amp;lc=US&amp;currency_code=EUR"><FONT FACE="Tahoma, sans-serif">Donate
+ <FONT FACE="Tahoma, sans-serif"><B>If you like FreeFileSync:</B> consider
+ supporting the project by a donation<BR></FONT><A HREF="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=zenju@gmx.de&amp;no_shipping=1&amp;lc=US&amp;currency_code=EUR"><FONT FACE="Tahoma, sans-serif">Donate
via PayPal</FONT></A></P>
</SPAN><BR CLEAR=LEFT><BR>
</P>
diff --git a/BUILD/Help/html/RealtimeSync.html b/BUILD/Help/html/RealtimeSync.html
index c147a60e..d765c818 100644
--- a/BUILD/Help/html/RealtimeSync.html
+++ b/BUILD/Help/html/RealtimeSync.html
@@ -3,9 +3,9 @@
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252">
<TITLE></TITLE>
- <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)">
+ <META NAME="GENERATOR" CONTENT="LibreOffice 4.0.4.2 (Windows)">
<META NAME="CREATED" CONTENT="20091206;16574000">
- <META NAME="CHANGED" CONTENT="20130206;20451343">
+ <META NAME="CHANGED" CONTENT="20130722;18255897">
<META NAME="Info 1" CONTENT="">
<META NAME="Info 2" CONTENT="">
<META NAME="Info 3" CONTENT="">
@@ -26,7 +26,7 @@
Synchronization</SPAN></I></FONT></H3>
<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">RealtimeSync
is a small tool which executes a command line each time it detects
-changes in one of the monitored directories </FONT><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>or</B></SPAN></FONT>
+changes in one of the monitored directories </FONT><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>or</B></SPAN></FONT></SPAN>
<FONT FACE="Tahoma, sans-serif">a directory becomes available (e. g.
insert of a USB-stick). Usually this command line will simply trigger
a FreeFileSync batch job.</FONT></P>
@@ -39,42 +39,46 @@ combination with FreeFileSync</FONT></P>
RealtimeSync.exe located in FreeFileSync's installation directory.
Then specify all folders that shall be monitored. Instead of doing
this manually you can import a </FONT><FONT FACE="Courier New, monospace">*.ffs_batch</FONT>
-<FONT FACE="Tahoma, sans-serif">file via </FONT><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>Menu
-&rarr; Program &rarr; Open</B></SPAN></FONT><FONT FACE="Tahoma, sans-serif">.
+<FONT FACE="Tahoma, sans-serif">file via </FONT><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>Menu
+&rarr; Program &rarr; Open</B></SPAN></FONT></SPAN><FONT FACE="Tahoma, sans-serif">.
This not only extracts all directories relevant for synchronization
but also sets up the command line to execute the </FONT><FONT FACE="Courier New, monospace">*.ffs_batch</FONT>
<FONT FACE="Tahoma, sans-serif">file each time changes are detected.
-Now press </FONT><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>Start</B></SPAN></FONT>
+Now press </FONT><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>Start</B></SPAN></FONT></SPAN>
<FONT FACE="Tahoma, sans-serif">to begin monitoring.</FONT></P>
<UL>
<P STYLE="margin-bottom: 0cm"><IMG SRC="../img/RealtimeSync.png" NAME="Grafik3" ALIGN=MIDDLE WIDTH=461 HEIGHT=388 BORDER=0></P>
</UL>
<P STYLE="margin-bottom: 0cm"><BR>
</P>
-<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen1" DIR="LTR" STYLE="float: left; width: 80%; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note</B></FONT></P>
- <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">The
+<SPAN ID="Rahmen1" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note</B></FONT></P>
+
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">The
command should </FONT><FONT FACE="Tahoma, sans-serif"><B>not</B></FONT>
<FONT FACE="Tahoma, sans-serif"><B>block</B></FONT> <FONT FACE="Tahoma, sans-serif">progress.
Make sure the FreeFileSync batch job does not show any popup
- dialogs. See notes in <A HREF="Batch%20Scripting.html">Batch
+ dialogs. See notes in <A HREF="Batch%20scripting.html">Batch
Scripting</A>.</FONT><SPAN STYLE="text-decoration: none"><FONT FACE="Tahoma, sans-serif"><BR>&nbsp;</FONT></SPAN></P>
- <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">The
+
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">The
settings dialog can be skipped by passing a RealtimeSync
configuration file (<FONT FACE="Courier New, monospace">*.ffs_real</FONT>)
<B>or</B> a FreeFileSync batch file (<FONT FACE="Courier New, monospace">*.ffs_batch</FONT>)
as first command line argument to RealtimeSync.exe. This can be
integrated into the operating system's auto start
facility:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT FACE="Courier New, monospace">&quot;C:\Program
- Files\FreeFileSync\RealtimeSync.exe&quot;
- &quot;C:\SyncJob.ffs_real&quot;<BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT FACE="Courier New, monospace">&quot;C:\Program
- Files\FreeFileSync\RealtimeSync.exe&quot; &quot;C:\SyncJob.ffs_batch&quot;</FONT><BR>&nbsp;</FONT></P>
- <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Using
+ Files\FreeFileSync\RealtimeSync.exe&quot; &quot;</FONT><FONT FACE="Courier New, monospace">D</FONT><FONT FACE="Courier New, monospace">:\</FONT><FONT FACE="Courier New, monospace">Backup
+ Projects</FONT><FONT FACE="Courier New, monospace">.ffs_real&quot;<BR></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT FACE="Courier New, monospace">&quot;C:\Program
+ Files\FreeFileSync\RealtimeSync.exe&quot; &quot;</FONT><FONT FACE="Courier New, monospace">D</FONT><FONT FACE="Courier New, monospace">:\</FONT><FONT FACE="Courier New, monospace">Backup
+ Projects</FONT><FONT FACE="Courier New, monospace">.ffs_batch&quot;</FONT><BR>&nbsp;</FONT></P>
+
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Using
RealtimeSync is not restricted to starting FreeFileSync. It can
also be used in other scenarios, like sending an email whenever a
certain directory is modified.</FONT></P>
</SPAN><BR CLEAR=LEFT><BR>
-</P>
+
<P STYLE="margin-bottom: 0cm"><BR>
</P>
<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Example:</B></FONT>
@@ -98,41 +102,37 @@ files are modified in &quot;</FONT><FONT FACE="Courier New, monospace">H:\Data</
</P>
<P STYLE="margin-bottom: 0cm"><BR>
</P>
-<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen2" DIR="LTR" STYLE="float: left; width: 80%; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note<BR></B></FONT><FONT FACE="Tahoma, sans-serif">The
- full path of the last changed file and the action that triggered
- the change notification (create, update or delete) are </FONT><SPAN STYLE="text-decoration: none"><FONT FACE="Tahoma, sans-serif">written
- to the environment variables </FONT></SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Courier New, monospace"><B>%change_path%</B></FONT></SPAN><SPAN STYLE="text-decoration: none">
- </SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Tahoma, sans-serif">and
- </FONT></SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Courier New, monospace"><B>%change_action%</B></FONT></SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Courier New, monospace">.</FONT></SPAN></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen2" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note<BR></B></FONT><FONT FACE="Tahoma, sans-serif">The
+ full path of the last changed file and the action that triggered the
+ change notification (create, update or delete) are </FONT><SPAN STYLE="text-decoration: none"><FONT FACE="Tahoma, sans-serif">written
+ to the environment variables </FONT></SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Courier New, monospace"><B>%change_path%</B></FONT></SPAN><SPAN STYLE="text-decoration: none">
+ </SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Tahoma, sans-serif">and
+ </FONT></SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Courier New, monospace"><B>%change_action%</B></FONT></SPAN><SPAN STYLE="text-decoration: none"><FONT FACE="Courier New, monospace">.</FONT></SPAN></P>
</SPAN><BR CLEAR=LEFT><BR>
</P>
<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Example:</B></FONT>
<FONT FACE="Tahoma, sans-serif">Show names of changed files or
directories. (Windows)</FONT></P>
-
- <P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen4" DIR="LTR" STYLE="float: left; width: 80%; border: none; padding: 0cm; background: #e6e6e6">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Courier New, monospace"><FONT FACE="Tahoma, sans-serif">Show
- which file or directory has triggered a change. Enter command
- line:</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;cmd /c echo
- %change_action% &quot;%change_path%&quot; &amp; pause<BR><BR><FONT FACE="Tahoma, sans-serif">Write
- a list of all changes to a logfile:</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;cmd
- /c echo %change_action% &quot;%change_path%&quot; &gt;&gt;
- c:\log.txt</FONT></P>
- </SPAN><BR CLEAR=LEFT>
- </P>
-
-<P STYLE="margin-bottom: 0cm"><BR>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen4" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Courier New, monospace"><FONT FACE="Tahoma, sans-serif">Show
+ which file or directory has triggered a change. Enter command
+ line:</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;cmd /c echo %change_action%
+ &quot;%change_path%&quot; &amp; pause<BR><BR><FONT FACE="Tahoma, sans-serif">Write
+ a list of all changes to a logfile:</FONT><BR>&nbsp;&nbsp;&nbsp;&nbsp;cmd
+ /c echo %change_action% &quot;%change_path%&quot; &gt;&gt;
+ c:\log.txt</FONT></P>
+</SPAN><BR CLEAR=LEFT><BR>
</P>
-<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen5" DIR="LTR" STYLE="float: left; width: 80%; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note<BR></B></FONT><FONT FACE="Tahoma, sans-serif">During
- execution of a Windows Batch file (*.bat/*.cmd) a black console
- window is shown. You can hide it using the Visual Basic script
- &quot;HideConsole.vbs&quot; located in FreeFileSync's installation
- directory:</FONT></P>
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Courier New, monospace"><B>wscript
- &quot;C:\Program files\FreeFileSync\HideConsole.vbs&quot;
- C:\MyBatchFile.cmd</B></FONT></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen5" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note<BR></B></FONT><FONT FACE="Tahoma, sans-serif">During
+ execution of a Windows Batch file (*.bat/*.cmd) a black console
+ window is shown. You can hide it using the Visual Basic script
+ &quot;HideConsole.vbs&quot; located in FreeFileSync's installation
+ directory:</FONT></P>
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Courier New, monospace"><B>wscript
+ &quot;C:\Program files\FreeFileSync\HideConsole.vbs&quot;
+ C:\MyBatchFile.cmd</B></FONT></P>
</SPAN><BR CLEAR=LEFT><BR>
</P>
<P STYLE="margin-bottom: 0cm"><BR>
diff --git a/BUILD/Help/html/Run as Service.html b/BUILD/Help/html/Run as Service.html
index 2a853717..d04d8115 100644
--- a/BUILD/Help/html/Run as Service.html
+++ b/BUILD/Help/html/Run as Service.html
@@ -3,9 +3,9 @@
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252">
<TITLE></TITLE>
- <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)">
+ <META NAME="GENERATOR" CONTENT="LibreOffice 4.0.4.2 (Windows)">
<META NAME="CREATED" CONTENT="20091206;16574000">
- <META NAME="CHANGED" CONTENT="20130206;19210588">
+ <META NAME="CHANGED" CONTENT="20130722;18264912">
<META NAME="Info 1" CONTENT="">
<META NAME="Info 2" CONTENT="">
<META NAME="Info 3" CONTENT="">
@@ -27,40 +27,40 @@ is designed as a background process which does not need further
attention once it is running. Depending on your requirements there
are a number of ways to start it together with the operating system.
Generally the goal is to execute a command line like the following:
-</FONT><FONT FACE="Courier New, monospace"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">&lt;installation
+</FONT><SPAN STYLE="font-variant: normal"><FONT FACE="Courier New, monospace"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">&lt;installation
folder&gt;\RealtimeSync.exe &lt;path to *.ffs_real or *.ffs_batch
-file&gt;</SPAN></SPAN></FONT></P>
+file&gt;</SPAN></SPAN></FONT></SPAN></P>
<P STYLE="margin-bottom: 0cm"><BR>
</P>
<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Example:</B></FONT></P>
<UL>
- <P ><SPAN ID="Rahmen2" DIR="LTR" STYLE="float: left; width: 80%; border: none; padding: 0cm; background: #e6e6e6">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
- <FONT FACE="Courier New, monospace">&quot;C:\Program
- Files\FreeFileSync\RealtimeSync.exe&quot; &quot;C:\some
- folder\SyncJob.ffs_real&quot;</FONT></P>
+ <P><SPAN ID="Rahmen2" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Courier New, monospace">&quot;C:\Program
+ Files\FreeFileSync\RealtimeSync.exe&quot; &quot;<FONT COLOR="#000000"><FONT SIZE=3><SPAN STYLE="font-weight: normal">D</SPAN></FONT></FONT><FONT COLOR="#000000"><FONT SIZE=3><SPAN STYLE="font-weight: normal">:\</SPAN></FONT></FONT><FONT COLOR="#000000"><FONT SIZE=3><SPAN STYLE="font-weight: normal">Backup
+ Projects</SPAN></FONT></FONT>.ffs_real&quot;</FONT></P>
</SPAN><BR CLEAR=LEFT>
</P>
</UL>
<P><BR><BR>
</P>
<OL>
- <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">RealtimeSync
- should be monitoring only while a specific user is logged in: </SPAN></SPAN>Create
+ <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-variant: normal"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">RealtimeSync
+ should be monitoring only while a specific user is logged in: </SPAN></SPAN></SPAN>Create
a new shortcut, enter the command line from above as target and
place it into the user's autostart folder.</FONT></P>
- <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/create_shortcut.png" NAME="Grafik3" ALIGN=BOTTOM WIDTH=626 HEIGHT=338 BORDER=0></P>
- <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/shortcut_properties.png" NAME="Grafik4" ALIGN=BOTTOM WIDTH=377 HEIGHT=451 BORDER=0></P>
+ <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/create_shortcut.png" NAME="Grafik3" ALIGN=BOTTOM BORDER=0></P>
+ <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/shortcut_properties.png" NAME="Grafik4" ALIGN=BOTTOM BORDER=0></P>
<P STYLE="margin-bottom: 0cm">&nbsp;</P>
<LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">RealtimeSync
should be monitoring while Windows is online irrespective of
currently logged in users: Create a new task in your operating
systems's task scheduler and have it execute the command line above
- when the system starts. See <A HREF="Schedule%20a%20Batch%20Job.html">Schedule
+ when the system starts. See <A HREF="Schedule%20a%20batch%20job.html">Schedule
a Batch Job</A> for an example how to add a new task. Then change
- the user to run the task to </FONT><FONT FACE="Tahoma, sans-serif"><B>SYSTEM</B></FONT><FONT FACE="Tahoma, sans-serif">
- - a special user account always running in the background.</FONT></P>
- <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/schedule_realtimesync.png" NAME="Grafik1" ALIGN=BOTTOM WIDTH=708 HEIGHT=284 BORDER=0></P>
+ the user to run the task to </FONT><FONT FACE="Tahoma, sans-serif"><B>SYSTEM</B></FONT>
+ <FONT FACE="Tahoma, sans-serif">- a special user account always
+ running in the background.</FONT></P>
+ <P STYLE="margin-bottom: 0cm"><IMG SRC="../img/schedule_realtimesync.png" NAME="Grafik1" ALIGN=BOTTOM BORDER=0></P>
</OL>
<P STYLE="margin-bottom: 0cm"><BR>
</P>
diff --git a/BUILD/Help/html/Schedule a Batch Job.html b/BUILD/Help/html/Schedule a Batch Job.html
index c77dbaad..76393187 100644
--- a/BUILD/Help/html/Schedule a Batch Job.html
+++ b/BUILD/Help/html/Schedule a Batch Job.html
@@ -3,9 +3,9 @@
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252">
<TITLE></TITLE>
- <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)">
+ <META NAME="GENERATOR" CONTENT="LibreOffice 4.0.4.2 (Windows)">
<META NAME="CREATED" CONTENT="20091206;16574000">
- <META NAME="CHANGED" CONTENT="20130207;1441045">
+ <META NAME="CHANGED" CONTENT="20130722;18221462">
<STYLE TYPE="text/css">
<!--
@page { margin: 2cm }
@@ -16,28 +16,29 @@
</STYLE>
</HEAD>
<BODY LANG="de-DE" DIR="LTR">
-<H2 CLASS="western"><FONT FACE="Tahoma, sans-serif"><FONT SIZE=4 STYLE="font-size: 15pt">Schedule a batch job</FONT></FONT></H2>
+<H2 CLASS="western"><FONT FACE="Tahoma, sans-serif"><FONT SIZE=4 STYLE="font-size: 15pt">Schedule
+a batch job</FONT></FONT></H2>
<OL>
<LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Create
- a new batch job via FreeFileSync's main dialog: </FONT><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>Menu
- &rarr; Program &rarr; Save as batch job...</B></SPAN></FONT><FONT FACE="Tahoma, sans-serif"><BR>&nbsp;</FONT></P>
+ a new batch job via FreeFileSync's main dialog: </FONT><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>Menu
+ &rarr; Program &rarr; Save as batch job...</B></SPAN></FONT></SPAN><FONT FACE="Tahoma, sans-serif"><BR>&nbsp;</FONT></P>
<LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">If
the batch job shall run without user interaction or as part of an
unattended batch script, make sure that no popup dialog stops the
- progress:<BR>Disable checkbox <SPAN STYLE="font-style: normal"><B>Show
- progress dialog</B></SPAN> to avoid blocking while showing the
- result after synchronization. Alternatively you can select the &quot;On
- completion&quot; action <B>Close progress dialog</B> located in
- synchronization settings.<BR><B>Note:</B> Even if the progress
- dialog is not shown at the beginning, a user can make it visible
- <B>during</B> synchronization by double-clicking the FreeFileSync
- systray icon.<BR><IMG SRC="../img/SetupBatch.png" NAME="Grafik3" ALIGN=BOTTOM WIDTH=660 HEIGHT=263 BORDER=0></FONT></P>
+ progress:<BR>Disable checkbox <SPAN STYLE="font-variant: normal"><SPAN STYLE="font-style: normal"><B>Show
+ progress dialog</B></SPAN></SPAN> to avoid blocking while showing
+ the result after synchronization. Alternatively you can select the
+ &quot;On completion&quot; action <B>Close progress dialog</B>
+ located in synchronization settings.<BR><B>Note:</B> Even if the
+ progress dialog is not shown at the beginning, a user can make it
+ visible <B>during</B> synchronization by double-clicking the
+ FreeFileSync systray icon.<BR><IMG SRC="../img/SetupBatch.png" NAME="Grafik3" ALIGN=BOTTOM WIDTH=660 HEIGHT=263 BORDER=0></FONT></P>
<P STYLE="margin-bottom: 0cm">&nbsp;</P>
<LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">In
order to prevent error or warning popup messages from stopping
- progress, set <SPAN STYLE="font-style: normal"><B>Error handling</B></SPAN>
- to either <SPAN STYLE="font-style: normal"><B>Ignore</B></SPAN> or
- <SPAN STYLE="font-style: normal"><B>Exit</B></SPAN>.</FONT></P>
+ progress, set <SPAN STYLE="font-variant: normal"><SPAN STYLE="font-style: normal"><B>Error
+ handling</B></SPAN></SPAN> to either <SPAN STYLE="font-variant: normal"><SPAN STYLE="font-style: normal"><B>Ignore</B></SPAN></SPAN>
+ or <SPAN STYLE="font-variant: normal"><SPAN STYLE="font-style: normal"><B>Exit</B></SPAN></SPAN>.</FONT></P>
<P STYLE="margin-bottom: 0cm">&nbsp;</P>
<LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Setup
your operating system's scheduler</FONT></P>
@@ -56,13 +57,12 @@
and insert the <FONT FACE="Courier New, monospace">*.ffs_batch</FONT>
file into <B>Add arguments</B>.</FONT></FONT></FONT></P>
<LI><P STYLE="margin-bottom: 0cm; font-weight: normal"><FONT FACE="Tahoma, sans-serif"><FONT COLOR="#000000"><FONT SIZE=3>Use
- quotation marks to protect spaces in path names, e.g.: </FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>&quot;C:\some
- folder\SyncJob.ffs_batch&quot;</FONT></FONT></FONT><FONT COLOR="#000000"><FONT SIZE=3><BR></FONT></FONT><IMG SRC="../img/win7scheduler.png" NAME="Grafik1" ALIGN=BOTTOM WIDTH=708 HEIGHT=284 BORDER=0></FONT></P>
+ quotation marks to protect spaces in path names, e.g.: </FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>&quot;</FONT></FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>D</FONT></FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>:\</FONT></FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Backup
+ Projects</FONT></FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>.ffs_batch&quot;</FONT></FONT></FONT><FONT COLOR="#000000"><FONT SIZE=3><BR></FONT></FONT><IMG SRC="../img/win7scheduler.png" NAME="Grafik1" ALIGN=BOTTOM WIDTH=708 HEIGHT=284 BORDER=0></FONT></P>
</UL>
</OL>
- <P STYLE="margin-bottom: 0cm"><SPAN ID="Rahmen2" DIR="LTR" STYLE="float: left; width: 80%; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
- <FONT FACE="Tahoma, sans-serif"><B>Note</B></FONT><FONT FACE="Tahoma, sans-serif"><BR>Beginning
+ <P STYLE="margin-bottom: 0cm"><SPAN ID="Rahmen2" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note</B></FONT><FONT FACE="Tahoma, sans-serif"><BR>Beginning
with Windows Vista the &quot;Program/script&quot; always needs
point to an executable file like &quot;FreeFileSync.exe&quot; even
if ffs_batch file associations have been set!</FONT> I<FONT FACE="Tahoma, sans-serif">f
@@ -77,9 +77,10 @@
XP Scheduled Tasks:</B></FONT></P>
<UL>
<LI VALUE=1><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Go
- to <SPAN STYLE="font-style: normal"><B>Start &rarr; Control Panel
- &rarr; Scheduled Tasks</B></SPAN> and select <SPAN STYLE="font-style: normal"><B>Add
- Scheduled Task</B></SPAN>.</FONT></P>
+ to <SPAN STYLE="font-variant: normal"><SPAN STYLE="font-style: normal"><B>Start
+ &rarr; Control Panel &rarr; Scheduled Tasks</B></SPAN></SPAN> and
+ select <SPAN STYLE="font-variant: normal"><SPAN STYLE="font-style: normal"><B>Add
+ Scheduled Task</B></SPAN></SPAN>.</FONT></P>
<LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Follow
the wizard and select FreeFileSync.exe as program to run.</FONT></P>
<LI><P STYLE="margin-bottom: 0cm; font-weight: normal"><FONT FACE="Tahoma, sans-serif">Fill
@@ -94,12 +95,12 @@
Gnome-schedule, if necessary: <FONT FACE="Courier New, monospace">sudo
apt-get install gnome-schedule</FONT></FONT></P>
<LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-weight: normal">Go
- to </SPAN></FONT><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>System
- &rarr; Preferences &rarr; Scheduled tasks</B></SPAN></FONT></P>
- <LI><P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">Enter
- the command </SPAN></SPAN></FONT><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">as:</SPAN></SPAN></FONT><SPAN STYLE="font-style: normal">
- </SPAN><FONT FACE="Courier New, monospace"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">&lt;FreeFileSync
- installation folder&gt;/FreeFileSync &lt;job name&gt;.ffs_batch</SPAN></SPAN></FONT><FONT FACE="Courier New, monospace"><I><SPAN STYLE="font-weight: normal"><BR></SPAN></I></FONT><IMG SRC="../img/ubuntuScheduler.png" NAME="Grafik5" ALIGN=BOTTOM WIDTH=629 HEIGHT=560 BORDER=0></P>
+ to </SPAN></FONT><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><B>System
+ &rarr; Preferences &rarr; Scheduled tasks</B></SPAN></FONT></SPAN></P>
+ <LI><P STYLE="margin-bottom: 0cm"><SPAN STYLE="font-variant: normal"><FONT FACE="Tahoma, sans-serif"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">Enter
+ the command as:</SPAN></SPAN></FONT></SPAN><SPAN STYLE="font-variant: normal">
+ </SPAN><SPAN STYLE="font-variant: normal"><FONT FACE="Courier New, monospace"><SPAN STYLE="font-style: normal"><SPAN STYLE="font-weight: normal">&lt;FreeFileSync
+ installation folder&gt;/FreeFileSync &lt;job name&gt;.ffs_batch</SPAN></SPAN></FONT></SPAN><FONT FACE="Courier New, monospace"><I><SPAN STYLE="font-weight: normal"><BR></SPAN></I></FONT><IMG SRC="../img/ubuntuScheduler.png" NAME="Grafik5" ALIGN=BOTTOM WIDTH=629 HEIGHT=560 BORDER=0></P>
</UL>
</OL>
</OL>
diff --git a/BUILD/Help/html/Variable Drive Letters.html b/BUILD/Help/html/Variable Drive Letters.html
index 5d41c4ca..ee95e4a3 100644
--- a/BUILD/Help/html/Variable Drive Letters.html
+++ b/BUILD/Help/html/Variable Drive Letters.html
@@ -3,9 +3,9 @@
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252">
<TITLE></TITLE>
- <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1 (Win32)">
+ <META NAME="GENERATOR" CONTENT="LibreOffice 4.0.4.2 (Windows)">
<META NAME="CREATED" CONTENT="20091206;16574000">
- <META NAME="CHANGED" CONTENT="20130206;18481035">
+ <META NAME="CHANGED" CONTENT="20130722;18235553">
<STYLE TYPE="text/css">
<!--
@page { margin: 2cm }
@@ -19,27 +19,27 @@
<H2 CLASS="western"><FONT FACE="Tahoma, sans-serif"><FONT SIZE=4 STYLE="font-size: 15pt">Variable
drive letters</FONT></FONT></H2>
<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">USB
-memory sticks or external hard disks are often assigned different drive letters when plugged into
-distinct computers. FreeFileSync offers two solutions to handle this
-problem:</FONT></P>
+memory sticks or external hard disks often get different
+drive letters assigned when plugged into distinct computers. FreeFileSync
+offers two solutions to handle this problem:</FONT></P>
<P STYLE="margin-bottom: 0cm"><BR>
</P>
<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Option
1: </B>Specify a folder path by using the volume name:</FONT></P>
-<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen5" DIR="LTR" STYLE="float: left; width: 80%; border: none; padding: 0cm; background: #e6e6e6">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
- <FONT FACE="Tahoma, sans-serif">Use &quot;<FONT FACE="Courier New, monospace">[ZENJU-USB]\folder</FONT>&quot;
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen5" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: none; padding: 0cm; background: #e6e6e6">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Use
+ &quot;<FONT FACE="Courier New, monospace">[ZENJU-USB]\folder</FONT>&quot;
instead of &quot;<FONT FACE="Courier New, monospace">G:\folder</FONT>&quot;
where &quot;<FONT FACE="Courier New, monospace">ZENJU-USB</FONT>&quot;
- is the volume name of the USB stick which is currently mounted in drive
- G:\.</FONT></P>
+ is the volume name of the USB stick which is currently mounted in
+ drive G:\.</FONT></P>
</SPAN><BR CLEAR=LEFT><BR>
</P>
-<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen4" DIR="LTR" STYLE="float: left; width: 80%; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note<BR></B>It
- is not required to look up and enter the volume name manually! Just
- select the corresponding entry in the drop down menu.</FONT></P>
- <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><IMG SRC="../img/VolumeName.png" NAME="Grafik1" ALIGN=BOTTOM WIDTH=424 HEIGHT=86 BORDER=0></P>
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen4" DIR="LTR" STYLE="float: left; width: 80%; height: 0.04cm; border: 1px solid #000080; padding: 0.05cm; background: #ccccff">
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Note<BR></B>It
+ is not required to look up and enter the volume name manually. Just
+ select the corresponding entry in the drop down menu.</FONT></P>
+ <P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><IMG SRC="../img/VolumeName.png" NAME="Grafik1" ALIGN=BOTTOM WIDTH=424 HEIGHT=86 BORDER=0></P>
</SPAN><BR CLEAR=LEFT><BR>
</P>
<P STYLE="margin-bottom: 0cm"><BR>
@@ -47,20 +47,32 @@ problem:</FONT></P>
<P STYLE="margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif"><B>Option
2: </B></FONT><FONT FACE="Tahoma, sans-serif">Use a relative
directory name:</FONT></P>
-<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen1" DIR="LTR" STYLE="float: left; width: 80%; border: none; padding: 0cm; background: #e6e6e6">
- <LI><P STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Use
- &quot;</FONT><FONT FACE="Courier New, monospace">\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;
- instead of &quot;</FONT><FONT FACE="Courier New, monospace">G:\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;<BR>&nbsp;</FONT></P>
- <LI><P STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Save
- and copy synchronization settings to the USB stick:
- &quot;</FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>G:\settings.ffs_gui&quot;<BR>&nbsp;</FONT></FONT></FONT></P>
- <LI><P STYLE="margin-left: 0.79cm; margin-bottom: 0cm"><FONT FACE="Tahoma, sans-serif">Start
- FreeFileSync by double-clicking on &quot;</FONT><FONT FACE="Courier New, monospace">G:\settings.ffs_gui</FONT><FONT FACE="Tahoma, sans-serif">&quot;<BR>&rarr;
- Working directory is automatically set to &quot;</FONT><FONT FACE="Courier New, monospace">G:\</FONT><FONT FACE="Tahoma, sans-serif">&quot;
- by the operating system so that &quot;</FONT><FONT FACE="Courier New, monospace">\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;
- will be resolved as &quot;</FONT><FONT FACE="Courier New, monospace">G:\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;
- during synchronization.</FONT></P>
+
+
+<P STYLE="margin-left: 1.32cm; margin-bottom: 0cm"><SPAN ID="Rahmen3" DIR="LTR" STYLE="float: left; width: 80%; border: none; padding: 0.05cm; background: #e6e6e6">
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm">
+ <FONT FACE="Tahoma, sans-serif">Use
+ &quot;</FONT><FONT FACE="Courier New, monospace">\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;
+ instead of &quot;</FONT><FONT FACE="Courier New, monospace">G:\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;</FONT>
+ </P><BR>
+
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm; font-style: normal">
+ <FONT FACE="Tahoma, sans-serif">Save
+ and copy synchronization settings to the USB stick:
+ &quot;</FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>G:\</FONT></FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>Backup</FONT></FONT></FONT><FONT COLOR="#000000"><FONT FACE="Courier New, monospace"><FONT SIZE=3>.ffs_gui&quot;</FONT></FONT></FONT>
+ </P><BR>
+
+ <LI><P ALIGN=LEFT STYLE="margin-left: 0.79cm; margin-bottom: 0cm; font-style: normal">
+ <FONT FACE="Tahoma, sans-serif">Start
+ FreeFileSync by double-clicking on &quot;</FONT><FONT FACE="Courier New, monospace">G:\</FONT><FONT FACE="Courier New, monospace">Backup</FONT><FONT FACE="Courier New, monospace">.ffs_gui</FONT><FONT FACE="Tahoma, sans-serif">&quot;<BR>&rarr;
+ The working directory is automatically set to &quot;</FONT><FONT FACE="Courier New, monospace">G:\</FONT><FONT FACE="Tahoma, sans-serif">&quot;
+ by the operating system so that </FONT><FONT FACE="Tahoma, sans-serif">the
+ relative path </FONT><FONT FACE="Tahoma, sans-serif">&quot;</FONT><FONT FACE="Courier New, monospace">\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;
+ will be resolved as &quot;</FONT><FONT FACE="Courier New, monospace">G:\folder</FONT><FONT FACE="Tahoma, sans-serif">&quot;
+ during synchronization.</FONT>
+ </P>
</SPAN><BR CLEAR=LEFT><BR>
</P>
+
</BODY>
</HTML> \ No newline at end of file
diff --git a/BUILD/Help/img/RealtimeSync.png b/BUILD/Help/img/RealtimeSync.png
index 32706c6f..59b4fa01 100644
--- a/BUILD/Help/img/RealtimeSync.png
+++ b/BUILD/Help/img/RealtimeSync.png
Binary files differ
diff --git a/BUILD/Help/img/ScheduleBatch.png b/BUILD/Help/img/ScheduleBatch.png
index c991d1fd..214a2870 100644
--- a/BUILD/Help/img/ScheduleBatch.png
+++ b/BUILD/Help/img/ScheduleBatch.png
Binary files differ
diff --git a/BUILD/Help/img/WatchUsbInsert.png b/BUILD/Help/img/WatchUsbInsert.png
index b057d6b0..35d0b45d 100644
--- a/BUILD/Help/img/WatchUsbInsert.png
+++ b/BUILD/Help/img/WatchUsbInsert.png
Binary files differ
diff --git a/BUILD/Help/img/create_shortcut.png b/BUILD/Help/img/create_shortcut.png
index 2c42f3a1..7b0a5e2c 100644
--- a/BUILD/Help/img/create_shortcut.png
+++ b/BUILD/Help/img/create_shortcut.png
Binary files differ
diff --git a/BUILD/Help/img/schedule_realtimesync.png b/BUILD/Help/img/schedule_realtimesync.png
index ac1e2a80..ce52fd28 100644
--- a/BUILD/Help/img/schedule_realtimesync.png
+++ b/BUILD/Help/img/schedule_realtimesync.png
Binary files differ
diff --git a/BUILD/Help/img/shortcut_properties.png b/BUILD/Help/img/shortcut_properties.png
index 1e06b617..77e9a773 100644
--- a/BUILD/Help/img/shortcut_properties.png
+++ b/BUILD/Help/img/shortcut_properties.png
Binary files differ
diff --git a/BUILD/Help/img/win7scheduler.png b/BUILD/Help/img/win7scheduler.png
index 93f1e020..eabf331f 100644
--- a/BUILD/Help/img/win7scheduler.png
+++ b/BUILD/Help/img/win7scheduler.png
Binary files differ
diff --git a/BUILD/Languages/arabic.lng b/BUILD/Languages/arabic.lng
index 8d84a0b1..4d5bf07b 100644
--- a/BUILD/Languages/arabic.lng
+++ b/BUILD/Languages/arabic.lng
@@ -250,8 +250,8 @@
<target>الفحص:</target>
<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
</source>
<target>
<pluralform>0 بند</pluralform>
diff --git a/BUILD/Languages/croatian.lng b/BUILD/Languages/croatian.lng
index b87ebc76..8407fe7f 100644
--- a/BUILD/Languages/croatian.lng
+++ b/BUILD/Languages/croatian.lng
@@ -1,84 +1,12 @@
<header>
<language>Hrvatski</language>
- <translator>Slavko Blažević</translator>
+ <translator>Miroslav Vranić</translator>
<locale>hr_HR</locale>
<flag_image>flag_croatia.png</flag_image>
<plural_form_count>3</plural_form_count>
<plural_definition>n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2</plural_definition>
</header>
-<source>Cannot determine final path for %x.</source>
-<target></target>
-
-<source>Failed to register to receive system messages.</source>
-<target></target>
-
-<source>Cannot create symbolic link %x.</source>
-<target></target>
-
-<source>
-<pluralform>Do you really want to delete the following item?</pluralform>
-<pluralform>Do you really want to delete the following %x items?</pluralform>
-</source>
-<target></target>
-
-<source>
-<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform>
-<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform>
-</source>
-<target></target>
-
-<source>Do&n't save</source>
-<target></target>
-
-<source>
-<pluralform>%y of 1 row in view</pluralform>
-<pluralform>%y of %x rows in view</pluralform>
-</source>
-<target></target>
-
-<source>Select a variant</source>
-<target></target>
-
-<source>Check for new &version</source>
-<target></target>
-
-<source>&Tools</source>
-<target></target>
-
-<source>Cannot find current FreeFileSync version number online. Do you want to check manually?</source>
-<target></target>
-
-<source>&Download</source>
-<target></target>
-
-<source>New version found</source>
-<target></target>
-
-<source>Switching to FreeFileSync main dialog</source>
-<target></target>
-
-<source>Data verification error: %x and %y have different content.</source>
-<target></target>
-
-<source>Cannot determine volume name for %x.</source>
-<target></target>
-
-<source>Starting comparison</source>
-<target></target>
-
-<source>Resolving symbolic link %x</source>
-<target></target>
-
-<source>The following folders have dependent paths. Be careful when setting up synchronization rules:</source>
-<target></target>
-
-<source>The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:</source>
-<target></target>
-
-<source>The database entry is not in sync considering current settings.</source>
-<target></target>
-
<source>Both sides have changed since last synchronization.</source>
<target>Obje su strane promjenjene od posljednje sinkronizacije.</target>
@@ -88,6 +16,9 @@
<source>No change since last synchronization.</source>
<target>Nema promjena od zadnje sinkronizacije.</target>
+<source>The database entry is not in sync considering current settings.</source>
+<target>Unos podataka nije u sinkronizaciji uzevši u obzir trenutne postavke.</target>
+
<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source>
<target>Postavljam zadani sinkronizacijski smijer: Stare datoteke će biti prepisane novim datotekama.</target>
@@ -112,11 +43,20 @@
<source>Deleting symbolic link %x</source>
<target>Brisanje simboličnih poveznica %x</target>
+<source>The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:</source>
+<target>Koš nije dostupan za sljedeće mape. Datoteke će biti nepovratno izbrisane:</target>
+
<source>An exception occurred</source>
<target>Dogodilo se izuzeće</target>
-<source>Cannot find file %x.</source>
-<target>Ne mogu pronaći datoteku %x.</target>
+<source>A directory path is expected after %x.</source>
+<target>Putanja direktorija se očekuje nakon %x.</target>
+
+<source>Syntax error</source>
+<target>Greška sintakse</target>
+
+<source>Cannot open file %x.</source>
+<target>Ne mogu otvoriti datoteku %x.</target>
<source>Error</source>
<target>Greška</target>
@@ -124,6 +64,36 @@
<source>File %x does not contain a valid configuration.</source>
<target>Datoteka %x ne sadrži valjanu konfiguraciju</target>
+<source>Unequal number of left and right directories specified.</source>
+<target>Neravnomjeran broj lijevih i desnih direkotrija specificiran.</target>
+
+<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
+<target>Config datoteka ne smije sadržavati postavke na direktorij par razini ukoliko su direktoriji upisani preko naredbene linije.</target>
+
+<source>Warning</source>
+<target>Oprez</target>
+
+<source>Directories cannot be set for more than one configuration file.</source>
+<target>Direkotrij nemože biti postavljen za više od jedne konfigracijske datoteke.</target>
+
+<source>Syntax:</source>
+<target>Sintaksa:</target>
+
+<source>config files</source>
+<target>config datoteke</target>
+
+<source>directory</source>
+<target>direktorij</target>
+
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
+<target>Bilo koji broj FreeFileSync . ffs_gui i/ili .ffs_batch konfiguracijskih datoteka.</target>
+
+<source>Any number of alternative directories for at most one config file.</source>
+<target>Bilo koji broj alternativnih mapa za najviše jedanu config datoteku.</target>
+
+<source>Command line</source>
+<target>Naredbena linija</target>
+
<source>A folder input field is empty.</source>
<target>Polje za odabir foldera je prazno.</target>
@@ -136,6 +106,9 @@
<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source>
<target>Možete ignorirati ovu pogrešku te uzeti u obzir svaku mapu praznom. Mape se tada stvaraju automatski tokom sinkronizacije.</target>
+<source>The following folders have dependent paths. Be careful when setting up synchronization rules:</source>
+<target>Sljedeće mape imaju zavisnu putanju. Budite pažljivi prilikom postavljanja pravila za sinkronizaciju:</target>
+
<source>File %x has an invalid date.</source>
<target>Datoteka %x ima nevaljan datum.</target>
@@ -151,12 +124,18 @@
<source>Items differ in attributes only</source>
<target>Stavke se razlikuju samo u atributima</target>
+<source>Resolving symbolic link %x</source>
+<target>Rješavam simboličku vezu %x</target>
+
<source>Comparing content of files %x</source>
<target>Uspoređujem sadržaj datoteka %x</target>
<source>Generating file list...</source>
<target>Generiram listu datoteka...</target>
+<source>Starting comparison</source>
+<target>Počinjem usporedbu</target>
+
<source>Calculating sync directions...</source>
<target>Izračunavam smjerove sinkronizacije...</target>
@@ -236,7 +215,7 @@
<target>Ne mogu čitati datoteku %x.</target>
<source>Database files do not share a common session.</source>
-<target>Datoteke baze ne dijele zajednički protokol</target>
+<target>Datoteke baze ne dijele zajednički protokol.</target>
<source>Searching for folder %x...</source>
<target>Tražim mapu %x...</target>
@@ -245,7 +224,7 @@
<target>Ne mogu pročitati osobine od %x.</target>
<source>Cannot get process information.</source>
-<target>Ne mogu dobit iniformacije o procesu</target>
+<target>Ne mogu dobit informacije o procesu</target>
<source>Waiting while directory is locked (%x)...</source>
<target>Čekam dok se direktorij zaključava (%x)...</target>
@@ -273,13 +252,13 @@
<target>Ukupno vrijeme:</target>
<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
</source>
<target>
-<pluralform>%x Bajt</pluralform>
-<pluralform>%x Bajta</pluralform>
-<pluralform>%x Bajtova</pluralform>
+<pluralform>%x bajt</pluralform>
+<pluralform>%x bajta</pluralform>
+<pluralform>%x bajtova</pluralform>
</target>
<source>%x MB</source>
@@ -301,13 +280,13 @@
<target>Pretražujem:</target>
<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
</source>
<target>
-<pluralform>%x Stavka</pluralform>
-<pluralform>%x Stavke</pluralform>
-<pluralform>%x Stavki</pluralform>
+<pluralform>%x nit</pluralform>
+<pluralform>%x niti</pluralform>
+<pluralform>%x niti</pluralform>
</target>
<source>Encoding extended time information: %x</source>
@@ -316,6 +295,9 @@
<source>/sec</source>
<target>/sek</target>
+<source>%x items</source>
+<target>%x stavke</target>
+
<source>Configuration file %x loaded partially only.</source>
<target>Datoteka postavki %x učitana samo djelomično</target>
@@ -337,8 +319,11 @@
<source>Cannot load file %x.</source>
<target>Ne mogu učitati datoteku %x.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>Naziv particije %x dio naziva datoteke %y.</target>
+<source>Cannot determine volume name for %x.</source>
+<target>Ne mogu ustanoviti ime volumen for %x.</target>
+
+<source>Volume name %x is not part of file path %y.</source>
+<target>Ime voumena %x nije dio puta datotke %y.</target>
<source>Abort requested: Waiting for current operation to finish...</source>
<target>Prekid zahtjevan: čekam da se trenutna akcija završi...</target>
@@ -406,9 +391,6 @@
<source>Idle time between last detected change and execution of command</source>
<target>Vrijeme čekanja između zadnje prepoznate promjene i izvršenja naredbe</target>
-<source>Command line</source>
-<target>Naredbena linija</target>
-
<source>
The command is triggered if:
- files or subfolders change
@@ -432,9 +414,6 @@ Naredba će biti pokrenuta ako se:
<source>RealtimeSync - Automated Synchronization</source>
<target>RealtimeSync - Automatska Sinkronizacija</target>
-<source>Warning</source>
-<target>Oprez</target>
-
<source>Build: %x</source>
<target>Inačnica: %x</target>
@@ -444,15 +423,21 @@ Naredba će biti pokrenuta ako se:
<source>All files</source>
<target>Sve datoteke</target>
+<source>Directory monitoring active</source>
+<target>Nadzor direktorija je aktivan</target>
+
+<source>Waiting until all directories are available...</source>
+<target>Čekanje na dostupnost svih direktorija...</target>
+
<source>&Restore</source>
<target>&Vrati</target>
+<source>&Show error</source>
+<target>&Prikaži grešku</target>
+
<source>&Exit</source>
<target>&Izlaz</target>
-<source>Waiting for missing directories...</source>
-<target>Čekam nedostajuće direktorije...</target>
-
<source>Invalid command line:</source>
<target>Netočna naredba:</target>
@@ -462,14 +447,14 @@ Naredba će biti pokrenuta ako se:
<source>File time and size</source>
<target>Vrijeme i veličina datoteke</target>
-<source> Two way </source>
+<source>Two way</source>
<target>Dvosmjerno</target>
<source>Mirror</source>
-<target>Zrcalno </target>
+<target>Zrcalno</target>
<source>Update</source>
-<target>Ažuriraj </target>
+<target>Ažuriraj</target>
<source>Custom</source>
<target>Uobičajeno</target>
@@ -514,22 +499,19 @@ Naredba će biti pokrenuta ako se:
<target>Odredišna mapa %x već postoji.</target>
<source>Target folder input field must not be empty.</source>
-<target>Odredišna mapa ne može biti prazna</target>
+<target>Odredišna mapa ne može biti prazna.</target>
<source>Folder input field for versioning must not be empty.</source>
-<target>Mapa unosa ne smije biti prazan</target>
+<target>Mapa unosa ne smije biti prazna.</target>
<source>Source folder %x not found.</source>
-<target>Izvorna mapa %x nije pronađena</target>
+<target>Izvorna mapa %x nije pronađena.</target>
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Slijedeće stavke imaju nedefiniranih sukoba te neće biti sinkronizirane:</target>
-<source>Significant difference detected:</source>
-<target>Značajna razlika opažena:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>Više od 50% od ukupnog broja datoteka će biti kopirano ili izbrisano.</target>
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
+<target>Navedene mape se značajno razlikuju. Prvojerite dali uspoređujete ispravne mape za sinkronizaciju.</target>
<source>Not enough free disk space available in:</source>
<target>Nedovoljno prostora na disku:</target>
@@ -552,6 +534,9 @@ Naredba će biti pokrenuta ako se:
<source>Creating Volume Shadow Copy for %x...</source>
<target>Izrađujem Volume Shadow Kopiju za %x...</target>
+<source>Data verification error: %x and %y have different content.</source>
+<target>Greška pri provjeri podataka: %x i %y imaju različit sadržaj.</target>
+
<source>Synchronization aborted</source>
<target>Sinkronizacija prekinuta</target>
@@ -573,12 +558,24 @@ Naredba će biti pokrenuta ako se:
<source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source>
<target>Preisni "Zamjeni" za riješavanje problema u FreeFileSync glavnom dijaloškom okviru</target>
+<source>Switching to FreeFileSync main dialog</source>
+<target>Prebacujem na FreeFileSync glavni dijaloški prozor</target>
+
+<source>Retrying operation after error:</source>
+<target>Ponavljam operaciju nakon greške:</target>
+
<source>A new version of FreeFileSync is available:</source>
<target>Nova verzija FreeFileSync je dostupna:</target>
<source>Download now?</source>
<target>Preuzeti sada?</target>
+<source>New version found</source>
+<target>Nova verzija je nađena</target>
+
+<source>&Download</source>
+<target>&Preuzmi</target>
+
<source>FreeFileSync is up to date.</source>
<target>FreeFileSync je ažuriran.</target>
@@ -588,6 +585,9 @@ Naredba će biti pokrenuta ako se:
<source>Unable to connect to sourceforge.net.</source>
<target>Ne mogu se povezati na sourceforge.net.</target>
+<source>Cannot find current FreeFileSync version number online. Do you want to check manually?</source>
+<target>Ne mogu pronaći trenutnu verziju FreeFileSync-a online. Dali želite ručno potražiti?</target>
+
<source>Symlink</source>
<target>Poveznica simbola</target>
@@ -690,12 +690,18 @@ Naredba će biti pokrenuta ako se:
<source>&Global settings...</source>
<target>&Globalne postavke...</target>
+<source>&Tools</source>
+<target>&Alati</target>
+
<source>&Check now</source>
<target>&Provjeri sada</target>
<source>Check &automatically once a week</source>
<target>Provjeri &automatski jednom tjedno</target>
+<source>Check for new &version</source>
+<target>Prvojeri dostupnost nove &verzije</target>
+
<source>Compare</source>
<target>Usporedi</target>
@@ -753,6 +759,9 @@ Naredba će biti pokrenuta ako se:
<source>Synchronizing...</source>
<target>Sinkroniziram...</target>
+<source>Minimize to notification area</source>
+<target>Spusti u notifikacije</target>
+
<source>On completion</source>
<target>Pri završetku</target>
@@ -762,6 +771,18 @@ Naredba će biti pokrenuta ako se:
<source>&Pause</source>
<target>&Pauziraj</target>
+<source>Variant</source>
+<target>Način</target>
+
+<source>Statistics</source>
+<target>Statistika</target>
+
+<source>Don't show this dialog again</source>
+<target>Ne prikazuj ovaj prozor ponovno</target>
+
+<source>Select a variant</source>
+<target>Odaberite varijantu</target>
+
<source>
Files are found equal if
- last write time and date
@@ -952,7 +973,7 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<target>Kopiraj datotečna dopuštenja</target>
<source>Transfer file and folder permissions (Requires Administrator rights)</source>
-<target>Premjesti datotečna dopuštenja (Zahtjeva Administratorska prava)</target>
+<target>Premjesti datotečna dopuštenja (Zahtjeva administratorska prava)</target>
<source>Restore hidden dialogs</source>
<target>Prikaži skrivene prozore</target>
@@ -966,17 +987,8 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>&Default</source>
<target>&Zadano</target>
-<source>Variant</source>
-<target>Način</target>
-
-<source>Statistics</source>
-<target>Statistika</target>
-
-<source>Don't show this dialog again</source>
-<target>Ne prikazuj ovaj prozor ponovno</target>
-
<source>Find what:</source>
-<target>Nađi što</target>
+<target>Nađi što:</target>
<source>Match case</source>
<target>Poklopi se</target>
@@ -984,15 +996,15 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>&Find next</source>
<target>&Nađi slijedeće</target>
+<source>Start synchronization</source>
+<target>Započni sinkronizaciju</target>
+
<source>Delete</source>
<target>Izbriši</target>
<source>Configure filter</source>
<target>Konfiguriraj filter</target>
-<source>Start synchronization</source>
-<target>Započni sinkronizaciju</target>
-
<source>Find</source>
<target>Pronađi</target>
@@ -1027,6 +1039,25 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<target>Usporedi obje strane</target>
<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target>
+<pluralform>Dali stvarno želite izvršiti nardebu %y za %x stavku?</pluralform>
+<pluralform>Dali stvarno želite izvršiti nardebu %y za %x stavke?</pluralform>
+<pluralform>Dali stvarno želite izvršiti nardebu %y za %x stavki?</pluralform>
+</target>
+
+<source>Confirm</source>
+<target>Potvrdi</target>
+
+<source>&Execute</source>
+<target>&Izvrši</target>
+
+<source>&Don't show this dialog again</source>
+<target>&Nemoj više prikazivati ovaj dijaloški prozor</target>
+
+<source>
<pluralform>1 directory</pluralform>
<pluralform>%x directories</pluralform>
</source>
@@ -1046,11 +1077,21 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<pluralform>%x datoteka</pluralform>
</target>
+<source>
+<pluralform>%y of 1 row in view</pluralform>
+<pluralform>%y of %x rows in view</pluralform>
+</source>
+<target>
+<pluralform>%y od %x reda u prikazu</pluralform>
+<pluralform>%y od %x reda u prikazu</pluralform>
+<pluralform>%y od %x reda u prikazu</pluralform>
+</target>
+
<source>Set direction:</source>
<target>Odaberi smijer:</target>
<source>Exclude temporarily</source>
-<target>Trenutno izključi</target>
+<target>Izuzmi privremeno</target>
<source>Include temporarily</source>
<target>Trenutno uključi</target>
@@ -1103,6 +1144,9 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>Do you want to save changes to %x?</source>
<target>Da li želite spremiti izmjene za %x?</target>
+<source>Do&n't save</source>
+<target>&Nemoj spremiti</target>
+
<source>Never save changes</source>
<target>Nikad ne spremaj promjene</target>
@@ -1157,8 +1201,8 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>All folders are in sync</source>
<target>Sve mape su u sinkronizaciji</target>
-<source>Comma separated list</source>
-<target>Zarezom odvojene liste</target>
+<source>Comma-separated values</source>
+<target>Zarezom odvojene vrijednosti</target>
<source>File list exported</source>
<target>Datotečna lista izvezena</target>
@@ -1211,17 +1255,14 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>Completed</source>
<target>Završeno</target>
-<source>Continue</source>
-<target>Nastavi</target>
-
-<source>Pause</source>
-<target>Pauziraj</target>
+<source>&Continue</source>
+<target>&Nastavi</target>
-<source>Logging</source>
-<target>Zapisivanje</target>
+<source>Log</source>
+<target>Dnevnik</target>
<source>Cannot find %x</source>
-<target>Nemogu pronać %x</target>
+<target>Nemogu pronaći %x</target>
<source>Inactive</source>
<target>Neaktivno</target>
@@ -1253,6 +1294,26 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>Filter</source>
<target>Filtriranje</target>
+<source>
+<pluralform>Do you really want to move the following item to the Recycle Bin?</pluralform>
+<pluralform>Do you really want to move the following %x items to the Recycle Bin?</pluralform>
+</source>
+<target>
+<pluralform>Dali stvarno želite premjestiti sljedeću %x stavku u koš?</pluralform>
+<pluralform>Dali stvarno želite premjestiti sljedeće %x stavke u koš?</pluralform>
+<pluralform>Dali stvarno želite premjestiti sljedećih %x stavki u koš?</pluralform>
+</target>
+
+<source>
+<pluralform>Do you really want to delete the following item?</pluralform>
+<pluralform>Do you really want to delete the following %x items?</pluralform>
+</source>
+<target>
+<pluralform>Dali stvarno želite obrisati sljedeću %x stavku?</pluralform>
+<pluralform>Dali stvarno želite obristi sljedeće %x stavke?</pluralform>
+<pluralform>Dali stvarno želite obristi sljedećih %x stavki?</pluralform>
+</target>
+
<source>Direct</source>
<target>Neposredno</target>
@@ -1295,9 +1356,6 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>Append a timestamp to each file name</source>
<target>Dodaj vremensku oznaku svakom imenu datoteke</target>
-<source>Folder</source>
-<target>Mapa</target>
-
<source>File</source>
<target>Datoteka</target>
@@ -1352,6 +1410,9 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>Cannot create directory %x.</source>
<target>Ne mogu izraditi mapu %x.</target>
+<source>Cannot create symbolic link %x.</source>
+<target>Ne mogu stvoriti simboličku vezu %x.</target>
+
<source>Cannot find system function %x.</source>
<target>Ne mogu pronaći sistemsku funkciju %x.</target>
@@ -1406,6 +1467,9 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<pluralform>%x dana</pluralform>
</target>
+<source>Failed to register to receive system messages.</source>
+<target>Neuspijela registracija za primanje sistemskih poruka.</target>
+
<source>Cannot set privilege %x.</source>
<target>Ne mogu postaviti prava za %x.</target>
@@ -1418,6 +1482,9 @@ Napomena: Imena datoteka moraju biti srodni s glavnim mapama.
<source>Unable to move %x to the Recycle Bin.</source>
<target>Nije moguće premjestiti %x u koš za smeće.</target>
+<source>Cannot determine final path for %x.</source>
+<target>Ne mogu odrediti završnu putanju za %x.</target>
+
<source>Error Code %x:</source>
<target>Pogreška broj %x:</target>
diff --git a/BUILD/Languages/dutch.lng b/BUILD/Languages/dutch.lng
index eee7d1fb..911893b9 100644
--- a/BUILD/Languages/dutch.lng
+++ b/BUILD/Languages/dutch.lng
@@ -7,57 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>Cannot determine final path for %x.</source>
-<target></target>
-
-<source>Failed to register to receive system messages.</source>
-<target></target>
-
-<source>Cannot create symbolic link %x.</source>
-<target></target>
-
-<source>Select a variant</source>
-<target></target>
-
-<source>Check for new &version</source>
-<target></target>
-
-<source>&Tools</source>
-<target></target>
-
-<source>Cannot find current FreeFileSync version number online. Do you want to check manually?</source>
-<target></target>
-
-<source>&Download</source>
-<target></target>
-
-<source>New version found</source>
-<target></target>
-
-<source>Switching to FreeFileSync main dialog</source>
-<target></target>
-
-<source>Data verification error: %x and %y have different content.</source>
-<target></target>
-
-<source>Cannot determine volume name for %x.</source>
-<target></target>
-
-<source>Starting comparison</source>
-<target></target>
-
-<source>Resolving symbolic link %x</source>
-<target></target>
-
-<source>The following folders have dependent paths. Be careful when setting up synchronization rules:</source>
-<target></target>
-
-<source>The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:</source>
-<target></target>
-
-<source>The database entry is not in sync considering current settings.</source>
-<target></target>
-
<source>Both sides have changed since last synchronization.</source>
<target>Beide zijdes zijn veranderd sinds de laatste synchronisatie.</target>
@@ -67,6 +16,9 @@
<source>No change since last synchronization.</source>
<target>Geen veranderingen sinds de laatste synchronisatie.</target>
+<source>The database entry is not in sync considering current settings.</source>
+<target>De database is niet gesynchroniseerd volgens de huidige instellingen.</target>
+
<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source>
<target>Stel standaard synchronisatie richtingen in: Oude bestanden worden door nieuwere bestanden overschreven.</target>
@@ -91,11 +43,20 @@
<source>Deleting symbolic link %x</source>
<target>Verwijderen van snelkoppeling %x</target>
+<source>The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:</source>
+<target>De Prullenbak is niet beschikbaar voor de volgende mappen. De bestanden zullen permanent worden verwijderd:</target>
+
<source>An exception occurred</source>
<target>Er heeft een uitzondering plaatsgevonden</target>
-<source>Cannot find file %x.</source>
-<target>Kan bestand %x niet vinden.</target>
+<source>A directory path is expected after %x.</source>
+<target>Een bestandslocatie pad is verwacht na %x.</target>
+
+<source>Syntax error</source>
+<target>Syntax fout</target>
+
+<source>Cannot open file %x.</source>
+<target>Kan bestand %x niet openen.</target>
<source>Error</source>
<target>Fout</target>
@@ -103,6 +64,36 @@
<source>File %x does not contain a valid configuration.</source>
<target>Bestand %x bevat geen valide configuratie.</target>
+<source>Unequal number of left and right directories specified.</source>
+<target>Oneven nummer van linker en rechter bestandslocaties gespecificeerd.</target>
+
+<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
+<target>Het configuratiebestand mag geen instellingen bevatten voor een bestandslocatie level als bestandslocaties in de command line ingesteld zijn.</target>
+
+<source>Warning</source>
+<target>Waarschuwing</target>
+
+<source>Directories cannot be set for more than one configuration file.</source>
+<target>Bestandslocaties kunnen niet voor meer dan een configuratiebestand gemaakt worden.</target>
+
+<source>Syntax:</source>
+<target>Syntax:</target>
+
+<source>config files</source>
+<target>configuratiebestanden</target>
+
+<source>directory</source>
+<target>bestandslocatie</target>
+
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
+<target>Elk aantal FreeFileSync .ffs_gui en/of .ffs_batch configuratiebestanden.</target>
+
+<source>Any number of alternative directories for at most one config file.</source>
+<target>Elk aantal alternatieve bestandslocaties voor een configuratiebestand.</target>
+
+<source>Command line</source>
+<target>Opdrachtregel</target>
+
<source>A folder input field is empty.</source>
<target>Een map invoerveld is leeg.</target>
@@ -115,6 +106,9 @@
<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source>
<target>U kunt deze foutmelding negeren om elke map als leeg te markeren. De mappen worden dan automatisch aangemaakt tijdens de synchronisatie.</target>
+<source>The following folders have dependent paths. Be careful when setting up synchronization rules:</source>
+<target>De volgende mappen hebben afhankelijke paden. Wees voorzichtig bij het opzetten van synchronisatieregels:</target>
+
<source>File %x has an invalid date.</source>
<target>Bestand %x heeft een ongeldige datum.</target>
@@ -130,12 +124,18 @@
<source>Items differ in attributes only</source>
<target>Items verschillen alleen in attributen</target>
+<source>Resolving symbolic link %x</source>
+<target>Bezig met vinden van snelkoppeling %x</target>
+
<source>Comparing content of files %x</source>
<target>De inhoud van %x bestanden wordt vergeleken</target>
<source>Generating file list...</source>
<target>Genereren van bestandslijst...</target>
+<source>Starting comparison</source>
+<target>Vergelijking starten</target>
+
<source>Calculating sync directions...</source>
<target>Synchronisatie richtingen calculeren...</target>
@@ -251,12 +251,12 @@
<target>Totale tijd:</target>
<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
</source>
<target>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
</target>
<source>%x MB</source>
@@ -278,12 +278,12 @@
<target>Doorzoekt:</target>
<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
</source>
<target>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
</target>
<source>Encoding extended time information: %x</source>
@@ -292,6 +292,9 @@
<source>/sec</source>
<target>/sec</target>
+<source>%x items</source>
+<target>%x items</target>
+
<source>Configuration file %x loaded partially only.</source>
<target>Configuratiebestand %x alleen deels geladen.</target>
@@ -313,8 +316,11 @@
<source>Cannot load file %x.</source>
<target>Kan bestand %x niet laden.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>Schijfnaam %x maakt geen deel uit van bestandsnaam %y.</target>
+<source>Cannot determine volume name for %x.</source>
+<target>Kan volumenaam voor %x niet bepalen.</target>
+
+<source>Volume name %x is not part of file path %y.</source>
+<target>Schijfnaam %x is geen deel van bestandspad %y.</target>
<source>Abort requested: Waiting for current operation to finish...</source>
<target>Bezig met afbreken: Wacht op beëindiging huidige bewerking...</target>
@@ -382,9 +388,6 @@
<source>Idle time between last detected change and execution of command</source>
<target>Tijd tussen de laatste gedetecteerde verandering en de uitvoering van het commando</target>
-<source>Command line</source>
-<target>Opdrachtregel</target>
-
<source>
The command is triggered if:
- files or subfolders change
@@ -408,9 +411,6 @@ De opdracht word geactiveerd als:
<source>RealtimeSync - Automated Synchronization</source>
<target>RealtimeSync - Geautomatiseerde Synchronisatie</target>
-<source>Warning</source>
-<target>Waarschuwing</target>
-
<source>Build: %x</source>
<target>Build: %x</target>
@@ -420,15 +420,21 @@ De opdracht word geactiveerd als:
<source>All files</source>
<target>Alle bestanden</target>
+<source>Directory monitoring active</source>
+<target>Bestandslocatie controle actief</target>
+
+<source>Waiting until all directories are available...</source>
+<target>Wachten tot alle bestandslocaties beschikbaar zijn...</target>
+
<source>&Restore</source>
<target>&Herstellen</target>
+<source>&Show error</source>
+<target>Laat &fouten zien</target>
+
<source>&Exit</source>
<target>&Afsluiten</target>
-<source>Waiting for missing directories...</source>
-<target>Wacht op ontbrekende mappen...</target>
-
<source>Invalid command line:</source>
<target>Ongeldige opdrachtregel:</target>
@@ -438,14 +444,14 @@ De opdracht word geactiveerd als:
<source>File time and size</source>
<target>Bestandstijd- en grootte</target>
-<source> Two way </source>
-<target> Twee kanten op </target>
+<source>Two way</source>
+<target>Twee kanten op</target>
<source>Mirror</source>
-<target>Spiegelen </target>
+<target>Spiegelen</target>
<source>Update</source>
-<target>Bijwerken </target>
+<target>Bijwerken</target>
<source>Custom</source>
<target>Aangepast</target>
@@ -501,11 +507,8 @@ De opdracht word geactiveerd als:
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd:</target>
-<source>Significant difference detected:</source>
-<target>Significant verschil gedetecteerd:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>Meer dan 50% van alle bestanden zal gekopiëerd of verwijderd worden.</target>
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
+<target>De volgende mappen zijn heel verschillend. Wees er zeker van dat dit de goede mappen zijn voor synchronisatie.</target>
<source>Not enough free disk space available in:</source>
<target>Niet genoeg vrije schijfruimte beschikbaar op:</target>
@@ -528,6 +531,9 @@ De opdracht word geactiveerd als:
<source>Creating Volume Shadow Copy for %x...</source>
<target>Bezig met Volume Schaduwkopie te maken voor %x...</target>
+<source>Data verification error: %x and %y have different content.</source>
+<target>Data verificatie fout: %x en %y hebben een verschillende inhoud.</target>
+
<source>Synchronization aborted</source>
<target>Synchronisatie afgebroken</target>
@@ -549,12 +555,24 @@ De opdracht word geactiveerd als:
<source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source>
<target>Druk op "Omschakelen" om problemen op te lossen in het hoofdscherm van FreeFileSync.</target>
+<source>Switching to FreeFileSync main dialog</source>
+<target>Wisselen naar FreeFileSync hoofdscherm</target>
+
+<source>Retrying operation after error:</source>
+<target>Bewerking opnieuw proberen na fout:</target>
+
<source>A new version of FreeFileSync is available:</source>
<target>Er is een nieuwe versie van FreeFileSync beschikbaar:</target>
<source>Download now?</source>
<target>Nu downloaden?</target>
+<source>New version found</source>
+<target>Nieuwe versie gevonden</target>
+
+<source>&Download</source>
+<target>&Download</target>
+
<source>FreeFileSync is up to date.</source>
<target>U gebruikt de nieuwste versie van FreeFileSync.</target>
@@ -564,6 +582,9 @@ De opdracht word geactiveerd als:
<source>Unable to connect to sourceforge.net.</source>
<target>Kan geen verbinding maken met sourceforge.net.</target>
+<source>Cannot find current FreeFileSync version number online. Do you want to check manually?</source>
+<target>Kan huidige FreeFileSync versienummer niet online vinden. Wilt u handmatig controleren?</target>
+
<source>Symlink</source>
<target>Symlink</target>
@@ -666,12 +687,18 @@ De opdracht word geactiveerd als:
<source>&Global settings...</source>
<target>&Algemene instellingen...</target>
+<source>&Tools</source>
+<target>&Gereedschap</target>
+
<source>&Check now</source>
<target>&Controleer nu</target>
<source>Check &automatically once a week</source>
<target>Controleer &automatisch eens per week</target>
+<source>Check for new &version</source>
+<target>Kijk voor een nieuwe &versie</target>
+
<source>Compare</source>
<target>Vergelijk</target>
@@ -729,6 +756,9 @@ De opdracht word geactiveerd als:
<source>Synchronizing...</source>
<target>Synchroniseert...</target>
+<source>Minimize to notification area</source>
+<target>Minimaliseer naar systeemvak</target>
+
<source>On completion</source>
<target>Na voltooiing</target>
@@ -738,6 +768,18 @@ De opdracht word geactiveerd als:
<source>&Pause</source>
<target>&Pauze</target>
+<source>Variant</source>
+<target>Variant</target>
+
+<source>Statistics</source>
+<target>Statistieken</target>
+
+<source>Don't show this dialog again</source>
+<target>Laat deze dialoog niet meer zien</target>
+
+<source>Select a variant</source>
+<target>Selecteer een variant</target>
+
<source>
Files are found equal if
- last write time and date
@@ -942,15 +984,6 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<source>&Default</source>
<target>&Standaard</target>
-<source>Variant</source>
-<target>Variant</target>
-
-<source>Statistics</source>
-<target>Statistieken</target>
-
-<source>Don't show this dialog again</source>
-<target>Laat deze dialoog niet meer zien</target>
-
<source>Find what:</source>
<target>Vind wat:</target>
@@ -960,15 +993,15 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<source>&Find next</source>
<target>&Vind volgende</target>
+<source>Start synchronization</source>
+<target>Start synchronisatie</target>
+
<source>Delete</source>
<target>Verwijderen</target>
<source>Configure filter</source>
<target>Filterconfiguratie</target>
-<source>Start synchronization</source>
-<target>Start synchronisatie</target>
-
<source>Find</source>
<target>Vind</target>
@@ -1003,6 +1036,24 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<target>Vergelijk beide zijdes</target>
<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target>
+<pluralform>Weet u zeker dat u commando %y wilt uitvoeren voor 1 item?</pluralform>
+<pluralform>Weet u zeker dat u commando %y wilt uitvoeren voor %x items?</pluralform>
+</target>
+
+<source>Confirm</source>
+<target>Bevestigen</target>
+
+<source>&Execute</source>
+<target>&Uitvoeren</target>
+
+<source>&Don't show this dialog again</source>
+<target>Laat deze dialoog &niet meer zien</target>
+
+<source>
<pluralform>1 directory</pluralform>
<pluralform>%x directories</pluralform>
</source>
@@ -1143,8 +1194,8 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<source>All folders are in sync</source>
<target>Alle mappen zijn gesynchroniseerd</target>
-<source>Comma separated list</source>
-<target>Kommagescheiden bestand</target>
+<source>Comma-separated values</source>
+<target>Door komma gescheiden waarden</target>
<source>File list exported</source>
<target>Bestandslijst geëxporteerd</target>
@@ -1197,14 +1248,11 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<source>Completed</source>
<target>Voltooid</target>
-<source>Continue</source>
-<target>Doorgaan</target>
+<source>&Continue</source>
+<target>&Doorgaan</target>
-<source>Pause</source>
-<target>Pauze</target>
-
-<source>Logging</source>
-<target>Loggen</target>
+<source>Log</source>
+<target>Log</target>
<source>Cannot find %x</source>
<target>Kan %x niet vinden</target>
@@ -1299,9 +1347,6 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<source>Append a timestamp to each file name</source>
<target>Voeg een timestamp aan elke bestandsnaam toe</target>
-<source>Folder</source>
-<target>Map</target>
-
<source>File</source>
<target>Bestand</target>
@@ -1356,6 +1401,9 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<source>Cannot create directory %x.</source>
<target>Kan map %x niet aanmaken.</target>
+<source>Cannot create symbolic link %x.</source>
+<target>Kan snelkoppeling %x niet aanmaken.</target>
+
<source>Cannot find system function %x.</source>
<target>Kan systeemfunctie %x niet vinden.</target>
@@ -1407,6 +1455,9 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<pluralform>%x dagen</pluralform>
</target>
+<source>Failed to register to receive system messages.</source>
+<target>Registreren voor het ontvangen van systeemmeldingen is mislukt.</target>
+
<source>Cannot set privilege %x.</source>
<target>Kan privilege %x niet instellen.</target>
@@ -1419,6 +1470,9 @@ Opmerking: Bestandsnamen moeten relatief zijn aan de basis mappen.
<source>Unable to move %x to the Recycle Bin.</source>
<target>Niet mogelijk om %x naar de Prullenbak te verplaatsen.</target>
+<source>Cannot determine final path for %x.</source>
+<target>Kan pad voor %x niet vaststellen.</target>
+
<source>Error Code %x:</source>
<target>Foutcode %x:</target>
diff --git a/BUILD/Languages/english_uk.lng b/BUILD/Languages/english_uk.lng
index d1fcab3d..753ccfd0 100644
--- a/BUILD/Languages/english_uk.lng
+++ b/BUILD/Languages/english_uk.lng
@@ -49,8 +49,14 @@
<source>An exception occurred</source>
<target>An exception occurred</target>
-<source>Cannot find file %x.</source>
-<target>Cannot find file %x.</target>
+<source>A directory path is expected after %x.</source>
+<target>A directory path is expected after %x.</target>
+
+<source>Syntax error</source>
+<target>Syntax error</target>
+
+<source>Cannot open file %x.</source>
+<target>Cannot open file %x.</target>
<source>Error</source>
<target>Error</target>
@@ -58,6 +64,36 @@
<source>File %x does not contain a valid configuration.</source>
<target>File %x does not contain a valid configuration.</target>
+<source>Unequal number of left and right directories specified.</source>
+<target>Unequal number of left and right directories specified.</target>
+
+<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
+<target>The config file must not contain settings at directory pair level when directories are set via command line.</target>
+
+<source>Warning</source>
+<target>Warning</target>
+
+<source>Directories cannot be set for more than one configuration file.</source>
+<target>Directories cannot be set for more than one configuration file.</target>
+
+<source>Syntax:</source>
+<target>Syntax:</target>
+
+<source>config files</source>
+<target>config files</target>
+
+<source>directory</source>
+<target>directory</target>
+
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
+<target>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</target>
+
+<source>Any number of alternative directories for at most one config file.</source>
+<target>Any number of alternative directories for at most one config file.</target>
+
+<source>Command line</source>
+<target>Command line</target>
+
<source>A folder input field is empty.</source>
<target>A folder input field is empty.</target>
@@ -215,12 +251,12 @@
<target>Total time:</target>
<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
</source>
<target>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
</target>
<source>%x MB</source>
@@ -242,12 +278,12 @@
<target>Scanning:</target>
<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
</source>
<target>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
</target>
<source>Encoding extended time information: %x</source>
@@ -256,6 +292,9 @@
<source>/sec</source>
<target>/sec</target>
+<source>%x items</source>
+<target>%x items</target>
+
<source>Configuration file %x loaded partially only.</source>
<target>Configuration file %x loaded partially only.</target>
@@ -280,8 +319,8 @@
<source>Cannot determine volume name for %x.</source>
<target>Cannot determine volume name for %x.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>Volume name %x not part of file name %y.</target>
+<source>Volume name %x is not part of file path %y.</source>
+<target>Volume name %x is not part of file path %y.</target>
<source>Abort requested: Waiting for current operation to finish...</source>
<target>Abort requested: Waiting for current operation to finish...</target>
@@ -349,9 +388,6 @@
<source>Idle time between last detected change and execution of command</source>
<target>Idle time between last detected change and execution of command</target>
-<source>Command line</source>
-<target>Command line</target>
-
<source>
The command is triggered if:
- files or subfolders change
@@ -375,9 +411,6 @@ The command is triggered if:
<source>RealtimeSync - Automated Synchronization</source>
<target>RealtimeSync - Automated Synchronisation</target>
-<source>Warning</source>
-<target>Warning</target>
-
<source>Build: %x</source>
<target>Build: %x</target>
@@ -387,15 +420,21 @@ The command is triggered if:
<source>All files</source>
<target>All files</target>
+<source>Directory monitoring active</source>
+<target>Directory monitoring active</target>
+
+<source>Waiting until all directories are available...</source>
+<target>Waiting until all directories are available...</target>
+
<source>&Restore</source>
<target>&Restore</target>
+<source>&Show error</source>
+<target>&Show error</target>
+
<source>&Exit</source>
<target>&Exit</target>
-<source>Waiting for missing directories...</source>
-<target>Waiting for missing directories...</target>
-
<source>Invalid command line:</source>
<target>Invalid command line:</target>
@@ -405,14 +444,14 @@ The command is triggered if:
<source>File time and size</source>
<target>File time and size</target>
-<source> Two way </source>
-<target> Two way </target>
+<source>Two way</source>
+<target>Two way</target>
<source>Mirror</source>
-<target>Mirror </target>
+<target>Mirror</target>
<source>Update</source>
-<target>Update </target>
+<target>Update</target>
<source>Custom</source>
<target>Custom</target>
@@ -468,11 +507,8 @@ The command is triggered if:
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>The following items have unresolved conflicts and will not be synchronised:</target>
-<source>Significant difference detected:</source>
-<target>Significant difference detected:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>More than 50% of the total number of files will be copied or deleted.</target>
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
+<target>The following folders are significantly different. Make sure you are matching the correct folders for synchronisation.</target>
<source>Not enough free disk space available in:</source>
<target>Not enough free disk space available in:</target>
@@ -520,7 +556,10 @@ The command is triggered if:
<target>Press "Switch" to resolve issues in FreeFileSync main dialogue.</target>
<source>Switching to FreeFileSync main dialog</source>
-<target>Switching to FreeFileSync main dialog</target>
+<target>Switching to FreeFileSync main dialogue</target>
+
+<source>Retrying operation after error:</source>
+<target>Retrying operation after error:</target>
<source>A new version of FreeFileSync is available:</source>
<target>A new version of FreeFileSync is available:</target>
@@ -717,6 +756,9 @@ The command is triggered if:
<source>Synchronizing...</source>
<target>Synchronising...</target>
+<source>Minimize to notification area</source>
+<target>Minimize to notification area</target>
+
<source>On completion</source>
<target>On completion</target>
@@ -726,6 +768,15 @@ The command is triggered if:
<source>&Pause</source>
<target>&Pause</target>
+<source>Variant</source>
+<target>Variant</target>
+
+<source>Statistics</source>
+<target>Statistics</target>
+
+<source>Don't show this dialog again</source>
+<target>Don't show this dialogue again</target>
+
<source>Select a variant</source>
<target>Select a variant</target>
@@ -933,15 +984,6 @@ Note: File names must be relative to base directories.
<source>&Default</source>
<target>&Default</target>
-<source>Variant</source>
-<target>Variant</target>
-
-<source>Statistics</source>
-<target>Statistics</target>
-
-<source>Don't show this dialog again</source>
-<target>Don't show this dialogue again</target>
-
<source>Find what:</source>
<target>Find what:</target>
@@ -951,15 +993,15 @@ Note: File names must be relative to base directories.
<source>&Find next</source>
<target>&Find next</target>
+<source>Start synchronization</source>
+<target>Start synchronisation</target>
+
<source>Delete</source>
<target>Delete</target>
<source>Configure filter</source>
<target>Configure filter</target>
-<source>Start synchronization</source>
-<target>Start synchronisation</target>
-
<source>Find</source>
<target>Find</target>
@@ -994,6 +1036,24 @@ Note: File names must be relative to base directories.
<target>Compare both sides</target>
<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</target>
+
+<source>Confirm</source>
+<target>Confirm</target>
+
+<source>&Execute</source>
+<target>&Execute</target>
+
+<source>&Don't show this dialog again</source>
+<target>&Don't show this dialogue again</target>
+
+<source>
<pluralform>1 directory</pluralform>
<pluralform>%x directories</pluralform>
</source>
@@ -1134,8 +1194,8 @@ Note: File names must be relative to base directories.
<source>All folders are in sync</source>
<target>All folders are in sync</target>
-<source>Comma separated list</source>
-<target>Comma separated list</target>
+<source>Comma-separated values</source>
+<target>Comma-separated values</target>
<source>File list exported</source>
<target>File list exported</target>
@@ -1188,14 +1248,11 @@ Note: File names must be relative to base directories.
<source>Completed</source>
<target>Completed</target>
-<source>Continue</source>
-<target>Continue</target>
+<source>&Continue</source>
+<target>&Continue</target>
-<source>Pause</source>
-<target>Pause</target>
-
-<source>Logging</source>
-<target>Logging</target>
+<source>Log</source>
+<target>Log</target>
<source>Cannot find %x</source>
<target>Cannot find %x</target>
@@ -1290,9 +1347,6 @@ Note: File names must be relative to base directories.
<source>Append a timestamp to each file name</source>
<target>Append a timestamp to each file name</target>
-<source>Folder</source>
-<target>Folder</target>
-
<source>File</source>
<target>File</target>
diff --git a/BUILD/Languages/german.lng b/BUILD/Languages/german.lng
index 94c955a4..35c30a34 100644
--- a/BUILD/Languages/german.lng
+++ b/BUILD/Languages/german.lng
@@ -7,9 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>Retrying operation after error:</source>
-<target>Wiederhole Operation nach Fehler:</target>
-
<source>Both sides have changed since last synchronization.</source>
<target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target>
@@ -52,8 +49,14 @@
<source>An exception occurred</source>
<target>Eine Ausnahme ist aufgetreten</target>
-<source>Cannot find file %x.</source>
-<target>Die Datei %x wurde nicht gefunden.</target>
+<source>A directory path is expected after %x.</source>
+<target>Ein Verzeichnispfad wird nach %x erwartet.</target>
+
+<source>Syntax error</source>
+<target>Syntaxfehler</target>
+
+<source>Cannot open file %x.</source>
+<target>Die Datei %x kann nicht geöffnet werden.</target>
<source>Error</source>
<target>Fehler</target>
@@ -61,6 +64,36 @@
<source>File %x does not contain a valid configuration.</source>
<target>Die Datei %x enthält keine gültige Konfiguration.</target>
+<source>Unequal number of left and right directories specified.</source>
+<target>Ungleiche Anzahl linker und rechter Verzeichnisse angegeben.</target>
+
+<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
+<target>Die Konfigurationsdatei darf keine Einstellungen auf Verzeichnispaarebene enthalten, wenn Verzeichnisse über die Befehlszeile gesetzt werden.</target>
+
+<source>Warning</source>
+<target>Warnung</target>
+
+<source>Directories cannot be set for more than one configuration file.</source>
+<target>Verzeichnisse können nicht für mehr als eine Konfigurationsdatei gesetzt werden.</target>
+
+<source>Syntax:</source>
+<target>Syntax:</target>
+
+<source>config files</source>
+<target>Konfigurationsdateien</target>
+
+<source>directory</source>
+<target>Verzeichnis</target>
+
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
+<target>Beliebige Anzahl von FreeFileSync .ffs_gui und/oder .ffs_batch Konfigurationsdateien.</target>
+
+<source>Any number of alternative directories for at most one config file.</source>
+<target>Beliebige Anzahl von alternativen Verzeichnissen für höchstens eine Konfigurationsdatei.</target>
+
+<source>Command line</source>
+<target>Befehlszeile</target>
+
<source>A folder input field is empty.</source>
<target>Ein Ordnereingabefeld ist leer.</target>
@@ -71,7 +104,7 @@
<target>Die folgenden Ordner wurden nicht gefunden:</target>
<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source>
-<target>Dieser Fehler kann ignoriert werden, um die Ordner als leer anzusehen. Die Ordner werden dann beim Synchronisieren automatisch erstellt.</target>
+<target>Dieser Fehler kann ignoriert werden, um die Ordner als leer anzusehen und beim Synchronisieren automatisch zu erstellen.</target>
<source>The following folders have dependent paths. Be careful when setting up synchronization rules:</source>
<target>Die folgenden Ordner haben abhängige Pfade. Achtung beim Festlegen der Synchronisationsregeln:</target>
@@ -218,8 +251,8 @@
<target>Gesamtzeit:</target>
<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
</source>
<target>
<pluralform>1 Byte</pluralform>
@@ -245,12 +278,12 @@
<target>Suche Dateien:</target>
<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
</source>
<target>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
+<pluralform>1 Thread</pluralform>
+<pluralform>%x Threads</pluralform>
</target>
<source>Encoding extended time information: %x</source>
@@ -259,6 +292,9 @@
<source>/sec</source>
<target>/s</target>
+<source>%x items</source>
+<target>%x Elemente</target>
+
<source>Configuration file %x loaded partially only.</source>
<target>Die Konfigurationsdatei %x wurde nur teilweise geladen.</target>
@@ -283,8 +319,8 @@
<source>Cannot determine volume name for %x.</source>
<target>Der Laufwerksname für %x konnte nicht bestimmt werden.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>Laufwerksname %x ist kein Teil des Dateinamens %y.</target>
+<source>Volume name %x is not part of file path %y.</source>
+<target>Laufwerksname %x ist kein Teil des Dateipfades %y.</target>
<source>Abort requested: Waiting for current operation to finish...</source>
<target>Abbruch initiiert: Warte bis die aktuelle Operation beendet ist...</target>
@@ -352,16 +388,13 @@
<source>Idle time between last detected change and execution of command</source>
<target>Ruhezeit zwischen der letzten erkannten Änderung und dem Aufruf der Befehlszeile</target>
-<source>Command line</source>
-<target>Befehlszeile</target>
-
<source>
The command is triggered if:
- files or subfolders change
- new folders arrive (e.g. USB stick insert)
</source>
<target>
-Die Befehlszeile wird ausgelöst wenn:
+Die Befehlszeile wird ausgelöst, wenn:
- Dateien oder Unterordner sich ändern
- neue Ordner erscheinen (z.B. Anschluss eines USB Sticks)
</target>
@@ -378,9 +411,6 @@ Die Befehlszeile wird ausgelöst wenn:
<source>RealtimeSync - Automated Synchronization</source>
<target>RealtimeSync - Automatisierte Synchronisation</target>
-<source>Warning</source>
-<target>Warnung</target>
-
<source>Build: %x</source>
<target>Build: %x</target>
@@ -390,15 +420,21 @@ Die Befehlszeile wird ausgelöst wenn:
<source>All files</source>
<target>Alle Dateien</target>
+<source>Directory monitoring active</source>
+<target>Verzeichnisüberwachung ist aktiv</target>
+
+<source>Waiting until all directories are available...</source>
+<target>Warte bis alle Verzeichnisse verfügbar sind...</target>
+
<source>&Restore</source>
<target>&Wiederherstellen</target>
+<source>&Show error</source>
+<target>&Zeige Fehler</target>
+
<source>&Exit</source>
<target>&Beenden</target>
-<source>Waiting for missing directories...</source>
-<target>Warte auf fehlende Verzeichnisse...</target>
-
<source>Invalid command line:</source>
<target>Ungültige Befehlszeile:</target>
@@ -408,14 +444,14 @@ Die Befehlszeile wird ausgelöst wenn:
<source>File time and size</source>
<target>Datum und Größe</target>
-<source> Two way </source>
-<target> Zwei Wege </target>
+<source>Two way</source>
+<target>Zwei Wege</target>
<source>Mirror</source>
-<target>Spiegeln </target>
+<target>Spiegeln</target>
<source>Update</source>
-<target>Aktualisieren </target>
+<target>Aktualisieren</target>
<source>Custom</source>
<target>Eigene</target>
@@ -471,11 +507,8 @@ Die Befehlszeile wird ausgelöst wenn:
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Die folgenden Elemente haben ungelöste Konflikte und werden nicht synchronisiert werden:</target>
-<source>Significant difference detected:</source>
-<target>Ein erheblicher Unterschied wurde festgestellt:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>Mehr als 50% aller Dateien werden kopiert oder gelöscht.</target>
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
+<target>Die folgenden Ordner unterscheiden sich erheblich. Stellen Sie sicher, dass die richtigen Ordner für die Synchronisation ausgewählt sind.</target>
<source>Not enough free disk space available in:</source>
<target>Nicht genügend freier Speicher verfügbar unter:</target>
@@ -525,6 +558,9 @@ Die Befehlszeile wird ausgelöst wenn:
<source>Switching to FreeFileSync main dialog</source>
<target>Wechsle in FreeFileSyncs Hauptdialog</target>
+<source>Retrying operation after error:</source>
+<target>Wiederhole Operation nach Fehler:</target>
+
<source>A new version of FreeFileSync is available:</source>
<target>Eine neue Version von FreeFileSync ist verfügbar:</target>
@@ -720,6 +756,9 @@ Die Befehlszeile wird ausgelöst wenn:
<source>Synchronizing...</source>
<target>Synchronisiere...</target>
+<source>Minimize to notification area</source>
+<target>In das Benachrichtigungsfeld minimieren</target>
+
<source>On completion</source>
<target>Nach Abschluss</target>
@@ -729,6 +768,15 @@ Die Befehlszeile wird ausgelöst wenn:
<source>&Pause</source>
<target>&Pause</target>
+<source>Variant</source>
+<target>Variante</target>
+
+<source>Statistics</source>
+<target>Statistiken</target>
+
+<source>Don't show this dialog again</source>
+<target>Diesen Dialog nicht mehr anzeigen</target>
+
<source>Select a variant</source>
<target>Variante auswählen</target>
@@ -936,15 +984,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein.
<source>&Default</source>
<target>&Standard</target>
-<source>Variant</source>
-<target>Variante</target>
-
-<source>Statistics</source>
-<target>Statistiken</target>
-
-<source>Don't show this dialog again</source>
-<target>Diesen Dialog nicht mehr anzeigen</target>
-
<source>Find what:</source>
<target>Suchen nach:</target>
@@ -954,15 +993,15 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein.
<source>&Find next</source>
<target>&Weitersuchen</target>
+<source>Start synchronization</source>
+<target>Synchronisation starten</target>
+
<source>Delete</source>
<target>Löschen</target>
<source>Configure filter</source>
<target>Konfiguriere Filter</target>
-<source>Start synchronization</source>
-<target>Synchronisation starten</target>
-
<source>Find</source>
<target>Suchen</target>
@@ -997,6 +1036,24 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein.
<target>Beide Seiten vergleichen</target>
<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target>
+<pluralform>Soll der Befehl %y wirklich für 1 Element ausgeführt werden?</pluralform>
+<pluralform>Soll der Befehl %y wirklich für %x Elemente ausgeführt werden?</pluralform>
+</target>
+
+<source>Confirm</source>
+<target>Bestätigen</target>
+
+<source>&Execute</source>
+<target>&Ausführen</target>
+
+<source>&Don't show this dialog again</source>
+<target>&Diesen Dialog nicht mehr anzeigen</target>
+
+<source>
<pluralform>1 directory</pluralform>
<pluralform>%x directories</pluralform>
</source>
@@ -1137,8 +1194,8 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein.
<source>All folders are in sync</source>
<target>Alle Ordner sind synchron</target>
-<source>Comma separated list</source>
-<target>Kommagetrennte Liste</target>
+<source>Comma-separated values</source>
+<target>Kommagetrennte Werte</target>
<source>File list exported</source>
<target>Dateiliste exportiert</target>
@@ -1191,13 +1248,10 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein.
<source>Completed</source>
<target>Fertig</target>
-<source>Continue</source>
-<target>Fortfahren</target>
+<source>&Continue</source>
+<target>&Fortfahren</target>
-<source>Pause</source>
-<target>Pause</target>
-
-<source>Logging</source>
+<source>Log</source>
<target>Protokoll</target>
<source>Cannot find %x</source>
@@ -1293,9 +1347,6 @@ Achtung: Dateinamen müssen relativ zu den Basisverzeichnissen sein.
<source>Append a timestamp to each file name</source>
<target>Einen Zeitstempel an jeden Dateinamen anhängen</target>
-<source>Folder</source>
-<target>Ordner</target>
-
<source>File</source>
<target>Datei</target>
diff --git a/BUILD/Languages/italian.lng b/BUILD/Languages/italian.lng
index e1bfa558..3dcac298 100644
--- a/BUILD/Languages/italian.lng
+++ b/BUILD/Languages/italian.lng
@@ -1,73 +1,82 @@
<header>
<language>Italiano</language>
- <translator>Emmo</translator>
+ <translator>Luciano Paravella</translator>
<locale>it_IT</locale>
<flag_image>flag_italy.png</flag_image>
<plural_form_count>2</plural_form_count>
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>Cannot determine final path for %x.</source>
+<source>Log</source>
<target></target>
-<source>Failed to register to receive system messages.</source>
+<source>Comma-separated values</source>
<target></target>
-<source>Cannot create symbolic link %x.</source>
+<source>Minimize to notification area</source>
<target></target>
-<source>Items</source>
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
<target></target>
-<source>
-<pluralform>%y of 1 row in view</pluralform>
-<pluralform>%y of %x rows in view</pluralform>
-</source>
+<source>&Show error</source>
<target></target>
-<source>Select a variant</source>
+<source>Waiting until all directories are available...</source>
<target></target>
-<source>Check for new &version</source>
+<source>Directory monitoring active</source>
<target></target>
-<source>&Tools</source>
+<source>Volume name %x is not part of file path %y.</source>
<target></target>
-<source>Cannot find current FreeFileSync version number online. Do you want to check manually?</source>
+<source>%x items</source>
<target></target>
-<source>&Download</source>
+<source>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
+</source>
<target></target>
-<source>New version found</source>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
<target></target>
-<source>Switching to FreeFileSync main dialog</source>
+<source>Any number of alternative directories for at most one config file.</source>
<target></target>
-<source>Data verification error: %x and %y have different content.</source>
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
<target></target>
-<source>Cannot determine volume name for %x.</source>
+<source>directory</source>
<target></target>
-<source>Starting comparison</source>
+<source>config files</source>
<target></target>
-<source>Resolving symbolic link %x</source>
+<source>Syntax:</source>
<target></target>
-<source>The following folders have dependent paths. Be careful when setting up synchronization rules:</source>
+<source>Directories cannot be set for more than one configuration file.</source>
<target></target>
-<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source>
+<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
<target></target>
-<source>The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:</source>
+<source>Unequal number of left and right directories specified.</source>
<target></target>
-<source>The database entry is not in sync considering current settings.</source>
+<source>Cannot open file %x.</source>
+<target></target>
+
+<source>Syntax error</source>
+<target></target>
+
+<source>A directory path is expected after %x.</source>
<target></target>
<source>Both sides have changed since last synchronization.</source>
@@ -79,8 +88,11 @@
<source>No change since last synchronization.</source>
<target>Nessun cambiamento dall'ultima sincronizzazione.</target>
+<source>The database entry is not in sync considering current settings.</source>
+<target>La voce di database non è in sincronia con le impostazioni correnti.</target>
+
<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source>
-<target>Imposta direzioni di sincronizzazione dpredefinite: i vecchi file saranno sovrascritti dai nuovi.</target>
+<target>Imposta le direzioni di sincronizzazione predefinite: i vecchi file saranno sovrascritti dai nuovi.</target>
<source>Checking recycle bin availability for folder %x...</source>
<target>Controllo di disponibilità Cestino per la cartella %x...</target>
@@ -103,20 +115,26 @@
<source>Deleting symbolic link %x</source>
<target>Eliminazione collegamento %x</target>
+<source>The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:</source>
+<target>Il Cestino non è disponibile per le seguenti cartelle. I file verranno quindi eliminati in modo permanente:</target>
+
<source>An exception occurred</source>
<target>Si è verificata una eccezione</target>
-<source>Cannot find file %x.</source>
-<target>Impossibile trovare il file %x.</target>
-
<source>Error</source>
<target>Errore</target>
<source>File %x does not contain a valid configuration.</source>
<target>Il file %x non contiene una configurazione valida.</target>
+<source>Warning</source>
+<target>Attenzione</target>
+
+<source>Command line</source>
+<target>Linea di comando</target>
+
<source>A folder input field is empty.</source>
-<target>Un campo cartella input è vuoto.</target>
+<target>Un campo di input cartella è vuoto.</target>
<source>The corresponding folder will be considered as empty.</source>
<target>La cartella corrispondente sarà considerata come vuota.</target>
@@ -124,6 +142,12 @@
<source>Cannot find the following folders:</source>
<target>Impossibile trovare le seguenti cartelle:</target>
+<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source>
+<target>È possibile ignorare questo errore che considera ogni cartella come vuota. Le cartelle poi verranno create automaticamente durante la sincronizzazione.</target>
+
+<source>The following folders have dependent paths. Be careful when setting up synchronization rules:</source>
+<target>Le seguenti cartelle hanno percorsi dipendenti. Fare attenzione quando si imposta le regole di sincronizzazione:</target>
+
<source>File %x has an invalid date.</source>
<target>Il file %x ha una data non valida.</target>
@@ -139,12 +163,18 @@
<source>Items differ in attributes only</source>
<target>Gli oggetti differiscono solo negli attributi</target>
+<source>Resolving symbolic link %x</source>
+<target>Risoluzione collegamento %x</target>
+
<source>Comparing content of files %x</source>
-<target>Comparazione contenuto del file %x</target>
+<target>Confronto contenuto del file %x</target>
<source>Generating file list...</source>
<target>Generazione elenco file...</target>
+<source>Starting comparison</source>
+<target>Inizio confronto</target>
+
<source>Calculating sync directions...</source>
<target>Calcolo della direzione di sincronizzazione...</target>
@@ -206,16 +236,16 @@
<target>Aggiorna attributi a destra</target>
<source>Database file %x is incompatible.</source>
-<target>Il file database %x non è compatibile.</target>
+<target>Il file del database %x non è compatibile.</target>
<source>Initial synchronization:</source>
<target>Prima sincronizzazione:</target>
<source>Database file %x does not yet exist.</source>
-<target>Il file database %x non è ancora stato creato.</target>
+<target>Il file del database %x non è ancora stato creato.</target>
<source>Database file is corrupt:</source>
-<target>Il file database è corrotto:</target>
+<target>Il file del database è corrotto:</target>
<source>Cannot write file %x.</source>
<target>Impossibile scrivere il file %x.</target>
@@ -224,7 +254,7 @@
<target>Impossibile leggere il file %x.</target>
<source>Database files do not share a common session.</source>
-<target>I file database non condividono una sessione comune.</target>
+<target>I file del database non condividono una sessione comune.</target>
<source>Searching for folder %x...</source>
<target>Ricerca della cartella %x...</target>
@@ -259,15 +289,6 @@
<source>Total time:</source>
<target>Tempo totale:</target>
-<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
-</source>
-<target>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Byte</pluralform>
-</target>
-
<source>%x MB</source>
<target>%x MB</target>
@@ -284,16 +305,7 @@
<target>Impossibile impostare blocco cartella per %x.</target>
<source>Scanning:</source>
-<target>Analisi di:</target>
-
-<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
-</source>
-<target>
-<pluralform>[1 thread]</pluralform>
-<pluralform>[%x threads]</pluralform>
-</target>
+<target>Scansione di:</target>
<source>Encoding extended time information: %x</source>
<target>Codifica estesa informazioni orario: %x</target>
@@ -322,11 +334,11 @@
<source>Cannot load file %x.</source>
<target>Impossibile caricare il file %x.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>Il nome volume %x non è parte del nome file %y.</target>
+<source>Cannot determine volume name for %x.</source>
+<target>Impossibile determinare nome del volume per %x.</target>
<source>Abort requested: Waiting for current operation to finish...</source>
-<target>Selezionata interruazione: conclusione dell'operazione...</target>
+<target>Richiesta interruazione: attendere conclusione dell'operazione...</target>
<source>Failure to create timestamp for versioning:</source>
<target>Errore nella creazione di data e ora per il controllo delle versioni:</target>
@@ -353,7 +365,7 @@
<target>&Informazioni</target>
<source>&Help</source>
-<target>&?</target>
+<target>&Aiuto</target>
<source>Usage:</source>
<target>Uso:</target>
@@ -365,7 +377,7 @@
<target>2. Inserisci linea di comando.</target>
<source>3. Press 'Start'.</source>
-<target>3. Premi 'Start'.</target>
+<target>3. Premi 'Avvio'.</target>
<source>To get started just import a .ffs_batch file.</source>
<target>Per iniziare è sufficiente importare un file con estensione .ffs_batch</target>
@@ -389,10 +401,7 @@
<target>Tempo di attesa [secondi]</target>
<source>Idle time between last detected change and execution of command</source>
-<target>Tempo di attesa tra ultimo cambiamento rilevato ed esecuzione dei comandi</target>
-
-<source>Command line</source>
-<target>Linea di comando</target>
+<target>Tempo di attesa tra ultimo cambiamento rilevato ed l'esecuzione del comando</target>
<source>
The command is triggered if:
@@ -415,10 +424,7 @@ Il comando è attivato se:
<target>Annulla</target>
<source>RealtimeSync - Automated Synchronization</source>
-<target>RealtimeSync - Sincronizzazione automatizzata</target>
-
-<source>Warning</source>
-<target>Attenzione</target>
+<target>SyncTempoReale - Sincronizzazione automatizzata</target>
<source>Build: %x</source>
<target>Versione: %x</target>
@@ -435,9 +441,6 @@ Il comando è attivato se:
<source>&Exit</source>
<target>&Esci</target>
-<source>Waiting for missing directories...</source>
-<target>In attesa delle cartelle mancanti...</target>
-
<source>Invalid command line:</source>
<target>Linea di comando non valida:</target>
@@ -447,14 +450,14 @@ Il comando è attivato se:
<source>File time and size</source>
<target>Ora e dimensione file</target>
-<source> Two way </source>
-<target> Due vie </target>
+<source>Two way</source>
+<target>Due vie</target>
<source>Mirror</source>
-<target>Mirror </target>
+<target>Specchio</target>
<source>Update</source>
-<target>Aggiorna </target>
+<target>Aggiorna</target>
<source>Custom</source>
<target>Personalizza</target>
@@ -510,12 +513,6 @@ Il comando è attivato se:
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>I seguenti oggetti hanno conflitti irrisolti e non saranno sincronizzati:</target>
-<source>Significant difference detected:</source>
-<target>Riscontrate differenze significative:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>Piu' del 50% del totale dei file saranno copiati o cancellati.</target>
-
<source>Not enough free disk space available in:</source>
<target>Spazio libero su disco insufficiente in:</target>
@@ -523,10 +520,10 @@ Il comando è attivato se:
<target>Richiesto:</target>
<source>Available:</source>
-<target>Dispobilile:</target>
+<target>Disponibile:</target>
<source>A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.</source>
-<target>Verrà modificata una cartella che è parte di molteplici coppie di cartelle. Controlla le impostazioni di sincronizzazione.</target>
+<target>Verrà modificata una cartella che è parte di molte coppie di cartelle. Controlla le impostazioni di sincronizzazione.</target>
<source>Synchronizing folder pair:</source>
<target>Sincronizzazione della coppia di cartelle:</target>
@@ -537,6 +534,9 @@ Il comando è attivato se:
<source>Creating Volume Shadow Copy for %x...</source>
<target>Creazione di Volume Shadow Copy per %x...</target>
+<source>Data verification error: %x and %y have different content.</source>
+<target>Dati di verifica errore: %x e %y hanno un contenuto diverso.</target>
+
<source>Synchronization aborted</source>
<target>Sincronizzazione abortita</target>
@@ -558,12 +558,24 @@ Il comando è attivato se:
<source>Press "Switch" to resolve issues in FreeFileSync main dialog.</source>
<target>Premere "Passa" per risolvere i problemi nella finestra principale di FreeFileSync.</target>
+<source>Switching to FreeFileSync main dialog</source>
+<target>Passaggio alla finestra di dialogo principale FreeFileSync</target>
+
+<source>Retrying operation after error:</source>
+<target>Ripetere l'operazione dopo l'errore:</target>
+
<source>A new version of FreeFileSync is available:</source>
<target>E' disponibile una nuova versione di FreeFileSync:</target>
<source>Download now?</source>
<target>Scaricarla ora?</target>
+<source>New version found</source>
+<target>Nuova versione trovata</target>
+
+<source>&Download</source>
+<target>&Scaricare</target>
+
<source>FreeFileSync is up to date.</source>
<target>FreeFileSync è aggiornato.</target>
@@ -573,6 +585,9 @@ Il comando è attivato se:
<source>Unable to connect to sourceforge.net.</source>
<target>Impossibile collegarsi a sourceforge.net.</target>
+<source>Cannot find current FreeFileSync version number online. Do you want to check manually?</source>
+<target>Non riesci a trovare l'attuale numero di versione di FreeFileSync on-line . Vuoi controllare manualmente?</target>
+
<source>Symlink</source>
<target>Symlink</target>
@@ -675,12 +690,18 @@ Il comando è attivato se:
<source>&Global settings...</source>
<target>&Preferenze...</target>
+<source>&Tools</source>
+<target>&Strumenti</target>
+
<source>&Check now</source>
<target>&Controlla ora</target>
<source>Check &automatically once a week</source>
<target>Controlla &automaticamente una volta a settimana</target>
+<source>Check for new &version</source>
+<target>Verificare presenza nuove &versione</target>
+
<source>Compare</source>
<target>Compara</target>
@@ -747,6 +768,18 @@ Il comando è attivato se:
<source>&Pause</source>
<target>&Pausa</target>
+<source>Variant</source>
+<target>Variante</target>
+
+<source>Statistics</source>
+<target>Statistiche</target>
+
+<source>Don't show this dialog again</source>
+<target>Non mostrare più questo messaggio</target>
+
+<source>Select a variant</source>
+<target>Seleziona una variante</target>
+
<source>
Files are found equal if
- last write time and date
@@ -775,7 +808,7 @@ I file sono considerati identici se
<target>Gestione Collegamenti</target>
<source>Help</source>
-<target>?</target>
+<target>Aiuto</target>
<source>OK</source>
<target>OK</target>
@@ -784,7 +817,7 @@ I file sono considerati identici se
<target>Identifica e propaga cambiamenti su entrambi i lati. Cancellazioni, spostamenti e conflitti sono identificati automaticamente usando un database.</target>
<source>Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization.</source>
-<target>Backup mirror della cartella di sinistra. La cartella destra verrà modificata per corrispondere esattamente alla cartella di sinistra dopo la sincronizzazione.</target>
+<target>Backup a Specchio della cartella di sinistra. La cartella di destra verrà modificata per corrispondere esattamente alla cartella di sinistra dopo la sincronizzazione.</target>
<source>Copy new or updated files to right folder.</source>
<target>Copia file nuovi o aggiornati nella cartella di destra.</target>
@@ -814,7 +847,7 @@ I file sono considerati identici se
<target>Permanente</target>
<source>Delete or overwrite files permanently</source>
-<target>Elimina o sovrascrivi file definitivamente</target>
+<target>Elimina o sovrascrivi i file definitivamente</target>
<source>Recycle Bin</source>
<target>Cestino</target>
@@ -893,7 +926,7 @@ Only files that match all filter settings will be synchronized.
Note: File names must be relative to base directories.
</source>
<target>
-Solo i file che corrispondono esattamente a tutti i filtri impostati verrranno sincronizzati.
+Solo i file che corrispondono esattamente a tutti i parametri dei filtri verrranno sincronizzati.
Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
</target>
@@ -922,7 +955,7 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<target>Preferenze</target>
<source>Fail-safe file copy</source>
-<target>Copia file sicura</target>
+<target>Copia file di fail-safe</target>
<source>Write to a temporary file (*.ffs_tmp) first then rename it. This guarantees a consistent state even in case of fatal error.</source>
<target>Scrivi in un file temporaneo (*.ffs_tmp) prima di rinominarlo. Ciò garantisce uno stato consistente anche in caso di errore fatale.</target>
@@ -931,7 +964,7 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<target>Copia file bloccati</target>
<source>Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights)</source>
-<target>Copia file condivisi o bloccati usando il Servizio Volume Shadow Copy (richiede diritti di amministarzione)</target>
+<target>Copia file condivisi o bloccati usando il Servizio Volume Shadow Copy (richiede diritti di amministratore)</target>
<source>Copy file access permissions</source>
<target>Copia permessi di accesso file</target>
@@ -951,15 +984,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>&Default</source>
<target>&Predefinito</target>
-<source>Variant</source>
-<target>Variante</target>
-
-<source>Statistics</source>
-<target>Statistiche</target>
-
-<source>Don't show this dialog again</source>
-<target>Non mostrare più questo messaggio</target>
-
<source>Find what:</source>
<target>Trova questo:</target>
@@ -969,15 +993,15 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>&Find next</source>
<target>&Trova successivo</target>
+<source>Start synchronization</source>
+<target>Avvia sincronizzazione</target>
+
<source>Delete</source>
<target>Elimina</target>
<source>Configure filter</source>
<target>Configurazione dei filtri</target>
-<source>Start synchronization</source>
-<target>Avvia sincronizzazione</target>
-
<source>Find</source>
<target>Trova</target>
@@ -1009,7 +1033,25 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<target>Salva</target>
<source>Compare both sides</source>
-<target>Compara le due liste</target>
+<target>Confronta entrambi i lati</target>
+
+<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target>
+<pluralform>Vuoi veramente eseguire il comando %y per 1 articolo?</pluralform>
+<pluralform>Vuoi veramente eseguire %y comando per %x articoli?</pluralform>
+</target>
+
+<source>Confirm</source>
+<target>Confermare</target>
+
+<source>&Execute</source>
+<target>&Esecuzione</target>
+
+<source>&Don't show this dialog again</source>
+<target>&Non mostrare più questo avviso</target>
<source>
<pluralform>1 directory</pluralform>
@@ -1029,6 +1071,15 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<pluralform>%x file</pluralform>
</target>
+<source>
+<pluralform>%y of 1 row in view</pluralform>
+<pluralform>%y of %x rows in view</pluralform>
+</source>
+<target>
+<pluralform>%y di 1 riga in vista</pluralform>
+<pluralform>%y di %x righe in vista</pluralform>
+</target>
+
<source>Set direction:</source>
<target>Imposta direzione:</target>
@@ -1099,10 +1150,10 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<target>Mostra file esistenti solo a destra</target>
<source>Show files that are newer on left</source>
-<target>Mostra file di sinistra più recenti che a destra</target>
+<target>Mostra i file che sono più recenti sulla sinistra</target>
<source>Show files that are newer on right</source>
-<target>Mostra file di destra più recenti che a sinistra</target>
+<target>Mostra i file che sono più recenti sulla destra</target>
<source>Show files that are equal</source>
<target>Mostra file identici</target>
@@ -1143,9 +1194,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>All folders are in sync</source>
<target>Tutte le cartelle sono sincronizzate</target>
-<source>Comma separated list</source>
-<target>Elenco elementi separati da virgola</target>
-
<source>File list exported</source>
<target>Elenco file esportato</target>
@@ -1197,14 +1245,8 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>Completed</source>
<target>Completato</target>
-<source>Continue</source>
-<target>Continua</target>
-
-<source>Pause</source>
-<target>Pausa</target>
-
-<source>Logging</source>
-<target>Registo attività</target>
+<source>&Continue</source>
+<target>&Continuare</target>
<source>Cannot find %x</source>
<target>Impossibile trovare %x</target>
@@ -1276,13 +1318,13 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<target>- solo porzione cartella</target>
<source>- Other side's counterpart to %item_path%</source>
-<target>- Controparte sull'altro lato a %item_path%</target>
+<target>- Omologo dell'altro lato a %item_path%</target>
<source>- Other side's counterpart to %item_folder%</source>
-<target>- Controparte sull'altro lato a %item_folder%</target>
+<target>- Omologo dell'altro lato a %item_folder%</target>
<source>Make hidden warnings and dialogs visible again?</source>
-<target>Vuoi visualizzare nuovamente i dialoghi e avvisi nascosti?</target>
+<target>Vuoi visualizzare nuovamente i dialoghi e gli avvisi nascosti?</target>
<source>Leave as unresolved conflict</source>
<target>Lascia come conflitti irrisolti</target>
@@ -1299,9 +1341,6 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>Append a timestamp to each file name</source>
<target>Accoda data e ora ad ogni nome di file</target>
-<source>Folder</source>
-<target>Cartella</target>
-
<source>File</source>
<target>File</target>
@@ -1311,6 +1350,9 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>Files</source>
<target>File</target>
+<source>Items</source>
+<target>Articoli</target>
+
<source>Percentage</source>
<target>Percentuale</target>
@@ -1353,8 +1395,11 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>Cannot create directory %x.</source>
<target>Impossibile creare cartella %x.</target>
+<source>Cannot create symbolic link %x.</source>
+<target>Impossibile creare il collegamento %x.</target>
+
<source>Cannot find system function %x.</source>
-<target>Impossibile trovare funzione di sistema %x.</target>
+<target>Impossibile trovare la funzione di sistema %x.</target>
<source>Cannot copy file %x to %y.</source>
<target>Impossibile copiare file %x in %y.</target>
@@ -1404,6 +1449,9 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<pluralform>%x giorni</pluralform>
</target>
+<source>Failed to register to receive system messages.</source>
+<target>Impossibile registrarsi per ricevere i messaggi di sistema.</target>
+
<source>Cannot set privilege %x.</source>
<target>Impossibile impostare privilegi %x.</target>
@@ -1416,6 +1464,9 @@ Nota: I nomi dei file devono essere relativi alle cartelle di appartenenza.
<source>Unable to move %x to the Recycle Bin.</source>
<target>Impossibile spostare %x nel Cestino.</target>
+<source>Cannot determine final path for %x.</source>
+<target>Impossibile determinare il percorso finale per %x.</target>
+
<source>Error Code %x:</source>
<target>Errore Codice %x:</target>
diff --git a/BUILD/Languages/lithuanian.lng b/BUILD/Languages/lithuanian.lng
index 0c4cc9b9..e3bfed10 100644
--- a/BUILD/Languages/lithuanian.lng
+++ b/BUILD/Languages/lithuanian.lng
@@ -22,15 +22,42 @@
<source>Items</source>
<target></target>
+<source>Log</source>
+<target></target>
+
+<source>&Continue</source>
+<target></target>
+
+<source>Comma-separated values</source>
+<target></target>
+
<source>
<pluralform>%y of 1 row in view</pluralform>
<pluralform>%y of %x rows in view</pluralform>
</source>
<target></target>
+<source>&Don't show this dialog again</source>
+<target></target>
+
+<source>&Execute</source>
+<target></target>
+
+<source>Confirm</source>
+<target></target>
+
+<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target></target>
+
<source>Select a variant</source>
<target></target>
+<source>Minimize to notification area</source>
+<target></target>
+
<source>Check for new &version</source>
<target></target>
@@ -46,15 +73,48 @@
<source>New version found</source>
<target></target>
+<source>Retrying operation after error:</source>
+<target></target>
+
<source>Switching to FreeFileSync main dialog</source>
<target></target>
<source>Data verification error: %x and %y have different content.</source>
<target></target>
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
+<target></target>
+
+<source>&Show error</source>
+<target></target>
+
+<source>Waiting until all directories are available...</source>
+<target></target>
+
+<source>Directory monitoring active</source>
+<target></target>
+
+<source>Volume name %x is not part of file path %y.</source>
+<target></target>
+
<source>Cannot determine volume name for %x.</source>
<target></target>
+<source>%x items</source>
+<target></target>
+
+<source>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
+</source>
+<target></target>
+
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target></target>
+
<source>Cannot read file attributes of %x.</source>
<target></target>
@@ -70,6 +130,39 @@
<source>You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization.</source>
<target></target>
+<source>Any number of alternative directories for at most one config file.</source>
+<target></target>
+
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
+<target></target>
+
+<source>directory</source>
+<target></target>
+
+<source>config files</source>
+<target></target>
+
+<source>Syntax:</source>
+<target></target>
+
+<source>Directories cannot be set for more than one configuration file.</source>
+<target></target>
+
+<source>The config file must not contain settings at folder pair level when directories are set via command line.</source>
+<target></target>
+
+<source>Unequal number of left and right directories specified.</source>
+<target></target>
+
+<source>Cannot open file %x.</source>
+<target></target>
+
+<source>Syntax error</source>
+<target></target>
+
+<source>A directory path is expected after %x.</source>
+<target></target>
+
<source>The Recycle Bin is not available for the following folders. Files will be deleted permanently instead:</source>
<target></target>
@@ -112,15 +205,18 @@
<source>An exception occurred</source>
<target>Atsirado išimtis</target>
-<source>Cannot find file %x.</source>
-<target>Nepavyksta rasti failo %x.</target>
-
<source>Error</source>
<target>Klaida</target>
<source>File %x does not contain a valid configuration.</source>
<target>Failas %x neturi tinkamų nustatymų.</target>
+<source>Warning</source>
+<target>Perspėjimas</target>
+
+<source>Command line</source>
+<target>Komandinė eilutė</target>
+
<source>A folder input field is empty.</source>
<target>Aplanko įvesties laukas yra tuščias.</target>
@@ -264,17 +360,6 @@
<source>Total time:</source>
<target>Visas laikas:</target>
-<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
-</source>
-<target>
-<pluralform>1 Baitas</pluralform>
-<pluralform>%x Baitai</pluralform>
-<pluralform>%x Baitų</pluralform>
-<pluralform>%x Baitas</pluralform>
-</target>
-
<source>%x MB</source>
<target>%x MB</target>
@@ -293,17 +378,6 @@
<source>Scanning:</source>
<target>Skenuojama:</target>
-<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
-</source>
-<target>
-<pluralform>[1 Grėsmė]</pluralform>
-<pluralform>[%x Grėsmės]</pluralform>
-<pluralform>[%x Grėsmių]</pluralform>
-<pluralform>[%x Grėsmė]</pluralform>
-</target>
-
<source>Encoding extended time information: %x</source>
<target>Koduojama išplėstinė laiko informacija: %x</target>
@@ -331,9 +405,6 @@
<source>Cannot load file %x.</source>
<target>Nepavyksta įkelti failo %x.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>Tomo pavadinimas %x nėra dalis failo pavadinimo %y.</target>
-
<source>Abort requested: Waiting for current operation to finish...</source>
<target>Nutraukti: laukiama kol baigsis esama operacija...</target>
@@ -400,9 +471,6 @@
<source>Idle time between last detected change and execution of command</source>
<target>Neveiklus laikas tarp paskutinio aptikto pokyčio ir komandos įvykdymo</target>
-<source>Command line</source>
-<target>Komandinė eilutė</target>
-
<source>
The command is triggered if:
- files or subfolders change
@@ -426,9 +494,6 @@ Komanda inicijuojama jei:
<source>RealtimeSync - Automated Synchronization</source>
<target>RealtimeSync - Automatizuotas sinchronizavimas</target>
-<source>Warning</source>
-<target>Perspėjimas</target>
-
<source>Build: %x</source>
<target>Versija: %x</target>
@@ -444,9 +509,6 @@ Komanda inicijuojama jei:
<source>&Exit</source>
<target>&Išeiti</target>
-<source>Waiting for missing directories...</source>
-<target>Laikiama trūkstamų katalogų...</target>
-
<source>Invalid command line:</source>
<target>Netinkama komandinė eilutė:</target>
@@ -456,14 +518,14 @@ Komanda inicijuojama jei:
<source>File time and size</source>
<target>Failo laiką ir dydį</target>
-<source> Two way </source>
-<target> Dvipusis </target>
+<source>Two way</source>
+<target>Dvipusis</target>
<source>Mirror</source>
-<target>Veidrodis </target>
+<target>Veidrodis</target>
<source>Update</source>
-<target>Atnaujinti </target>
+<target>Atnaujinti</target>
<source>Custom</source>
<target>Savitas</target>
@@ -519,12 +581,6 @@ Komanda inicijuojama jei:
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Šie elementai turi neišspręstų konfliktų ir nebus sinchronizuoti:</target>
-<source>Significant difference detected:</source>
-<target>Žymus skirtumas nustatytas:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>Daugiau nei 50% failų bus nukopijuota arba ištrinta.</target>
-
<source>Not enough free disk space available in:</source>
<target>Nepakanka laisvos disko vietos:</target>
@@ -756,6 +812,15 @@ Komanda inicijuojama jei:
<source>&Pause</source>
<target>&Pauzė</target>
+<source>Variant</source>
+<target>Variantas</target>
+
+<source>Statistics</source>
+<target>Statistika</target>
+
+<source>Don't show this dialog again</source>
+<target>Daugiau nerodyti šio dialogo</target>
+
<source>
Files are found equal if
- last write time and date
@@ -960,15 +1025,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus.
<source>&Default</source>
<target>&Numatyta</target>
-<source>Variant</source>
-<target>Variantas</target>
-
-<source>Statistics</source>
-<target>Statistika</target>
-
-<source>Don't show this dialog again</source>
-<target>Daugiau nerodyti šio dialogo</target>
-
<source>Find what:</source>
<target>Rasti kas:</target>
@@ -978,15 +1034,15 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus.
<source>&Find next</source>
<target>&Rasti kitą</target>
+<source>Start synchronization</source>
+<target>Pradėti sinchronizavimą</target>
+
<source>Delete</source>
<target>Trinti</target>
<source>Configure filter</source>
<target>Nustatyti filtrą</target>
-<source>Start synchronization</source>
-<target>Pradėti sinchronizavimą</target>
-
<source>Find</source>
<target>Rasti</target>
@@ -1156,9 +1212,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus.
<source>All folders are in sync</source>
<target>Visi aplankai susinchronizuoti</target>
-<source>Comma separated list</source>
-<target>Kableliais atksirtas sąrašas</target>
-
<source>File list exported</source>
<target>Failų sąrašas eksportuotas</target>
@@ -1210,15 +1263,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus.
<source>Completed</source>
<target>Baigta</target>
-<source>Continue</source>
-<target>Tęsti</target>
-
-<source>Pause</source>
-<target>Pristabdyti</target>
-
-<source>Logging</source>
-<target>Užduoties duomenys</target>
-
<source>Cannot find %x</source>
<target>Nepavyksta rasti %x</target>
@@ -1316,9 +1360,6 @@ Pastaba: Failų pavadinimai privalo atitikti bazinius katalogus.
<source>Append a timestamp to each file name</source>
<target>Pridėti laiko žymę kiekvieno failo pavadinime</target>
-<source>Folder</source>
-<target>Aplankas</target>
-
<source>File</source>
<target>Failas</target>
diff --git a/BUILD/Languages/slovenian.lng b/BUILD/Languages/slovenian.lng
index e368737c..a5bed20e 100644
--- a/BUILD/Languages/slovenian.lng
+++ b/BUILD/Languages/slovenian.lng
@@ -7,9 +7,99 @@
<plural_definition>n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3</plural_definition>
</header>
+<source>Log</source>
+<target></target>
+
+<source>&Continue</source>
+<target></target>
+
+<source>Comma-separated values</source>
+<target></target>
+
+<source>&Don't show this dialog again</source>
+<target></target>
+
+<source>&Execute</source>
+<target></target>
+
+<source>Confirm</source>
+<target></target>
+
+<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target></target>
+
+<source>Minimize to notification area</source>
+<target></target>
+
<source>Retrying operation after error:</source>
<target></target>
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
+<target></target>
+
+<source>&Show error</source>
+<target></target>
+
+<source>Waiting until all directories are available...</source>
+<target></target>
+
+<source>Directory monitoring active</source>
+<target></target>
+
+<source>Volume name %x is not part of file path %y.</source>
+<target></target>
+
+<source>%x items</source>
+<target></target>
+
+<source>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
+</source>
+<target></target>
+
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target></target>
+
+<source>Any number of alternative directories for at most one config file.</source>
+<target></target>
+
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
+<target></target>
+
+<source>directory</source>
+<target></target>
+
+<source>config files</source>
+<target></target>
+
+<source>Syntax:</source>
+<target></target>
+
+<source>Directories cannot be set for more than one configuration file.</source>
+<target></target>
+
+<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
+<target></target>
+
+<source>Unequal number of left and right directories specified.</source>
+<target></target>
+
+<source>Cannot open file %x.</source>
+<target></target>
+
+<source>Syntax error</source>
+<target></target>
+
+<source>A directory path is expected after %x.</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Obe strani sta se spremenili od zadnje sinhronizacije.</target>
@@ -52,15 +142,18 @@
<source>An exception occurred</source>
<target>Zgodila se je napaka</target>
-<source>Cannot find file %x.</source>
-<target>Ne morem najti datoteke %x.</target>
-
<source>Error</source>
<target>Napaka</target>
<source>File %x does not contain a valid configuration.</source>
<target>Datoteka %x ne vsebuje veljavnih nastavitev</target>
+<source>Warning</source>
+<target>Pozor</target>
+
+<source>Command line</source>
+<target>Ukazna vrstica</target>
+
<source>A folder input field is empty.</source>
<target>Vnosno polje za mapo je prazno.</target>
@@ -219,17 +312,6 @@
<source>Total time:</source>
<target>Celoten čas:</target>
-<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
-</source>
-<target>
-<pluralform>%x Bajt</pluralform>
-<pluralform>%x Bajta</pluralform>
-<pluralform>%x Bajti</pluralform>
-<pluralform>%x Bajtov</pluralform>
-</target>
-
<source>%x MB</source>
<target>%x MB</target>
@@ -248,17 +330,6 @@
<source>Scanning:</source>
<target>Pregledujem:</target>
-<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
-</source>
-<target>
-<pluralform>[%x nit]</pluralform>
-<pluralform>[%x niti]</pluralform>
-<pluralform>[%x niti]</pluralform>
-<pluralform>[%x niti]</pluralform>
-</target>
-
<source>Encoding extended time information: %x</source>
<target>Podrobne informacije o času enkodiranja: %x</target>
@@ -289,9 +360,6 @@
<source>Cannot determine volume name for %x.</source>
<target>Ne morem določiti ime nosilca za %x.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>Ime nosilca %x ni del imena datoteke %y.</target>
-
<source>Abort requested: Waiting for current operation to finish...</source>
<target>Zahtevana je bila prekinitev: čakam, da se zaključi trenutna operacija...</target>
@@ -358,9 +426,6 @@
<source>Idle time between last detected change and execution of command</source>
<target>Čas nedejavnosti med zadnjo zaznano spremembo in izvršitvijo ukaza</target>
-<source>Command line</source>
-<target>Ukazna vrstica</target>
-
<source>
The command is triggered if:
- files or subfolders change
@@ -384,9 +449,6 @@ Ukaz se sproži če:
<source>RealtimeSync - Automated Synchronization</source>
<target>RealtimeSync - Avtomatizirana sinhronizacija</target>
-<source>Warning</source>
-<target>Pozor</target>
-
<source>Build: %x</source>
<target>Izgradnja: %x</target>
@@ -402,9 +464,6 @@ Ukaz se sproži če:
<source>&Exit</source>
<target>&Izhod</target>
-<source>Waiting for missing directories...</source>
-<target>Čakam na manjkajoče imenike...</target>
-
<source>Invalid command line:</source>
<target>Napačna ukazna vrstica:</target>
@@ -414,14 +473,14 @@ Ukaz se sproži če:
<source>File time and size</source>
<target>Čas in velikost datoteke</target>
-<source> Two way </source>
-<target> Obojesmerno </target>
+<source>Two way</source>
+<target>Obojesmerno</target>
<source>Mirror</source>
-<target>Zrcalno </target>
+<target>Zrcalno</target>
<source>Update</source>
-<target>Posodobi </target>
+<target>Posodobi</target>
<source>Custom</source>
<target>Po meri</target>
@@ -477,12 +536,6 @@ Ukaz se sproži če:
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Naslednji elementi imajo nerešene konflikte in ne bodo sinhronizirani:</target>
-<source>Significant difference detected:</source>
-<target>Zaznana je pomembna razlika:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>Več kot 50% od celotnega števila datotek bo kopiranih ali izbrisanih.</target>
-
<source>Not enough free disk space available in:</source>
<target>Na voljo ni dovolj prostega prostora na disku v:</target>
@@ -735,6 +788,15 @@ Ukaz se sproži če:
<source>&Pause</source>
<target>&Premor</target>
+<source>Variant</source>
+<target>Različica</target>
+
+<source>Statistics</source>
+<target>Statistika</target>
+
+<source>Don't show this dialog again</source>
+<target>Ne prikazuj več tega pogovora</target>
+
<source>Select a variant</source>
<target>Izberite spremenljivko</target>
@@ -942,15 +1004,6 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom.
<source>&Default</source>
<target>&Privzeto</target>
-<source>Variant</source>
-<target>Različica</target>
-
-<source>Statistics</source>
-<target>Statistika</target>
-
-<source>Don't show this dialog again</source>
-<target>Ne prikazuj več tega pogovora</target>
-
<source>Find what:</source>
<target>Najdi kaj</target>
@@ -960,15 +1013,15 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom.
<source>&Find next</source>
<target>&Najdi naslednje</target>
+<source>Start synchronization</source>
+<target>Začni sinhronizacijo</target>
+
<source>Delete</source>
<target>Izbriši</target>
<source>Configure filter</source>
<target>Konfiguriraj filter</target>
-<source>Start synchronization</source>
-<target>Začni sinhronizacijo</target>
-
<source>Find</source>
<target>Najdi</target>
@@ -1149,9 +1202,6 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom.
<source>All folders are in sync</source>
<target>Vse mape so sinhronizirane</target>
-<source>Comma separated list</source>
-<target>Seznam ločen z vejico</target>
-
<source>File list exported</source>
<target>Seznam datotek je bil izvožen</target>
@@ -1203,15 +1253,6 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom.
<source>Completed</source>
<target>Zaključeno</target>
-<source>Continue</source>
-<target>Nadaljuj</target>
-
-<source>Pause</source>
-<target>Premor</target>
-
-<source>Logging</source>
-<target>Beleženje</target>
-
<source>Cannot find %x</source>
<target>Ne najdem %x</target>
@@ -1309,9 +1350,6 @@ Opomba: Imena datoteka morajo biti relativna osnovnim imenikom.
<source>Append a timestamp to each file name</source>
<target>Dodaj časovno oznako k vsakemu imenu datoteke</target>
-<source>Folder</source>
-<target>Mapa</target>
-
<source>File</source>
<target>Datoteka</target>
diff --git a/BUILD/Languages/spanish.lng b/BUILD/Languages/spanish.lng
index 6f48fdd6..e1726b2b 100644
--- a/BUILD/Languages/spanish.lng
+++ b/BUILD/Languages/spanish.lng
@@ -7,6 +7,99 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
+<source>Log</source>
+<target></target>
+
+<source>&Continue</source>
+<target></target>
+
+<source>Comma-separated values</source>
+<target></target>
+
+<source>&Don't show this dialog again</source>
+<target></target>
+
+<source>&Execute</source>
+<target></target>
+
+<source>Confirm</source>
+<target></target>
+
+<source>
+<pluralform>Do you really want to execute the command %y for 1 item?</pluralform>
+<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
+</source>
+<target></target>
+
+<source>Minimize to notification area</source>
+<target></target>
+
+<source>Retrying operation after error:</source>
+<target></target>
+
+<source>The following folders are significantly different. Make sure you are matching the correct folders for synchronization.</source>
+<target></target>
+
+<source>&Show error</source>
+<target></target>
+
+<source>Waiting until all directories are available...</source>
+<target></target>
+
+<source>Directory monitoring active</source>
+<target></target>
+
+<source>Volume name %x is not part of file path %y.</source>
+<target></target>
+
+<source>%x items</source>
+<target></target>
+
+<source>
+<pluralform>1 thread</pluralform>
+<pluralform>%x threads</pluralform>
+</source>
+<target></target>
+
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target></target>
+
+<source>Any number of alternative directories for at most one config file.</source>
+<target></target>
+
+<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
+<target></target>
+
+<source>directory</source>
+<target></target>
+
+<source>config files</source>
+<target></target>
+
+<source>Syntax:</source>
+<target></target>
+
+<source>Directories cannot be set for more than one configuration file.</source>
+<target></target>
+
+<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
+<target></target>
+
+<source>Unequal number of left and right directories specified.</source>
+<target></target>
+
+<source>Cannot open file %x.</source>
+<target></target>
+
+<source>Syntax error</source>
+<target></target>
+
+<source>A directory path is expected after %x.</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Ambos lados han cambiado desde la última sincronización.</target>
@@ -49,15 +142,18 @@
<source>An exception occurred</source>
<target>Ha ocurrido una excepción</target>
-<source>Cannot find file %x.</source>
-<target>No se puede encontrar el archivo %x.</target>
-
<source>Error</source>
<target>Error</target>
<source>File %x does not contain a valid configuration.</source>
<target>El archivo %x no contiene una configuración válida.</target>
+<source>Warning</source>
+<target>Atención</target>
+
+<source>Command line</source>
+<target>Línea de comandos</target>
+
<source>A folder input field is empty.</source>
<target>Un campo de entrada de la carpeta está vacío.</target>
@@ -214,15 +310,6 @@
<source>Total time:</source>
<target>Tiempo total:</target>
-<source>
-<pluralform>1 Byte</pluralform>
-<pluralform>%x Bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</target>
-
<source>%x MB</source>
<target>%x MB</target>
@@ -241,15 +328,6 @@
<source>Scanning:</source>
<target>Escanear:</target>
-<source>
-<pluralform>[1 Thread]</pluralform>
-<pluralform>[%x Threads]</pluralform>
-</source>
-<target>
-<pluralform>[1 hilo]</pluralform>
-<pluralform>[%x hilos]</pluralform>
-</target>
-
<source>Encoding extended time information: %x</source>
<target>Condificando información de hora extendida: %x</target>
@@ -280,9 +358,6 @@
<source>Cannot determine volume name for %x.</source>
<target>No se puede determinar nombre del volumen de %x.</target>
-<source>Volume name %x not part of file name %y.</source>
-<target>El nombre de volumen %x no es parte del nombre de archivo %y.</target>
-
<source>Abort requested: Waiting for current operation to finish...</source>
<target>Anulación solicitada: esperando a que la operación actual finalice…</target>
@@ -349,9 +424,6 @@
<source>Idle time between last detected change and execution of command</source>
<target>Tiempo ocioso entre el último cambio detectado y la ejecución del comando</target>
-<source>Command line</source>
-<target>Línea de comandos</target>
-
<source>
The command is triggered if:
- files or subfolders change
@@ -375,9 +447,6 @@ El comando es disparado si::
<source>RealtimeSync - Automated Synchronization</source>
<target>RealtimeSync - Sincronización Automática</target>
-<source>Warning</source>
-<target>Atención</target>
-
<source>Build: %x</source>
<target>Completado: %x</target>
@@ -393,9 +462,6 @@ El comando es disparado si::
<source>&Exit</source>
<target>&Salir</target>
-<source>Waiting for missing directories...</source>
-<target>Esperando directorios faltantes…</target>
-
<source>Invalid command line:</source>
<target>Línea de comandos errónea:</target>
@@ -405,14 +471,14 @@ El comando es disparado si::
<source>File time and size</source>
<target>Fecha y tamaño del archivo</target>
-<source> Two way </source>
-<target> Dos formas </target>
+<source>Two way</source>
+<target>Dos formas</target>
<source>Mirror</source>
-<target>Espejo </target>
+<target>Espejo</target>
<source>Update</source>
-<target>Actualizar </target>
+<target>Actualizar</target>
<source>Custom</source>
<target>Personalizado</target>
@@ -468,12 +534,6 @@ El comando es disparado si::
<source>The following items have unresolved conflicts and will not be synchronized:</source>
<target>Los siguientes elementos tienen conflictos sin resolver y no serán sincronizados:</target>
-<source>Significant difference detected:</source>
-<target>Diferencia significante detectada:</target>
-
-<source>More than 50% of the total number of files will be copied or deleted.</source>
-<target>Más del 50% del número total de archivos serán copiados o eliminados.</target>
-
<source>Not enough free disk space available in:</source>
<target>Espacio en disco insuficiente en:</target>
@@ -726,6 +786,15 @@ El comando es disparado si::
<source>&Pause</source>
<target>&Pausa</target>
+<source>Variant</source>
+<target>Tipo</target>
+
+<source>Statistics</source>
+<target>Estadísticas</target>
+
+<source>Don't show this dialog again</source>
+<target>No volver a mostrar este diálogo</target>
+
<source>Select a variant</source>
<target>Seleccione una variante</target>
@@ -933,15 +1002,6 @@ Aviso: Los nombres de archivos deben ser relativos a los directorios base.
<source>&Default</source>
<target>&Configuración predeterminada</target>
-<source>Variant</source>
-<target>Tipo</target>
-
-<source>Statistics</source>
-<target>Estadísticas</target>
-
-<source>Don't show this dialog again</source>
-<target>No volver a mostrar este diálogo</target>
-
<source>Find what:</source>
<target>Buscar:</target>
@@ -951,15 +1011,15 @@ Aviso: Los nombres de archivos deben ser relativos a los directorios base.
<source>&Find next</source>
<target>&Buscar siguiente</target>
+<source>Start synchronization</source>
+<target>Iniciar sincronización</target>
+
<source>Delete</source>
<target>Eliminar</target>
<source>Configure filter</source>
<target>Configurar filtro</target>
-<source>Start synchronization</source>
-<target>Iniciar sincronización</target>
-
<source>Find</source>
<target>Buscar</target>
@@ -1134,9 +1194,6 @@ Aviso: Los nombres de archivos deben ser relativos a los directorios base.
<source>All folders are in sync</source>
<target>Todas las carpetas están sincronizadas</target>
-<source>Comma separated list</source>
-<target>Lista separada por comas</target>
-
<source>File list exported</source>
<target>Lista de archivos exportada</target>
@@ -1188,15 +1245,6 @@ Aviso: Los nombres de archivos deben ser relativos a los directorios base.
<source>Completed</source>
<target>Terminado</target>
-<source>Continue</source>
-<target>Continuar</target>
-
-<source>Pause</source>
-<target>Pausa</target>
-
-<source>Logging</source>
-<target>Registro</target>
-
<source>Cannot find %x</source>
<target>No se puede encontrar %x</target>
@@ -1290,9 +1338,6 @@ Aviso: Los nombres de archivos deben ser relativos a los directorios base.
<source>Append a timestamp to each file name</source>
<target>Incluir fecha y hora a cada nombre de archivo</target>
-<source>Folder</source>
-<target>Carpeta</target>
-
<source>File</source>
<target>Archivo</target>
diff --git a/BUILD/Resources.zip b/BUILD/Resources.zip
index 864883e8..4b79261a 100644
--- a/BUILD/Resources.zip
+++ b/BUILD/Resources.zip
Binary files differ
diff --git a/FreeFileSync.vcxproj b/FreeFileSync.vcxproj
index 0bf7114b..fd2c8db9 100644
--- a/FreeFileSync.vcxproj
+++ b/FreeFileSync.vcxproj
@@ -29,27 +29,27 @@
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@@ -92,6 +92,7 @@
<IntDir>OBJ\$(ProjectName)_VCPP_$(PlatformName)_$(Configuration)\</IntDir>
<GenerateManifest>false</GenerateManifest>
<TargetName>$(ProjectName)_$(PlatformName)</TargetName>
+ <RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@@ -99,7 +100,7 @@
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x86_debug_dll\mswud;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x86_debug_dll\mswud;.</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>wx+/pch.h</PrecompiledHeaderFile>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -114,7 +115,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>wxmsw28ud_aui.lib;wxmsw28ud_adv.lib;wxmsw28ud_core.lib;wxbase28ud_net.lib;wxbase28ud.lib;wxpngd.lib;wxzlibd.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies);Wininet.lib</AdditionalDependencies>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x86_debug_dll</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x86_debug_dll</AdditionalLibraryDirectories>
<LinkStatus>
</LinkStatus>
</Link>
@@ -129,7 +130,7 @@
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x64_debug_dll\mswud;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x64_debug_dll\mswud;.</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>wx+/pch.h</PrecompiledHeaderFile>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -146,7 +147,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>wxbase28ud.lib;wxmsw28ud_core.lib;wxmsw28ud_adv.lib;wxmsw28ud_aui.lib;wxbase28ud_net.lib;wxpngd.lib;wxzlibd.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies);Wininet.lib</AdditionalDependencies>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x64_debug_dll</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x64_debug_dll</AdditionalLibraryDirectories>
<LinkStatus>
</LinkStatus>
<ManifestFile>
@@ -164,14 +165,12 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x86_release_lib\mswu;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x86_release_lib\mswu;.</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles>
- <AdditionalOptions>
- </AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -180,7 +179,7 @@
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wxmsw28u_aui.lib;wxmsw28u_adv.lib;wxmsw28u_core.lib;wxbase28u.lib;wxpng.lib;wxzlib.lib;wxbase28u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies);Wininet.lib</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x86_release_lib</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x86_release_lib</AdditionalLibraryDirectories>
<LinkStatus>
</LinkStatus>
</Link>
@@ -199,23 +198,22 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x64_release_lib\mswu;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x64_release_lib\mswu;.</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ForcedIncludeFiles>zen/warn_static.h</ForcedIncludeFiles>
- <AdditionalOptions>
- </AdditionalOptions>
+ <EnablePREfast>false</EnablePREfast>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
- <GenerateDebugInformation>false</GenerateDebugInformation>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wxmsw28u_aui.lib;wxmsw28u_adv.lib;wxmsw28u_core.lib;wxbase28u.lib;wxpng.lib;wxzlib.lib;wxbase28u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies);Wininet.lib</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x64_release_lib</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x64_release_lib</AdditionalLibraryDirectories>
<LinkStatus>
</LinkStatus>
</Link>
diff --git a/RealtimeSync/RealtimeSync.vcxproj b/RealtimeSync/RealtimeSync.vcxproj
index 487f2cce..08abd76f 100644
--- a/RealtimeSync/RealtimeSync.vcxproj
+++ b/RealtimeSync/RealtimeSync.vcxproj
@@ -29,27 +29,27 @@
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
- <PlatformToolset>Windows7.1SDK</PlatformToolset>
+ <PlatformToolset>v120_xp</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@@ -99,7 +99,7 @@
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x86_debug_dll\mswud;..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x86_debug_dll\mswud;..</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>wx+/pch.h</PrecompiledHeaderFile>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -114,7 +114,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>wxmsw28ud_adv.lib;wxmsw28ud_core.lib;wxbase28ud.lib;wxpngd.lib;wxzlibd.lib;wxbase28ud_net.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x86_debug_dll</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x86_debug_dll</AdditionalLibraryDirectories>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>C:\Program Files\C++\wxWidgets\include</AdditionalIncludeDirectories>
@@ -127,7 +127,7 @@
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;__WXDEBUG__;WXUSINGDLL;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x64_debug_dll\mswud;..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x64_debug_dll\mswud;..</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>wx+/pch.h</PrecompiledHeaderFile>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@@ -144,7 +144,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>wxmsw28ud_adv.lib;wxmsw28ud_core.lib;wxbase28ud.lib;wxpngd.lib;wxzlibd.lib;wxbase28ud_net.lib;comctl32.lib;ws2_32.lib;Rpcrt4.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x64_debug_dll</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x64_debug_dll</AdditionalLibraryDirectories>
<LinkStatus>
</LinkStatus>
</Link>
@@ -160,7 +160,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x86_release_lib\mswu;..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x86_release_lib\mswu;..</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -174,7 +174,7 @@
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wxbase28u.lib;wxmsw28u_adv.lib;wxmsw28u_core.lib;wxpng.lib;wxzlib.lib;wxbase28u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x86_release_lib</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x86_release_lib</AdditionalLibraryDirectories>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>C:\Program Files\C++\wxWidgets\include</AdditionalIncludeDirectories>
@@ -191,7 +191,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;wxUSE_UNICODE;__WXMSW__;ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc10_x64_release_lib\mswu;..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>C:\Program Files\C++\Boost;C:\Program Files\C++\wxWidgets\include;C:\Program Files\C++\wxWidgets\lib\vc12_x64_release_lib\mswu;..</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<DisableSpecificWarnings>4100;4512</DisableSpecificWarnings>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -205,7 +205,7 @@
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wxmsw28u_adv.lib;wxmsw28u_core.lib;wxbase28u.lib;wxpng.lib;wxzlib.lib;wxbase28u_net.lib;comctl32.lib;ws2_32.lib;winmm.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
- <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc10_x64_release_lib</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>C:\Program Files\C++\Boost\stage_x64\lib;C:\Program Files\C++\wxWidgets\lib\vc12_x64_release_lib</AdditionalLibraryDirectories>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>C:\Program Files\C++\wxWidgets\include</AdditionalIncludeDirectories>
@@ -246,9 +246,9 @@
</ClCompile>
<ClCompile Include="gui_generated.cpp" />
<ClCompile Include="main_dlg.cpp" />
+ <ClCompile Include="monitor.cpp" />
<ClCompile Include="resources.cpp" />
<ClCompile Include="tray_menu.cpp" />
- <ClCompile Include="watcher.cpp" />
<ClCompile Include="xml_ffs.cpp" />
<ClCompile Include="xml_proc.cpp" />
</ItemGroup>
diff --git a/RealtimeSync/application.cpp b/RealtimeSync/application.cpp
index a7aff305..af8a40a9 100644
--- a/RealtimeSync/application.cpp
+++ b/RealtimeSync/application.cpp
@@ -36,19 +36,20 @@ IMPLEMENT_APP(Application);
namespace
{
+/*
boost::thread::id mainThreadId = boost::this_thread::get_id();
void onTerminationRequested()
{
- std::wstring msg = boost::this_thread::get_id() == mainThreadId ?
- L"Termination requested in main thread!\n\n" :
- L"Termination requested in worker thread!\n\n";
- msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync";
+std::wstring msg = boost::this_thread::get_id() == mainThreadId ?
+ L"Termination requested in main thread!\n\n" :
+ L"Termination requested in worker thread!\n\n";
+msg += L"Please file a bug report at: http://sourceforge.net/projects/freefilesync";
- wxSafeShowMessage(_("An exception occurred"), msg);
- std::abort();
+wxSafeShowMessage(_("An exception occurred"), msg);
+std::abort();
}
-
+*/
#ifdef _MSC_VER
void crtInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { assert(false); }
#endif
@@ -59,7 +60,7 @@ const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType();
bool Application::OnInit()
{
- std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads
+ //std::set_terminate(onTerminationRequested); //unlike wxWidgets uncaught exception handling, this works for all worker threads
#ifdef ZEN_WIN
#ifdef _MSC_VER
@@ -107,12 +108,12 @@ void Application::onEnterEventLoop(wxEvent& event)
}
catch (const FileError& e)
{
- wxMessageBox(e.toString(), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(e.toString(), L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR);
//continue!
}
//try to set config/batch-filename set by %1 parameter
- std::vector<wxString> commandArgs;
+ std::vector<Zstring> commandArgs;
for (int i = 1; i < argc; ++i)
{
Zstring filename = toZ(argv[i]);
@@ -125,14 +126,14 @@ void Application::onEnterEventLoop(wxEvent& event)
filename += Zstr(".ffs_batch");
else
{
- wxMessageBox(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename)), L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR);
return;
}
}
- commandArgs.push_back(toWx(filename));
+ commandArgs.push_back(filename);
}
- wxString cfgFilename;
+ Zstring cfgFilename;
if (!commandArgs.empty())
cfgFilename = commandArgs[0];
diff --git a/RealtimeSync/gui_generated.cpp b/RealtimeSync/gui_generated.cpp
index cf072240..659f7ea4 100644
--- a/RealtimeSync/gui_generated.cpp
+++ b/RealtimeSync/gui_generated.cpp
@@ -100,6 +100,8 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr
bSizer1->Add( m_staticText7, 0, wxALL, 5 );
m_panelMainFolder = new wxPanel( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panelMainFolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
wxFlexGridSizer* fgSizer1;
fgSizer1 = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizer1->AddGrowableCol( 1 );
@@ -152,6 +154,8 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr
m_scrolledWinFolders = new wxScrolledWindow( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
m_scrolledWinFolders->SetScrollRate( 10, 10 );
+ m_scrolledWinFolders->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
bSizerFolders = new wxBoxSizer( wxVERTICAL );
@@ -243,6 +247,8 @@ MainDlgGenerated::~MainDlgGenerated()
FolderGenerated::FolderGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style )
{
+ this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
wxBoxSizer* bSizer114;
bSizer114 = new wxBoxSizer( wxHORIZONTAL );
diff --git a/RealtimeSync/gui_generated.h b/RealtimeSync/gui_generated.h
index 2b42b271..aabb23fc 100644
--- a/RealtimeSync/gui_generated.h
+++ b/RealtimeSync/gui_generated.h
@@ -140,7 +140,7 @@ protected:
public:
- ErrorDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Error"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
+ ErrorDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Error"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~ErrorDlgGenerated();
};
diff --git a/RealtimeSync/main_dlg.cpp b/RealtimeSync/main_dlg.cpp
index b750b110..b7a0aee1 100644
--- a/RealtimeSync/main_dlg.cpp
+++ b/RealtimeSync/main_dlg.cpp
@@ -9,8 +9,7 @@
#include <wx/msgdlg.h>
#include <wx/wupdlock.h>
#include <wx/filedlg.h>
-#include <wx/utils.h>
-#include <wx/filedlg.h>
+//#include <wx/utils.h>
#include <wx+/button.h>
#include <wx+/string_conv.h>
#include <wx+/mouse_move_dlg.h>
@@ -18,7 +17,6 @@
#include <zen/assert_static.h>
#include <zen/file_handling.h>
#include <zen/build_info.h>
-#include "watcher.h"
#include "xml_proc.h"
#include "tray_menu.h"
#include "xml_ffs.h"
@@ -44,7 +42,7 @@ private:
};
-MainDialog::MainDialog(wxDialog* dlg, const wxString& cfgFileName)
+MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName)
: MainDlgGenerated(dlg)
{
#ifdef ZEN_WIN
@@ -73,20 +71,20 @@ MainDialog::MainDialog(wxDialog* dlg, const wxString& cfgFileName)
//--------------------------- load config values ------------------------------------
xmlAccess::XmlRealConfig newConfig;
- const wxString currentConfigFile = cfgFileName.empty() ? lastConfigFileName() : cfgFileName;
+ const Zstring currentConfigFile = cfgFileName.empty() ? lastConfigFileName() : cfgFileName;
bool loadCfgSuccess = false;
- if (!cfgFileName.empty() || wxFileExists(lastConfigFileName()))
+ if (!cfgFileName.empty() || fileExists(lastConfigFileName()))
try
{
- rts::readRealOrBatchConfig(toZ(currentConfigFile), newConfig); //throw FfsXmlError
+ rts::readRealOrBatchConfig(currentConfigFile, newConfig); //throw FfsXmlError
loadCfgSuccess = true;
}
catch (const xmlAccess::FfsXmlError& error)
{
if (error.getSeverity() == xmlAccess::FfsXmlError::WARNING)
- wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING, this);
+ wxMessageBox(error.toString(),L"RealtimeSync" + _("Warning"), wxOK | wxICON_WARNING, this);
else
- wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(error.toString(), L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR, this);
}
const bool startWatchingImmediately = loadCfgSuccess && !cfgFileName.empty();
@@ -127,11 +125,11 @@ MainDialog::~MainDialog()
try //write config to XML
{
- writeRealConfig(currentCfg, toZ(lastConfigFileName())); //throw FfsXmlError
+ writeRealConfig(currentCfg, lastConfigFileName()); //throw FfsXmlError
}
catch (const xmlAccess::FfsXmlError& error)
{
- wxMessageBox(error.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(error.toString().c_str(), L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR, this);
}
}
@@ -145,9 +143,9 @@ void MainDialog::onProcessAsyncTasks(wxEvent& event)
}
-const wxString& MainDialog::lastConfigFileName()
+const Zstring& MainDialog::lastConfigFileName()
{
- static wxString instance = toWx(zen::getConfigDir()) + L"LastRun.ffs_real";
+ static Zstring instance = zen::getConfigDir() + Zstr("LastRun.ffs_real");
return instance;
}
@@ -206,54 +204,59 @@ void MainDialog::OnStart(wxCommandEvent& event)
break;
}
Show(); //don't show for EXIT_APP
+ Raise();
}
void MainDialog::OnConfigSave(wxCommandEvent& event)
{
- wxString defaultFileName = currentConfigFileName.empty() ? L"Realtime.ffs_real" : currentConfigFileName;
+ Zstring defaultFileName = currentConfigFileName.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName;
//attention: currentConfigFileName may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config!
- if (endsWith(defaultFileName, L".ffs_batch"))
- replace(defaultFileName, L".ffs_batch", L".ffs_real", false);
+ if (endsWith(defaultFileName, Zstr(".ffs_batch")))
+ replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_real"), false);
- wxFileDialog filePicker(this, wxEmptyString, wxEmptyString, defaultFileName,
+ wxFileDialog filePicker(this,
+ wxEmptyString,
+ //OS X really needs dir/file separated like this:
+ utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found
+ utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found
wxString(L"RealtimeSync (*.ffs_real)|*.ffs_real") + L"|" +_("All files") + L" (*.*)|*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
return;
- const wxString newFileName = filePicker.GetPath();
+ const Zstring newFileName = utfCvrtTo<Zstring>(filePicker.GetPath());
//write config to XML
const xmlAccess::XmlRealConfig currentCfg = getConfiguration();
try
{
- writeRealConfig(currentCfg, toZ(newFileName)); //throw FfsXmlError
+ writeRealConfig(currentCfg, newFileName); //throw FfsXmlError
setLastUsedConfig(newFileName);
}
catch (const xmlAccess::FfsXmlError& e)
{
- wxMessageBox(e.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(e.toString().c_str(), L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR, this);
}
}
-void MainDialog::loadConfig(const wxString& filename)
+void MainDialog::loadConfig(const Zstring& filename)
{
xmlAccess::XmlRealConfig newConfig;
try
{
- rts::readRealOrBatchConfig(toZ(filename), newConfig);
+ rts::readRealOrBatchConfig(filename, newConfig);
}
catch (const xmlAccess::FfsXmlError& error)
{
if (error.getSeverity() == xmlAccess::FfsXmlError::WARNING)
- wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING, this);
+ wxMessageBox(error.toString(), L"RealtimeSync" + _("Warning"), wxOK | wxICON_WARNING, this);
else
{
- wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(error.toString(), L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR, this);
return;
}
}
@@ -263,7 +266,7 @@ void MainDialog::loadConfig(const wxString& filename)
}
-void MainDialog::setLastUsedConfig(const wxString& filename)
+void MainDialog::setLastUsedConfig(const Zstring& filename)
{
//set title
if (filename == lastConfigFileName())
@@ -273,7 +276,7 @@ void MainDialog::setLastUsedConfig(const wxString& filename)
}
else
{
- SetTitle(filename);
+ SetTitle(utfCvrtTo<wxString>(filename));
currentConfigFileName = filename;
}
}
@@ -281,11 +284,14 @@ void MainDialog::setLastUsedConfig(const wxString& filename)
void MainDialog::OnConfigLoad(wxCommandEvent& event)
{
- wxFileDialog filePicker(this, wxEmptyString, wxEmptyString, wxEmptyString,
+ wxFileDialog filePicker(this,
+ wxEmptyString,
+ utfCvrtTo<wxString>(beforeLast(currentConfigFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found
+ wxEmptyString,
wxString(L"RealtimeSync (*.ffs_real;*.ffs_batch)|*.ffs_real;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*",
wxFD_OPEN);
if (filePicker.ShowModal() == wxID_OK)
- loadConfig(filePicker.GetPath());
+ loadConfig(utfCvrtTo<Zstring>(filePicker.GetPath()));
}
@@ -293,7 +299,7 @@ void MainDialog::onFilesDropped(FileDropEvent& event)
{
const auto& files = event.getFiles();
if (!files.empty())
- loadConfig(files[0]);
+ loadConfig(utfCvrtTo<Zstring>(files[0]));
}
@@ -307,14 +313,14 @@ void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg)
if (!cfg.directories.empty())
{
//fill top folder
- dirNameFirst->setName(*cfg.directories.begin());
+ dirNameFirst->setName(utfCvrtTo<wxString>(*cfg.directories.begin()));
//fill additional folders
- addFolder(std::vector<wxString>(cfg.directories.begin() + 1, cfg.directories.end()));
+ addFolder(std::vector<Zstring>(cfg.directories.begin() + 1, cfg.directories.end()));
}
//fill commandline
- m_textCtrlCommand->SetValue(cfg.commandline);
+ m_textCtrlCommand->SetValue(utfCvrtTo<wxString>(cfg.commandline));
//set delay
m_spinCtrlDelay->SetValue(static_cast<int>(cfg.delay));
@@ -325,11 +331,11 @@ xmlAccess::XmlRealConfig MainDialog::getConfiguration()
{
xmlAccess::XmlRealConfig output;
- output.directories.push_back(dirNameFirst->getName());
- for (std::vector<DirectoryPanel*>::const_iterator i = dirNamesExtra.begin(); i != dirNamesExtra.end(); ++i)
- output.directories.push_back((*i)->getName());
+ output.directories.push_back(utfCvrtTo<Zstring>(dirNameFirst->getName()));
+ for (auto it = dirNamesExtra.begin(); it != dirNamesExtra.end(); ++it)
+ output.directories.push_back(utfCvrtTo<Zstring>((*it)->getName()));
- output.commandline = m_textCtrlCommand->GetValue();
+ output.commandline = utfCvrtTo<Zstring>(m_textCtrlCommand->GetValue());
output.delay = m_spinCtrlDelay->GetValue();
return output;
@@ -338,12 +344,12 @@ xmlAccess::XmlRealConfig MainDialog::getConfiguration()
void MainDialog::OnAddFolder(wxCommandEvent& event)
{
- const wxString topFolder = dirNameFirst->getName();
+ const Zstring topFolder = utfCvrtTo<Zstring>(dirNameFirst->getName());
//clear existing top folder first
dirNameFirst->setName(wxString());
- std::vector<wxString> newFolders;
+ std::vector<Zstring> newFolders;
newFolders.push_back(topFolder);
addFolder(newFolders, true); //add pair in front of additonal pairs
@@ -383,7 +389,7 @@ static const size_t MAX_ADD_FOLDERS = 6;
#endif
-void MainDialog::addFolder(const std::vector<wxString>& newFolders, bool addFront)
+void MainDialog::addFolder(const std::vector<Zstring>& newFolders, bool addFront)
{
if (newFolders.size() == 0)
return;
@@ -391,7 +397,7 @@ void MainDialog::addFolder(const std::vector<wxString>& newFolders, bool addFron
wxWindowUpdateLocker dummy(this); //avoid display distortion
int folderHeight = 0;
- for (std::vector<wxString>::const_iterator i = newFolders.begin(); i != newFolders.end(); ++i)
+ for (auto it = newFolders.begin(); it != newFolders.end(); ++it)
{
//add new folder pair
DirectoryPanel* newFolder = new DirectoryPanel(m_scrolledWinFolders);
@@ -415,7 +421,7 @@ void MainDialog::addFolder(const std::vector<wxString>& newFolders, bool addFron
newFolder->m_bpButtonRemoveFolder->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolder), nullptr, this );
//insert directory name
- newFolder->setName(*i);
+ newFolder->setName(utfCvrtTo<wxString>(*it));
}
//set size of scrolled window
diff --git a/RealtimeSync/main_dlg.h b/RealtimeSync/main_dlg.h
index 5a028f3a..2a1fb03e 100644
--- a/RealtimeSync/main_dlg.h
+++ b/RealtimeSync/main_dlg.h
@@ -10,9 +10,10 @@
#include "gui_generated.h"
#include <vector>
#include <memory>
+#include <zen/zstring.h>
+#include <zen/async_task.h>
#include <wx+/file_drop.h>
#include "../ui/dir_name.h"
-#include <zen/async_task.h>
namespace xmlAccess
{
@@ -24,11 +25,11 @@ class DirectoryPanel;
class MainDialog: public MainDlgGenerated
{
public:
- MainDialog(wxDialog* dlg, const wxString& cfgFileName);
+ MainDialog(wxDialog* dlg, const Zstring& cfgFileName);
~MainDialog();
private:
- void loadConfig(const wxString& filename);
+ void loadConfig(const Zstring& filename);
virtual void OnClose (wxCloseEvent& event) { Destroy(); }
virtual void OnShowHelp (wxCommandEvent& event);
@@ -45,21 +46,21 @@ private:
void setConfiguration(const xmlAccess::XmlRealConfig& cfg);
xmlAccess::XmlRealConfig getConfiguration();
- void setLastUsedConfig(const wxString& filename);
+ void setLastUsedConfig(const Zstring& filename);
void layoutAsync(); //call Layout() asynchronously
- void addFolder(const wxString& dirname, bool addFront = false);
- void addFolder(const std::vector<wxString>& newFolders, bool addFront = false);
+ //void addFolder(const Zstring& dirname, bool addFront = false);
+ void addFolder(const std::vector<Zstring>& newFolders, bool addFront = false);
void removeAddFolder(size_t pos);
void clearAddFolders();
- static const wxString& lastConfigFileName();
+ static const Zstring& lastConfigFileName();
std::unique_ptr<zen::DirectoryName<wxTextCtrl>> dirNameFirst;
std::vector<DirectoryPanel*> dirNamesExtra; //additional pairs to the standard pair
- wxString currentConfigFileName;
+ Zstring currentConfigFileName;
void onProcessAsyncTasks(wxEvent& event);
diff --git a/RealtimeSync/makefile b/RealtimeSync/makefile
index 865d73a1..d84c425f 100644
--- a/RealtimeSync/makefile
+++ b/RealtimeSync/makefile
@@ -58,7 +58,7 @@ CPP_LIST+=gui_generated.cpp
CPP_LIST+=main_dlg.cpp
CPP_LIST+=resources.cpp
CPP_LIST+=tray_menu.cpp
-CPP_LIST+=watcher.cpp
+CPP_LIST+=monitor.cpp
CPP_LIST+=xml_ffs.cpp
CPP_LIST+=xml_proc.cpp
CPP_LIST+=../structures.cpp
diff --git a/RealtimeSync/watcher.cpp b/RealtimeSync/monitor.cpp
index 6ef5e924..88536281 100644
--- a/RealtimeSync/watcher.cpp
+++ b/RealtimeSync/monitor.cpp
@@ -4,16 +4,14 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#include "watcher.h"
+#include "monitor.h"
+#include <ctime>
#include <set>
-#include <zen/tick_count.h>
#include <zen/file_handling.h>
-#include <zen/stl_tools.h>
#include <zen/dir_watcher.h>
#include <zen/thread.h>
-#include <zen/assert_static.h>
#include <zen/tick_count.h>
-#include <wx+/string_conv.h>
+#include <wx/utils.h>
#include "../lib/resolve_path.h"
//#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header
//#include "../library/lock_holder.h" //LOCK_FILE_ENDING
@@ -23,24 +21,6 @@ using namespace zen;
namespace
{
-const std::int64_t TICKS_UPDATE_INTERVAL = rts::UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
-TickVal lastExec = getTicks();
-};
-
-bool rts::updateUiIsAllowed()
-{
- const TickVal now = getTicks(); //0 on error
- if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary
- {
- lastExec = now;
- return true;
- }
- return false;
-}
-
-
-namespace
-{
const int CHECK_DIR_INTERVAL = 1; //unit: [s]
@@ -53,10 +33,28 @@ std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& dirs) //throw
return std::vector<Zstring>(tmp.begin(), tmp.end());
}
-}
-rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt, WaitCallback& statusHandler) //throw FileError
+//wait until changes are detected or if a directory is not available (anymore)
+struct WaitResult
+{
+ enum ChangeType
+ {
+ CHANGE_DETECTED,
+ CHANGE_DIR_MISSING
+ };
+
+ WaitResult(const zen::DirWatcher::Entry& changedItem) : type(CHANGE_DETECTED), changedItem_(changedItem) {}
+ WaitResult(const Zstring& dirname) : type(CHANGE_DIR_MISSING), dirname_(dirname) {}
+
+ ChangeType type;
+ zen::DirWatcher::Entry changedItem_; //for type == CHANGE_DETECTED: file or directory
+ Zstring dirname_; //for type == CHANGE_DIR_MISSING
+};
+
+
+WaitResult waitForChanges(const std::vector<Zstring>& dirNamesNonFmt, //throw FileError
+ const std::function<void(bool)>& onRefreshGui) //bool: readyForSync
{
const std::vector<Zstring> dirNamesFmt = getFormattedDirs(dirNamesNonFmt); //throw FileError
if (dirNamesFmt.empty()) //pathological case, but we have to check else this function will wait endlessly
@@ -72,20 +70,17 @@ rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt,
{
//a non-existent network path may block, so check existence asynchronously!
auto ftDirExists = async([=] { return zen::dirExists(dirnameFmt); });
- while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL / 2)))
- statusHandler.requestUiRefresh(); //may throw!
+ //we need to check dirExists(), not somethingExists(): it's not clear if DirWatcher detects a type clash (file instead of directory!)
+ while (!ftDirExists.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2)))
+ onRefreshGui(false); //may throw!
if (!ftDirExists.get())
return WaitResult(dirnameFmt);
- watches.push_back(std::make_pair(dirnameFmt, std::make_shared<DirWatcher>(dirnameFmt))); //throw FileError, ErrorNotExisting
+ watches.push_back(std::make_pair(dirnameFmt, std::make_shared<DirWatcher>(dirnameFmt))); //throw FileError
}
- catch (ErrorNotExisting&) //nice atomic behavior: *no* second directory existence check!!!
+ catch (FileError&)
{
- return WaitResult(dirnameFmt);
- }
- catch (FileError&) //play safe: remedy potential FileErrors that should have been ErrorNotExisting (e.g. Linux: errors during directory traversing)
- {
- if (!dirExists(dirnameFmt)) //file system race condition!!
+ if (!somethingExists(dirnameFmt)) //a benign(?) race condition with FileError
return WaitResult(dirnameFmt);
throw;
}
@@ -114,12 +109,11 @@ rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt,
//IMPORTANT CHECK: dirwatcher has problems detecting removal of top watched directories!
if (checkDirExistNow)
- if (!dirExists(dirname)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED
+ if (!dirExists(dirname)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED -> somethingExists() is NOT sufficient here!
return WaitResult(dirname);
-
try
{
- std::vector<DirWatcher::Entry> changedItems = watcher.getChanges([&] { statusHandler.requestUiRefresh(); }); //throw FileError, ErrorNotExisting
+ std::vector<DirWatcher::Entry> changedItems = watcher.getChanges([&] { onRefreshGui(false); /*may throw!*/ }); //throw FileError
//remove to be ignored changes
vector_remove_if(changedItems, [](const DirWatcher::Entry& e)
@@ -139,26 +133,23 @@ rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNamesNonFmt,
}
}
- catch (ErrorNotExisting&) //nice atomic behavior: *no* second directory existence check!!!
+ catch (FileError&)
{
- return WaitResult(dirname);
- }
- catch (FileError&) //play safe: remedy potential FileErrors that should have been ErrorNotExisting (e.g. Linux: errors during directory traversing)
- {
- if (!dirExists(dirname)) //file system race condition!!
+ if (!somethingExists(dirname)) //a benign(?) race condition with FileError
return WaitResult(dirname);
throw;
}
}
boost::this_thread::sleep(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2));
- statusHandler.requestUiRefresh(true); //throw ?: may start sync at this presumably idle time
+ onRefreshGui(true); //throw ?: may start sync at this presumably idle time
}
}
-//support for monitoring newly connected directories volumes (e.g.: USB-sticks)
-void rts::waitForMissingDirs(const std::vector<Zstring>& dirNamesNonFmt, WaitCallback& statusHandler) //throw FileError
+//wait until all directories become available (again) + logs in network share
+void waitForMissingDirs(const std::vector<Zstring>& dirNamesNonFmt, //throw FileError
+ const std::function<void(const Zstring&)>& onRefreshGui) //Zstring: the directory that is currently being waited for
{
while (true)
{
@@ -179,24 +170,112 @@ void rts::waitForMissingDirs(const std::vector<Zstring>& dirNamesNonFmt, WaitCal
return zen::dirExists(dirnameFmt);
});
while (!ftDirExisting.timed_wait(boost::posix_time::milliseconds(rts::UI_UPDATE_INTERVAL / 2)))
- statusHandler.requestUiRefresh(); //may throw!
+ onRefreshGui(dirnameFmt); //may throw!
if (!ftDirExisting.get())
{
allExisting = false;
+ //wait some time...
+ const int refreshInterval = rts::UI_UPDATE_INTERVAL / 2;
+ assert_static(CHECK_DIR_INTERVAL * 1000 % refreshInterval == 0);
+ for (int i = 0; i < CHECK_DIR_INTERVAL * 1000 / refreshInterval; ++i)
+ {
+ onRefreshGui(dirnameFmt); //may throw!
+ boost::this_thread::sleep(boost::posix_time::milliseconds(refreshInterval));
+ }
break;
}
}
if (allExisting)
return;
+ }
+}
+
+
+inline
+wxString toString(DirWatcher::ActionType type)
+{
+ switch (type)
+ {
+ case DirWatcher::ACTION_CREATE:
+ return L"CREATE";
+ case DirWatcher::ACTION_UPDATE:
+ return L"UPDATE";
+ case DirWatcher::ACTION_DELETE:
+ return L"DELETE";
+ }
+ return L"ERROR";
+}
+
+struct ExecCommandNowException {};
+}
+
+
+void rts::monitorDirectories(const std::vector<Zstring>& dirNamesNonFmt, unsigned int delay, rts::MonitorCallback& callback)
+{
+ if (dirNamesNonFmt.empty())
+ {
+ assert(false);
+ return;
+ }
+
+ auto execMonitoring = [&] //throw FileError
+ {
+ callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING);
+ waitForMissingDirs(dirNamesNonFmt, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError
+ callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE);
+
+ //schedule initial execution (*after* all directories have arrived, which could take some time which we don't want to include)
+ time_t nextExecDate = std::time(nullptr) + delay;
+
+ while (true) //loop over command invocations
+ {
+ DirWatcher::Entry lastChangeDetected;
+ try
+ {
+ while (true) //loop over detected changes
+ {
+ //wait for changes (and for all directories to become available)
+ WaitResult res = waitForChanges(dirNamesNonFmt, [&](bool readyForSync) //throw FileError, ExecCommandNowException
+ {
+ if (readyForSync)
+ if (nextExecDate <= std::time(nullptr))
+ throw ExecCommandNowException(); //abort wait and start sync
+ callback.requestUiRefresh();
+ });
+ switch (res.type)
+ {
+ case WaitResult::CHANGE_DIR_MISSING: //don't execute the command before all directories are available!
+ callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING);
+ waitForMissingDirs(dirNamesNonFmt, [&](const Zstring& dirname) { callback.requestUiRefresh(); }); //throw FileError
+ callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE);
+ break;
+
+ case WaitResult::CHANGE_DETECTED:
+ lastChangeDetected = res.changedItem_;
+ break;
+ }
+ nextExecDate = std::time(nullptr) + delay;
+ }
+ }
+ catch (ExecCommandNowException&) {}
+
+ ::wxSetEnv(L"change_path", utfCvrtTo<wxString>(lastChangeDetected.filename_)); //some way to output what file changed to the user
+ ::wxSetEnv(L"change_action", toString(lastChangeDetected.action_)); //
- //wait some time...
- const int refreshInterval = UI_UPDATE_INTERVAL / 2;
- assert_static(1000 * CHECK_DIR_INTERVAL % refreshInterval == 0);
- for (int i = 0; i < 1000 * CHECK_DIR_INTERVAL / refreshInterval; ++i)
+ //execute command
+ callback.executeExternalCommand();
+ nextExecDate = std::numeric_limits<time_t>::max();
+ }
+ };
+
+ while (true)
+ try
{
- boost::this_thread::sleep(boost::posix_time::milliseconds(refreshInterval));
- statusHandler.requestUiRefresh();
+ execMonitoring(); //throw FileError
+ }
+ catch (const zen::FileError& e)
+ {
+ callback.reportError(e.toString());
}
- }
}
diff --git a/RealtimeSync/monitor.h b/RealtimeSync/monitor.h
new file mode 100644
index 00000000..0b9dfbc0
--- /dev/null
+++ b/RealtimeSync/monitor.h
@@ -0,0 +1,38 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef MONITOR_HEADER_345087425834253425
+#define MONITOR_HEADER_345087425834253425
+
+#include <functional>
+#include <zen/zstring.h>
+
+namespace rts
+{
+const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss
+
+
+struct MonitorCallback
+{
+ virtual ~MonitorCallback() {}
+
+ enum WatchPhase
+ {
+ MONITOR_PHASE_ACTIVE,
+ MONITOR_PHASE_WAITING,
+ };
+ virtual void setPhase(WatchPhase mode) = 0;
+ virtual void executeExternalCommand() = 0;
+ virtual void requestUiRefresh() = 0;
+ virtual void reportError(const std::wstring& msg) = 0; //automatically retries after return!
+};
+void monitorDirectories(const std::vector<Zstring>& dirNamesNonFmt,
+ //non-formatted dirnames that yet require call to getFormattedDirectoryName(); empty directories must be checked by caller!
+ unsigned int delay,
+ MonitorCallback& callback);
+}
+
+#endif //MONITOR_HEADER_345087425834253425
diff --git a/RealtimeSync/tray_menu.cpp b/RealtimeSync/tray_menu.cpp
index 676904f1..33758ad2 100644
--- a/RealtimeSync/tray_menu.cpp
+++ b/RealtimeSync/tray_menu.cpp
@@ -5,28 +5,20 @@
// **************************************************************************
#include "tray_menu.h"
-#include <algorithm>
-#include <iterator>
-#include <limits>
-#include <set>
-#include <zen/assert_static.h>
#include <zen/build_info.h>
+#include <zen/tick_count.h>
+#include <zen/thread.h>
#include <wx+/mouse_move_dlg.h>
#include <wx+/image_tools.h>
-#include <wx+/string_conv.h>
+//#include <wx+/string_conv.h>
#include <wx+/shell_execute.h>
#include <wx+/std_button_order.h>
-#include <wx/msgdlg.h>
#include <wx/taskbar.h>
-#include <wx/app.h>
-#include <wx/utils.h>
-#include <wx/menu.h>
-#include <wx/utils.h>
#include <wx/icon.h> //Linux needs this
-#include <wx/timer.h>
+#include <wx/app.h>
#include "resources.h"
#include "gui_generated.h"
-#include "watcher.h"
+#include "monitor.h"
#include "../lib/resolve_path.h"
using namespace rts;
@@ -35,67 +27,149 @@ using namespace zen;
namespace
{
-struct AbortCallback //never throw exceptions through a C-Layer (GUI)!
+const std::int64_t TICKS_UPDATE_INTERVAL = rts::UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
+TickVal lastExec = getTicks();
+
+bool updateUiIsAllowed()
+{
+ const TickVal now = getTicks(); //0 on error
+ if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary
+ {
+ lastExec = now;
+ return true;
+ }
+ return false;
+}
+
+
+enum TrayMode
{
- virtual ~AbortCallback() {}
- virtual void requestResume() = 0;
- virtual void requestAbort() = 0;
+ TRAY_MODE_ACTIVE,
+ TRAY_MODE_WAITING,
+ TRAY_MODE_ERROR,
};
-//RtsTrayIcon is a dumb class whose sole purpose is to enable wxWidgets deferred deletion
-class RtsTrayIconRaw : public wxTaskBarIcon
+class TrayIconObject : public wxTaskBarIcon
{
public:
- RtsTrayIconRaw(AbortCallback& abortCb) : abortCb_(&abortCb)
+ TrayIconObject(const wxString& jobname) :
+ resumeRequested(false),
+ abortRequested(false),
+ showErrorMsgRequested(false),
+ mode(TRAY_MODE_ACTIVE),
+ iconFlashStatusLast(false),
+ jobName_(jobname),
+#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X
+ trayBmp(getResourceImage(L"RTS_tray_16x16")) //use a 16x16 bitmap
+#elif defined ZEN_LINUX
+ trayBmp(getResourceImage(L"RTS_tray_24x24")) //use a 24x24 bitmap for perfect fit
+#endif
{
- Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(RtsTrayIconRaw::OnDoubleClick), nullptr, this);
+ Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(TrayIconObject::OnDoubleClick), nullptr, this);
+ setMode(mode);
}
- void dontCallBackAnymore() { abortCb_ = nullptr; } //call before tray icon is marked for deferred deletion
+ //require polling:
+ bool resumeIsRequested() const { return resumeRequested; }
+ bool abortIsRequested () const { return abortRequested; }
+
+ //during TRAY_MODE_ERROR those two functions are available:
+ void clearShowErrorRequested() { assert(mode == TRAY_MODE_ERROR); showErrorMsgRequested = false; }
+ bool getShowErrorRequested() const { assert(mode == TRAY_MODE_ERROR); return showErrorMsgRequested; }
+
+ void setMode(TrayMode m)
+ {
+ mode = m;
+ timer.Stop();
+ timer.Disconnect(wxEVT_TIMER, wxEventHandler(TrayIconObject::OnErrorFlashIcon), nullptr, this);
+ switch (m)
+ {
+ case TRAY_MODE_ACTIVE:
+ setTrayIcon(trayBmp, _("Directory monitoring active"));
+ break;
+
+ case TRAY_MODE_WAITING:
+ setTrayIcon(greyScale(trayBmp), _("Waiting until all directories are available..."));
+ break;
+
+ case TRAY_MODE_ERROR:
+ timer.Connect(wxEVT_TIMER, wxEventHandler(TrayIconObject::OnErrorFlashIcon), nullptr, this);
+ timer.Start(500); //timer interval in [ms]
+ break;
+ }
+ }
private:
+ void OnErrorFlashIcon(wxEvent& event)
+ {
+ iconFlashStatusLast = !iconFlashStatusLast;
+ setTrayIcon(iconFlashStatusLast ? trayBmp : greyScale(trayBmp), _("Error"));
+ }
+
+ void setTrayIcon(const wxBitmap& bmp, const wxString& statusTxt)
+ {
+ wxIcon realtimeIcon;
+ realtimeIcon.CopyFromBitmap(bmp);
+ wxString tooltip = L"RealtimeSync\n" + statusTxt;
+ if (!jobName_.empty())
+ tooltip += L"\n\"" + jobName_ + L"\"";
+ SetIcon(realtimeIcon, tooltip);
+ }
+
enum Selection
{
- CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of Zero does not work under Mac"
+ CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of zero does not work under Mac"
+ CONTEXT_SHOW_ERROR,
CONTEXT_ABORT = wxID_EXIT,
CONTEXT_ABOUT = wxID_ABOUT
};
virtual wxMenu* CreatePopupMenu()
{
- if (!abortCb_)
- return nullptr;
-
wxMenu* contextMenu = new wxMenu;
- contextMenu->Append(CONTEXT_RESTORE, _("&Restore"));
+ switch (mode)
+ {
+ case TRAY_MODE_ACTIVE:
+ case TRAY_MODE_WAITING:
+ contextMenu->Append(CONTEXT_RESTORE, _("&Restore"));
+ break;
+ case TRAY_MODE_ERROR:
+ contextMenu->Append(CONTEXT_SHOW_ERROR, _("&Show error"));
+ break;
+ }
contextMenu->Append(CONTEXT_ABOUT, _("&About"));
contextMenu->AppendSeparator();
contextMenu->Append(CONTEXT_ABORT, _("&Exit"));
//event handling
- contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RtsTrayIconRaw::OnContextMenuSelection), nullptr, this);
+ contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TrayIconObject::OnContextMenuSelection), nullptr, this);
return contextMenu; //ownership transferred to caller
}
void OnContextMenuSelection(wxCommandEvent& event)
{
- if (!abortCb_)
- return;
-
switch (static_cast<Selection>(event.GetId()))
{
case CONTEXT_ABORT:
- abortCb_->requestAbort();
+ abortRequested = true;
break;
case CONTEXT_RESTORE:
- abortCb_->requestResume();
+ resumeRequested = true;
+ break;
+
+ case CONTEXT_SHOW_ERROR:
+ showErrorMsgRequested = true;
break;
case CONTEXT_ABOUT:
{
- //build information
+ //ATTENTION: the modal dialog below does NOT disable all GUI input, e.g. user may still double-click on tray icon
+ //no crash in this context, but the double-click is remembered and executed after the modal dialog quits
+ SetEvtHandlerEnabled(false);
+ ZEN_ON_SCOPE_EXIT(SetEvtHandlerEnabled(true));
+
wxString build = __TDATE__;
#if wxUSE_UNICODE
build += L" - Unicode";
@@ -103,7 +177,6 @@ private:
build += L" - ANSI";
#endif //wxUSE_UNICODE
- //compile time info about 32/64-bit build
if (zen::is64BitBuild)
build += L" x64";
else
@@ -118,136 +191,86 @@ private:
void OnDoubleClick(wxCommandEvent& event)
{
- if (abortCb_)
- abortCb_->requestResume();
- }
-
- AbortCallback* abortCb_;
-};
-
-
-class TrayIconHolder
-{
-public:
- TrayIconHolder(const wxString& jobname, AbortCallback& abortCb) :
- jobName_(jobname),
- trayMenu(new RtsTrayIconRaw(abortCb))
- {
- showIconActive();
+ switch (mode)
+ {
+ case TRAY_MODE_ACTIVE:
+ case TRAY_MODE_WAITING:
+ resumeRequested = true; //never throw exceptions through a C-Layer call stack (GUI)!
+ break;
+ case TRAY_MODE_ERROR:
+ showErrorMsgRequested = true;
+ break;
+ }
}
- ~TrayIconHolder()
- {
- trayMenu->RemoveIcon(); //(try to) hide icon until final deletion takes place
- trayMenu->dontCallBackAnymore();
+ bool resumeRequested;
+ bool abortRequested;
+ bool showErrorMsgRequested;
- //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking)
- if (!wxPendingDelete.Member(trayMenu))
- wxPendingDelete.Append(trayMenu);
- }
+ TrayMode mode;
- void doUiRefreshNow()
- {
- wxTheApp->Yield();
- } //yield is UI-layer which is represented by this tray icon
+ bool iconFlashStatusLast; //flash try icon for TRAY_MODE_ERROR
+ wxTimer timer; //
- void showIconActive()
- {
- wxIcon realtimeIcon;
-#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X
- realtimeIcon.CopyFromBitmap(getResourceImage(L"RTS_tray_16x16")); //use a 16x16 bitmap
-#elif defined ZEN_LINUX
- realtimeIcon.CopyFromBitmap(getResourceImage(L"RTS_tray_24x24")); //use a 24x24 bitmap for perfect fit
-#endif
- wxString tooltip = L"RealtimeSync";
- if (!jobName_.empty())
- tooltip += L"\n\"" + jobName_ + L"\"";
- trayMenu->SetIcon(realtimeIcon, tooltip);
- }
-
- void showIconWaiting()
- {
- wxIcon realtimeIcon;
-#if defined ZEN_WIN || defined ZEN_MAC
- realtimeIcon.CopyFromBitmap(greyScale(getResourceImage(L"RTS_tray_16x16")));
-#elif defined ZEN_LINUX
- realtimeIcon.CopyFromBitmap(greyScale(getResourceImage(L"RTS_tray_24x24")));
-#endif
- wxString tooltip = _("Waiting for missing directories...");
- if (!jobName_.empty())
- tooltip += L"\n\"" + jobName_ + L"\"";
- trayMenu->SetIcon(realtimeIcon, tooltip);
- }
-
-private:
const wxString jobName_; //RTS job name, may be empty
- RtsTrayIconRaw* trayMenu;
+ const wxBitmap trayBmp;
};
-//##############################################################################################################
-
-struct AbortMonitoring//exception class
+struct AbortMonitoring //exception class
{
AbortMonitoring(AbortReason reasonCode) : reasonCode_(reasonCode) {}
AbortReason reasonCode_;
};
-class StartSyncNowException {};
-
-//##############################################################################################################
-class WaitCallbackImpl : public rts::WaitCallback, private AbortCallback
+//=> don't derive from wxEvtHandler or any other wxWidgets object unless instance is safely deleted (deferred) during idle event!!tray_icon.h
+class TrayIconHolder
{
public:
- WaitCallbackImpl(const wxString& jobname) :
- trayIcon(jobname, *this),
- nextSyncStart_(std::numeric_limits<long>::max()),
- resumeRequested(false),
- abortRequested(false) {}
-
- void notifyAllDirectoriesExist() { trayIcon.showIconActive(); }
- void notifyDirectoryMissing () { trayIcon.showIconWaiting(); }
+ TrayIconHolder(const wxString& jobname) :
+ trayObj(new TrayIconObject(jobname)) {}
- void scheduleNextSync(long nextSyncStart) { nextSyncStart_ = nextSyncStart; }
- void clearSchedule() { nextSyncStart_ = std::numeric_limits<long>::max(); }
+ ~TrayIconHolder()
+ {
+ //harmonize with tray_icon.cpp!!!
+ trayObj->RemoveIcon();
+ //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking)
+ wxPendingDelete.Append(trayObj);
+ }
- //implement WaitCallback
- virtual void requestUiRefresh(bool readyForSync) //throw StartSyncNowException, AbortMonitoring
+ void doUiRefreshNow() //throw AbortMonitoring
{
- if (resumeRequested)
+ wxTheApp->Yield(); //yield is UI-layer which is represented by this tray icon
+
+ //advantage of polling vs callbacks: we can throw exceptions!
+ if (trayObj->resumeIsRequested())
throw AbortMonitoring(SHOW_GUI);
- if (abortRequested)
+ if (trayObj->abortIsRequested())
throw AbortMonitoring(EXIT_APP);
+ }
- if (readyForSync)
- if (nextSyncStart_ <= wxGetLocalTime())
- throw StartSyncNowException(); //abort wait and start sync
+ void setMode(TrayMode m) { trayObj->setMode(m); }
- if (updateUiIsAllowed())
- trayIcon.doUiRefreshNow();
- }
+ bool getShowErrorRequested() const { return trayObj->getShowErrorRequested(); }
+ void clearShowErrorRequested() { trayObj->clearShowErrorRequested(); }
private:
- //implement AbortCallback: used from C-GUI call stack
- virtual void requestResume() { resumeRequested = true; }
- virtual void requestAbort () { abortRequested = true; }
-
- TrayIconHolder trayIcon;
- long nextSyncStart_;
- bool resumeRequested;
- bool abortRequested;
+ TrayIconObject* trayObj;
};
+//##############################################################################################################
-
+//#define ERROR_DLG_ENABLE_TIMEOUT
class ErrorDlgWithTimeout : public ErrorDlgGenerated
{
public:
ErrorDlgWithTimeout(wxWindow* parent, const wxString& messageText) :
- ErrorDlgGenerated(parent),
- secondsLeft(15) //give user some time to read msg!?
+ ErrorDlgGenerated(parent)
+#ifdef ERROR_DLG_ENABLE_TIMEOUT
+ , secondsLeft(15) //give user some time to read msg!?
+#endif
{
#ifdef ZEN_WIN
new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this"
@@ -257,11 +280,12 @@ public:
m_bitmap10->SetBitmap(getResourceImage(L"msg_error"));
m_textCtrl8->SetValue(messageText);
+#ifdef ERROR_DLG_ENABLE_TIMEOUT
//count down X seconds then automatically press "retry"
timer.Connect(wxEVT_TIMER, wxEventHandler(ErrorDlgWithTimeout::OnTimerEvent), nullptr, this);
timer.Start(1000); //timer interval in ms
updateButtonLabel();
-
+#endif
Fit(); //child-element widths have changed: image was set
m_buttonRetry->SetFocus();
}
@@ -273,6 +297,7 @@ public:
};
private:
+#ifdef ERROR_DLG_ENABLE_TIMEOUT
void OnTimerEvent(wxEvent& event)
{
if (secondsLeft <= 0)
@@ -289,20 +314,23 @@ private:
m_buttonRetry->SetLabel(_("&Retry") + L" (" + replaceCpy(_P("1 sec", "%x sec", secondsLeft), L"%x", numberTo<std::wstring>(secondsLeft)) + L")");
Layout();
}
+#endif
void OnClose(wxCloseEvent& event) { EndModal(BUTTON_ABORT); }
void OnRetry(wxCommandEvent& event) { EndModal(BUTTON_RETRY); }
void OnAbort(wxCommandEvent& event) { EndModal(BUTTON_ABORT); }
+#ifdef ERROR_DLG_ENABLE_TIMEOUT
int secondsLeft;
wxTimer timer;
+#endif
};
-bool reportErrorTimeout(const std::wstring& msg) //return true if timeout or user selected "retry", else abort
+bool reportErrorTimeout(const std::wstring& msg) //return true: "retry"; false: "abort"
{
ErrorDlgWithTimeout errorDlg(nullptr, msg);
- //errorDlg.Raise(); -> don't steal focus every X seconds
+ errorDlg.Raise();
switch (static_cast<ErrorDlgWithTimeout::ButtonPressed>(errorDlg.ShowModal()))
{
case ErrorDlgWithTimeout::BUTTON_RETRY:
@@ -312,120 +340,90 @@ bool reportErrorTimeout(const std::wstring& msg) //return true if timeout or use
}
return false;
}
-
-
-inline
-wxString toString(DirWatcher::ActionType type)
-{
- switch (type)
- {
- case DirWatcher::ACTION_CREATE:
- return L"CREATE";
- case DirWatcher::ACTION_UPDATE:
- return L"UPDATE";
- case DirWatcher::ACTION_DELETE:
- return L"DELETE";
- }
- return L"ERROR";
-}
}
-/*
-Data Flow:
-----------
-
-TrayIconHolder (GUI output)
- /|\
- |
-WaitCallbackImpl
- /|\
- |
-startDirectoryMonitor() (wire dir-changes and execution of commandline)
-*/
-
rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname)
{
- std::vector<Zstring> dirList = toZ(config.directories);
- vector_remove_if(dirList, [](Zstring str) -> bool { trim(str); return str.empty(); }); //remove empty entries WITHOUT formatting dirList yet!
+ std::vector<Zstring> dirNamesNonFmt = config.directories;
+ vector_remove_if(dirNamesNonFmt, [](Zstring str) -> bool { trim(str); return str.empty(); }); //remove empty entries WITHOUT formatting paths yet!
- if (dirList.empty())
+ if (dirNamesNonFmt.empty())
{
- wxMessageBox(_("A folder input field is empty."), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(_("A folder input field is empty."), L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR);
return SHOW_GUI;
}
- wxString cmdLine = config.commandline;
+ Zstring cmdLine = config.commandline;
trim(cmdLine);
if (cmdLine.empty())
{
- wxMessageBox(_("Invalid command line:") + L" \"\"", _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(_("Invalid command line:") + L" \"\"", L"RealtimeSync" + _("Error"), wxOK | wxICON_ERROR);
return SHOW_GUI;
}
- try
+ struct MonitorCallbackImpl : public MonitorCallback
{
- DirWatcher::Entry lastChangeDetected;
- WaitCallbackImpl callback(jobname);
+ MonitorCallbackImpl(const wxString& jobname,
+ const Zstring& cmdLine) : trayIcon(jobname), cmdLine_(cmdLine) {}
+
+ virtual void setPhase(WatchPhase mode)
+ {
+ switch (mode)
+ {
+ case MONITOR_PHASE_ACTIVE:
+ trayIcon.setMode(TRAY_MODE_ACTIVE);
+ break;
+ case MONITOR_PHASE_WAITING:
+ trayIcon.setMode(TRAY_MODE_WAITING);
+ break;
+ }
+ }
- auto execMonitoring = [&] //throw FileError, AbortMonitoring
+ virtual void executeExternalCommand()
{
- callback.notifyDirectoryMissing();
- callback.clearSchedule();
- waitForMissingDirs(dirList, callback); //throw FileError, StartSyncNowException(not scheduled yet), AbortMonitoring
- callback.notifyAllDirectoriesExist();
+ auto cmdLineExp = expandMacros(cmdLine_);
+ zen::shellExecute(cmdLineExp, zen::EXEC_TYPE_SYNC);
+ }
- //schedule initial execution (*after* all directories have arrived, which could take some time which we don't want to include)
- callback.scheduleNextSync(wxGetLocalTime() + static_cast<long>(config.delay));
+ virtual void requestUiRefresh()
+ {
+ if (updateUiIsAllowed())
+ trayIcon.doUiRefreshNow(); //throw AbortMonitoring
+ }
+
+ virtual void reportError(const std::wstring& msg)
+ {
+ trayIcon.setMode(TRAY_MODE_ERROR);
+ trayIcon.clearShowErrorRequested();
- while (true)
+ //wait for some time, then return to retry
+ assert_static(15 * 1000 % UI_UPDATE_INTERVAL == 0);
+ for (int i = 0; i < 15 * 1000 / UI_UPDATE_INTERVAL; ++i)
{
- try
+ trayIcon.doUiRefreshNow(); //throw AbortMonitoring
+
+ if (trayIcon.getShowErrorRequested())
{
- while (true)
- {
- //wait for changes (and for all directories to become available)
- WaitResult res = waitForChanges(dirList, callback); //throw FileError, StartSyncNowException, AbortMonitoring
- switch (res.type)
- {
- case CHANGE_DIR_MISSING: //don't execute the commandline before all directories are available!
- callback.notifyDirectoryMissing();
- callback.clearSchedule();
- waitForMissingDirs(dirList, callback); //throw FileError, StartSyncNowException(not scheduled yet), AbortMonitoring
- callback.notifyAllDirectoriesExist();
- break;
-
- case CHANGE_DETECTED:
- lastChangeDetected = res.changedItem_;
- break;
- }
- callback.scheduleNextSync(wxGetLocalTime() + static_cast<long>(config.delay));
- }
+ if (reportErrorTimeout(msg)) //return true: "retry"; false: "abort"
+ return;
+ else
+ throw AbortMonitoring(SHOW_GUI);
}
- catch (StartSyncNowException&) {}
-
- ::wxSetEnv(L"change_path", utfCvrtTo<wxString>(lastChangeDetected.filename_)); //some way to output what file changed to the user
- ::wxSetEnv(L"change_action", toString(lastChangeDetected.action_)); //
- lastChangeDetected = DirWatcher::Entry(); //make sure old name is not shown again after a directory reappears
-
- //execute command
- auto cmdLineExp = expandMacros(utfCvrtTo<Zstring>(cmdLine));
- zen::shellExecute(cmdLineExp, zen::EXEC_TYPE_SYNC);
- callback.clearSchedule();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL));
}
- };
+ }
- while (true)
- try
- {
- execMonitoring(); //throw FileError, AbortMonitoring
- }
- catch (const zen::FileError& e)
- {
- if (!reportErrorTimeout(e.toString())) //return true if timeout or user selected "retry", else abort
- return SHOW_GUI;
- }
+ TrayIconHolder trayIcon;
+ const Zstring cmdLine_;
+ } cb(jobname, cmdLine);
+
+ try
+ {
+ monitorDirectories(dirNamesNonFmt, config.delay, cb); //cb: throw AbortMonitoring
+ assert(false);
+ return SHOW_GUI;
}
catch (const AbortMonitoring& ab)
{
diff --git a/RealtimeSync/tray_menu.h b/RealtimeSync/tray_menu.h
index 006dae82..1f71a017 100644
--- a/RealtimeSync/tray_menu.h
+++ b/RealtimeSync/tray_menu.h
@@ -4,13 +4,12 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef TRAYMENU_H_INCLUDED
-#define TRAYMENU_H_INCLUDED
+#ifndef TRAY_583967857420987534253245
+#define TRAY_583967857420987534253245
-#include "watcher.h"
+#include <wx/string.h>
#include "xml_proc.h"
-
namespace rts
{
enum AbortReason
@@ -21,5 +20,4 @@ enum AbortReason
AbortReason startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname); //jobname may be empty
}
-
-#endif // TRAYMENU_H_INCLUDED
+#endif //TRAY_583967857420987534253245
diff --git a/RealtimeSync/watcher.h b/RealtimeSync/watcher.h
deleted file mode 100644
index 2fd32119..00000000
--- a/RealtimeSync/watcher.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#ifndef WATCHER_H_INCLUDED
-#define WATCHER_H_INCLUDED
-
-#include <zen/dir_watcher.h>
-#include <zen/file_error.h>
-
-
-namespace rts
-{
-const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss
-bool updateUiIsAllowed();
-
-
-class WaitCallback
-{
-public:
- virtual ~WaitCallback() {}
- virtual void requestUiRefresh(bool readyForSync = false) = 0; //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh()
-};
-
-
-//wait until changes are detected or if a directory is not available (anymore)
-enum ChangeType
-{
- CHANGE_DETECTED,
- CHANGE_DIR_MISSING
-};
-
-struct WaitResult
-{
- WaitResult(const zen::DirWatcher::Entry& changedItem) : type(CHANGE_DETECTED), changedItem_(changedItem) {}
- WaitResult(const Zstring& dirname) : type(CHANGE_DIR_MISSING), dirname_(dirname) {}
-
- ChangeType type;
- zen::DirWatcher::Entry changedItem_; //for type == CHANGE_DETECTED: file or directory
- Zstring dirname_; //for type == CHANGE_DIR_MISSING
-};
-
-WaitResult waitForChanges(const std::vector<Zstring>& dirNamesNonFmt,
- //non-formatted dirnames that yet require call to getFormattedDirectoryName(); empty directories must be checked by caller!
- WaitCallback& statusHandler); //throw FileError
-
-//wait until all directories become available (again) + logs in network share
-void waitForMissingDirs(const std::vector<Zstring>& dirNamesNonFmt,
- WaitCallback& statusHandler); //throw FileError
-}
-
-#endif // WATCHER_H_INCLUDED
diff --git a/RealtimeSync/xml_ffs.cpp b/RealtimeSync/xml_ffs.cpp
index 1f3fb1b1..149b0bbe 100644
--- a/RealtimeSync/xml_ffs.cpp
+++ b/RealtimeSync/xml_ffs.cpp
@@ -7,7 +7,7 @@
#include "xml_ffs.h"
#include "../lib/ffs_paths.h"
#include <zen/zstring.h>
-#include <wx+/string_conv.h>
+//#include <wx+/string_conv.h>
//include FreeFileSync xml headers
#include "../lib/process_xml.h"
@@ -17,8 +17,6 @@ using namespace zen;
xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& batchCfg, const Zstring& filename)
{
- xmlAccess::XmlRealConfig output;
-
std::set<Zstring, LessFilename> uniqueFolders;
//add main folders
@@ -35,13 +33,9 @@ xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& bat
uniqueFolders.erase(Zstring());
- output.directories.clear();
- std::transform(uniqueFolders.begin(), uniqueFolders.end(), std::back_inserter(output.directories),
- [](const Zstring & fn) { return toWx(fn); });
-
- output.commandline = std::wstring(L"\"") + zen::getFreeFileSyncLauncher() + L"\"" +
- L" \"" + filename + L"\"";
-
+ xmlAccess::XmlRealConfig output;
+ output.directories.assign(uniqueFolders.begin(), uniqueFolders.end());
+ output.commandline = Zstr("\"") + zen::getFreeFileSyncLauncher() + Zstr("\" \"") + filename + Zstr("\"");
return output;
}
diff --git a/RealtimeSync/xml_proc.h b/RealtimeSync/xml_proc.h
index ab57e816..671a237f 100644
--- a/RealtimeSync/xml_proc.h
+++ b/RealtimeSync/xml_proc.h
@@ -8,7 +8,7 @@
#define XMLPROCESSING_H_INCLUDED
#include <vector>
-#include <wx/string.h>
+//#include <wx/string.h>
#include <zen/zstring.h>
#include "../lib/xml_base.h"
@@ -18,9 +18,9 @@ namespace xmlAccess
struct XmlRealConfig
{
XmlRealConfig() : delay(10) {}
- std::vector<wxString> directories;
- wxString commandline;
- size_t delay;
+ std::vector<Zstring> directories;
+ Zstring commandline;
+ unsigned int delay;
};
void readRealConfig(const Zstring& filename, XmlRealConfig& config); //throw FfsXmlError
diff --git a/algorithm.cpp b/algorithm.cpp
index ab2494e5..2415cd18 100644
--- a/algorithm.cpp
+++ b/algorithm.cpp
@@ -57,9 +57,9 @@ private:
//##################### schedule old temporary files for deletion ####################
if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING))
- return fileObj.setSyncDir(SYNC_DIR_LEFT);
+ return fileObj.setSyncDir(SyncDirection::LEFT);
else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING))
- return fileObj.setSyncDir(SYNC_DIR_RIGHT);
+ return fileObj.setSyncDir(SyncDirection::RIGHT);
//####################################################################################
switch (cat)
@@ -81,13 +81,13 @@ private:
break;
case FILE_CONFLICT:
case FILE_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize"
- if (dirCfg.conflict == SYNC_DIR_NONE)
+ if (dirCfg.conflict == SyncDirection::NONE)
fileObj.setSyncDirConflict(fileObj.getCatExtraDescription()); //take over category conflict
else
fileObj.setSyncDir(dirCfg.conflict);
break;
case FILE_EQUAL:
- fileObj.setSyncDir(SYNC_DIR_NONE);
+ fileObj.setSyncDir(SyncDirection::NONE);
break;
}
}
@@ -110,7 +110,7 @@ private:
break;
case SYMLINK_CONFLICT:
case SYMLINK_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize"
- if (dirCfg.conflict == SYNC_DIR_NONE)
+ if (dirCfg.conflict == SyncDirection::NONE)
linkObj.setSyncDirConflict(linkObj.getCatExtraDescription()); //take over category conflict
else
linkObj.setSyncDir(dirCfg.conflict);
@@ -119,7 +119,7 @@ private:
linkObj.setSyncDir(dirCfg.different);
break;
case SYMLINK_EQUAL:
- linkObj.setSyncDir(SYNC_DIR_NONE);
+ linkObj.setSyncDir(SyncDirection::NONE);
break;
}
}
@@ -130,9 +130,9 @@ private:
//########### schedule abandoned temporary recycle bin directory for deletion ##########
if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING))
- return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); //
+ return setSyncDirectionRec(SyncDirection::LEFT, dirObj); //
else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING))
- return setSyncDirectionRec(SYNC_DIR_RIGHT, dirObj); //don't recurse below!
+ return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below!
//#######################################################################################
switch (cat)
@@ -144,10 +144,10 @@ private:
dirObj.setSyncDir(dirCfg.exRightSideOnly);
break;
case DIR_EQUAL:
- dirObj.setSyncDir(SYNC_DIR_NONE);
+ dirObj.setSyncDir(SyncDirection::NONE);
break;
case DIR_DIFFERENT_METADATA: //use setting from "conflict/cannot categorize"
- if (dirCfg.conflict == SYNC_DIR_NONE)
+ if (dirCfg.conflict == SyncDirection::NONE)
dirObj.setSyncDirConflict(dirObj.getCatExtraDescription()); //take over category conflict
else
dirObj.setSyncDir(dirCfg.conflict);
@@ -408,9 +408,9 @@ private:
//##################### schedule old temporary files for deletion ####################
if (cat == FILE_LEFT_SIDE_ONLY && endsWith(fileObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING))
- return fileObj.setSyncDir(SYNC_DIR_LEFT);
+ return fileObj.setSyncDir(SyncDirection::LEFT);
else if (cat == FILE_RIGHT_SIDE_ONLY && endsWith(fileObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING))
- return fileObj.setSyncDir(SYNC_DIR_RIGHT);
+ return fileObj.setSyncDir(SyncDirection::RIGHT);
//####################################################################################
//try to find corresponding database entry
@@ -432,7 +432,7 @@ private:
if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance))
fileObj.setSyncDirConflict(txtDbNotInSync);
else
- fileObj.setSyncDir(changeOnLeft ? SYNC_DIR_RIGHT : SYNC_DIR_LEFT);
+ fileObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
}
else
{
@@ -468,7 +468,7 @@ private:
if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance))
linkObj.setSyncDirConflict(txtDbNotInSync);
else
- linkObj.setSyncDir(changeOnLeft ? SYNC_DIR_RIGHT : SYNC_DIR_LEFT);
+ linkObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
}
else
{
@@ -485,9 +485,9 @@ private:
//########### schedule abandoned temporary recycle bin directory for deletion ##########
if (cat == DIR_LEFT_SIDE_ONLY && endsWith(dirObj.getShortName<LEFT_SIDE>(), TEMP_FILE_ENDING))
- return setSyncDirectionRec(SYNC_DIR_LEFT, dirObj); //
+ return setSyncDirectionRec(SyncDirection::LEFT, dirObj); //
else if (cat == DIR_RIGHT_SIDE_ONLY && endsWith(dirObj.getShortName<RIGHT_SIDE>(), TEMP_FILE_ENDING))
- return setSyncDirectionRec(SYNC_DIR_RIGHT, dirObj); //don't recurse below!
+ return setSyncDirectionRec(SyncDirection::RIGHT, dirObj); //don't recurse below!
//#######################################################################################
//try to find corresponding database entry
@@ -511,7 +511,7 @@ private:
if (dbEntry && !stillInSync(dbEntry->second))
dirObj.setSyncDirConflict(txtDbNotInSync);
else
- dirObj.setSyncDir(changeOnLeft ? SYNC_DIR_RIGHT : SYNC_DIR_LEFT);
+ dirObj.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
}
else
{
@@ -599,11 +599,11 @@ private:
Algorithm:
----------
- DB-file left <--- (name, size, date) ---> DB-file right
- | |
- | (file ID, size, date) | (file ID, size, date)
- \|/ \|/
- file left only file right only
+ DB-file left <--- (name, size, date) ---> DB-file right
+ | |
+ | (file ID, size, date) | (file ID, size, date)
+ \|/ \|/
+ file left only file right only
FAT caveat: File Ids are generally not stable when file is either moved or renamed!
=> 1. Move/rename operations on FAT cannot be detected reliably.
@@ -1384,10 +1384,10 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
auto cfgIter = baseDirCfgs.find(&fsObj.root());
if (cfgIter != baseDirCfgs.end())
{
- SyncDirection newDir = SYNC_DIR_NONE;
+ SyncDirection newDir = SyncDirection::NONE;
if (cfgIter->second.var == DirectionConfig::AUTOMATIC)
- newDir = fsObj.isEmpty<LEFT_SIDE>() ? SYNC_DIR_RIGHT : SYNC_DIR_LEFT;
+ newDir = fsObj.isEmpty<LEFT_SIDE>() ? SyncDirection::RIGHT : SyncDirection::LEFT;
else
{
const DirectionSet& dirCfg = extractDirections(cfgIter->second);
diff --git a/comparison.cpp b/comparison.cpp
index dff58786..c674269e 100644
--- a/comparison.cpp
+++ b/comparison.cpp
@@ -83,6 +83,26 @@ std::set<Zstring, LessFilename> determineExistentDirs(const std::set<Zstring, Le
tryReportingError2([&]
{
+ warn_static("remove after test")
+#if 0
+ dirsEx.clear();
+ std::for_each(dirnames.begin(), dirnames.end(),
+ [&](const Zstring& dirname)
+ {
+ if (!dirname.empty())
+ {
+ loginNetworkShare(dirname, allowUserInteraction);
+
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
+ if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) //returns true for (dir-)symlinks also
+ dirsEx.insert(dirname);
+ else
+ throw FileError(_("Cannot find the following folders:") + L"\n" + std::wstring(L"\n") + dirname,
+ attr == INVALID_FILE_ATTRIBUTES ? formatSystemError(L"GetFileAttributes", getLastError()) : L"not a directory!");
+ }
+ });
+
+#else
dirsEx = getExistingDirsUpdating(dirnames, allowUserInteraction, callback); //check *all* directories on each try!
//get list of not existing directories
@@ -95,6 +115,7 @@ std::set<Zstring, LessFilename> determineExistentDirs(const std::set<Zstring, Le
std::for_each(dirsMissing.begin(), dirsMissing.end(), [&](const Zstring& dirname) { msg += std::wstring(L"\n") + dirname; });
throw FileError(msg, _("You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization."));
}
+#endif
}, callback);
return dirsEx;
@@ -268,8 +289,8 @@ std::wstring getConflictInvalidDate(const Zstring& fileNameFull, Int64 utcTime)
std::wstring getConflictSameDateDiffSize(const FilePair& fileObj)
{
return replaceCpy(_("Files %x have the same date but a different size."), L"%x", fmtFileName(fileObj.getObjRelativeName())) + L"\n" +
- arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<LEFT_SIDE >()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<LEFT_SIDE>()) + L"\n" +
- arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<RIGHT_SIDE>());
+ L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<LEFT_SIDE >()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<LEFT_SIDE>()) + L"\n" +
+ L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(fileObj.getFileSize<RIGHT_SIDE>());
}
@@ -277,8 +298,8 @@ inline
std::wstring getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj)
{
return _("Items differ in attributes only") + L"\n" +
- arrowLeft + L" " + fmtFileName(fsObj.getShortName<LEFT_SIDE >()) + L"\n" +
- arrowRight + L" " + fmtFileName(fsObj.getShortName<RIGHT_SIDE>());
+ L" " + arrowLeft + L" " + fmtFileName(fsObj.getShortName<LEFT_SIDE >()) + L"\n" +
+ L" " + arrowRight + L" " + fmtFileName(fsObj.getShortName<RIGHT_SIDE>());
}
@@ -286,8 +307,8 @@ template <class FileOrLinkPair> inline
std::wstring getDescrDiffMetaDate(const FileOrLinkPair& fileObj)
{
return _("Items differ in attributes only") + L"\n" +
- arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<LEFT_SIDE >()) + L"\n" +
- arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<RIGHT_SIDE>());
+ L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<LEFT_SIDE >()) + L"\n" +
+ L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(fileObj.template getLastWriteTime<RIGHT_SIDE>());
}
//-----------------------------------------------------------------------------
diff --git a/file_hierarchy.cpp b/file_hierarchy.cpp
index 13c73731..c20b971e 100644
--- a/file_hierarchy.cpp
+++ b/file_hierarchy.cpp
@@ -30,9 +30,8 @@ void HierarchyObject::removeEmptyRec()
if (emptyExisting) //notify if actual deletion happened
notifySyncCfgChanged(); //mustn't call this in ~FileSystemObject(), since parent, usually a DirPair, is already partially destroyed and existing as a pure HierarchyObject!
- // for (auto& subDir : refSubDirs())
- // subDir.removeEmptyRec(); //recurse
- std::for_each(refSubDirs().begin(), refSubDirs().end(), [&](HierarchyObject& hierObj) { hierObj.removeEmptyRec(); });
+ for (auto& subDir : refSubDirs())
+ subDir.removeEmptyRec(); //recurse
}
namespace
@@ -42,7 +41,7 @@ SyncOperation getIsolatedSyncOperation(CompareFilesResult cmpResult,
SyncDirection syncDir,
bool hasDirConflict) //perf: std::wstring was wasteful here
{
- assert(!hasDirConflict || syncDir == SYNC_DIR_NONE);
+ assert(!hasDirConflict || syncDir == SyncDirection::NONE);
if (!selectedForSynchronization)
return cmpResult == FILE_EQUAL ?
@@ -54,11 +53,11 @@ SyncOperation getIsolatedSyncOperation(CompareFilesResult cmpResult,
case FILE_LEFT_SIDE_ONLY:
switch (syncDir)
{
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
return SO_DELETE_LEFT; //delete files on left
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
return SO_CREATE_NEW_RIGHT; //copy files to right
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING;
}
break;
@@ -66,11 +65,11 @@ SyncOperation getIsolatedSyncOperation(CompareFilesResult cmpResult,
case FILE_RIGHT_SIDE_ONLY:
switch (syncDir)
{
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
return SO_CREATE_NEW_LEFT; //copy files to left
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
return SO_DELETE_RIGHT; //delete files on right
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING;
}
break;
@@ -81,11 +80,11 @@ SyncOperation getIsolatedSyncOperation(CompareFilesResult cmpResult,
case FILE_CONFLICT:
switch (syncDir)
{
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
return SO_OVERWRITE_LEFT; //copy from right to left
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
return SO_OVERWRITE_RIGHT; //copy from left to right
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING;
}
break;
@@ -93,17 +92,17 @@ SyncOperation getIsolatedSyncOperation(CompareFilesResult cmpResult,
case FILE_DIFFERENT_METADATA:
switch (syncDir)
{
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
return SO_COPY_METADATA_TO_LEFT;
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
return SO_COPY_METADATA_TO_RIGHT;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
return hasDirConflict ? SO_UNRESOLVED_CONFLICT : SO_DO_NOTHING;
}
break;
case FILE_EQUAL:
- assert(syncDir == SYNC_DIR_NONE);
+ assert(syncDir == SyncDirection::NONE);
return SO_EQUAL;
}
diff --git a/file_hierarchy.h b/file_hierarchy.h
index 7710e7ac..1675d223 100644
--- a/file_hierarchy.h
+++ b/file_hierarchy.h
@@ -379,7 +379,7 @@ public:
//sync settings
SyncDirection getSyncDir() const;
void setSyncDir(SyncDirection newDir);
- void setSyncDirConflict(const std::wstring& description); //set syncDir = SYNC_DIR_NONE + fill conflict description
+ void setSyncDirConflict(const std::wstring& description); //set syncDir = SyncDirection::NONE + fill conflict description
bool isActive() const;
void setActive(bool active);
@@ -410,7 +410,7 @@ protected:
CompareFilesResult defaultCmpResult) :
cmpResult(defaultCmpResult),
selectedForSynchronization(true),
- syncDir_(static_cast<unsigned char>(SYNC_DIR_NONE)),
+ syncDir_(SyncDirection::NONE),
shortNameLeft_(shortNameLeft),
shortNameRight_(shortNameRight),
//shortNameRight_(shortNameRight == shortNameLeft ? shortNameLeft : shortNameRight), -> strangely doesn't seem to shrink peak memory consumption at all!
@@ -437,7 +437,7 @@ private:
bool selectedForSynchronization;
- unsigned char syncDir_; //use "char" instead of "SyncDirection" lacking C++11 custom size enum classes to optimize memory layout!
+ SyncDirection syncDir_; //1 byte: optimize memory layout!
std::unique_ptr<std::wstring> syncDirConflict; //non-empty if we have a conflict setting sync-direction
//get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!)
@@ -641,14 +641,14 @@ std::wstring FileSystemObject::getCatExtraDescription() const
inline
SyncDirection FileSystemObject::getSyncDir() const
{
- return static_cast<SyncDirection>(syncDir_);
+ return syncDir_;
}
inline
void FileSystemObject::setSyncDir(SyncDirection newDir)
{
- syncDir_ = static_cast<unsigned char>(newDir);
+ syncDir_ = newDir;
syncDirConflict.reset();
notifySyncCfgChanged();
@@ -658,7 +658,7 @@ void FileSystemObject::setSyncDir(SyncDirection newDir)
inline
void FileSystemObject::setSyncDirConflict(const std::wstring& description)
{
- syncDir_ = static_cast<unsigned char>(SYNC_DIR_NONE);
+ syncDir_ = SyncDirection::NONE;
syncDirConflict.reset(new std::wstring(description));
notifySyncCfgChanged();
@@ -751,7 +751,7 @@ void FileSystemObject::removeObject<LEFT_SIDE>()
shortNameLeft_.clear();
removeObjectL();
- setSyncDir(SYNC_DIR_NONE); //calls notifySyncCfgChanged()
+ setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged()
}
@@ -762,7 +762,7 @@ void FileSystemObject::removeObject<RIGHT_SIDE>()
shortNameRight_.clear();
removeObjectR();
- setSyncDir(SYNC_DIR_NONE); //calls notifySyncCfgChanged()
+ setSyncDir(SyncDirection::NONE); //calls notifySyncCfgChanged()
}
@@ -772,7 +772,7 @@ void FileSystemObject::setSynced(const Zstring& shortName)
assert(!isEmpty());
shortNameRight_ = shortNameLeft_ = shortName;
cmpResult = FILE_EQUAL;
- setSyncDir(SYNC_DIR_NONE);
+ setSyncDir(SyncDirection::NONE);
}
diff --git a/lib/ShadowCopy/Shadow_Server2003.vcxproj b/lib/ShadowCopy/Shadow_Server2003.vcxproj
index 520a4d6d..a9b8f740 100644
--- a/lib/ShadowCopy/Shadow_Server2003.vcxproj
+++ b/lib/ShadowCopy/Shadow_Server2003.vcxproj
@@ -87,7 +87,7 @@
</BuildLog>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -123,7 +123,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -157,7 +157,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
@@ -195,7 +195,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_2003;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
diff --git a/lib/ShadowCopy/Shadow_Windows7.vcxproj b/lib/ShadowCopy/Shadow_Windows7.vcxproj
index 1fe769d0..7c6f1f1d 100644
--- a/lib/ShadowCopy/Shadow_Windows7.vcxproj
+++ b/lib/ShadowCopy/Shadow_Windows7.vcxproj
@@ -87,7 +87,7 @@
</BuildLog>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -133,7 +133,7 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
@@ -157,7 +157,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
@@ -205,7 +205,7 @@
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..;C:\Program Files\C++\boost</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_WINDOWS7;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
diff --git a/lib/ShadowCopy/Shadow_XP.vcxproj b/lib/ShadowCopy/Shadow_XP.vcxproj
index 0d231f3d..0d54f2e4 100644
--- a/lib/ShadowCopy/Shadow_XP.vcxproj
+++ b/lib/ShadowCopy/Shadow_XP.vcxproj
@@ -86,7 +86,7 @@
</BuildLog>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -123,7 +123,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -158,7 +158,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
@@ -196,7 +196,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;SHADOWDLL_EXPORTS;USE_SHADOW_XP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
diff --git a/lib/Thumbnail/Thumbnail.vcxproj b/lib/Thumbnail/Thumbnail.vcxproj
index 87fb152b..1ec016be 100644
--- a/lib/Thumbnail/Thumbnail.vcxproj
+++ b/lib/Thumbnail/Thumbnail.vcxproj
@@ -94,7 +94,7 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
<SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
@@ -129,7 +129,7 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
<SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
@@ -161,7 +161,7 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
</ClCompile>
@@ -198,7 +198,7 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100;4996</DisableSpecificWarnings>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
</ClCompile>
diff --git a/lib/db_file.cpp b/lib/db_file.cpp
index 2f699e3a..ab7f9212 100644
--- a/lib/db_file.cpp
+++ b/lib/db_file.cpp
@@ -91,7 +91,7 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError, FileErrorD
{
try
{
- BinStreamIn streamIn = loadBinStream<BinaryStream>(filename); //throw FileError, ErrorNotExisting
+ BinStreamIn streamIn = loadBinStream<BinaryStream>(filename); //throw FileError
//read FreeFileSync file identifier
char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {};
@@ -118,10 +118,12 @@ StreamMapping loadStreams(const Zstring& filename) //throw FileError, FileErrorD
}
return output;
}
- catch (ErrorNotExisting&)
+ catch (FileError&)
{
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" +
- replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename)));
+ if (!somethingExists(filename)) //a benign(?) race condition with FileError
+ throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" +
+ replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtFileName(filename)));
+ throw;
}
catch (UnexpectedEndOfStreamError&)
{
diff --git a/lib/dir_exist_async.h b/lib/dir_exist_async.h
index dd77a36a..39ae6aff 100644
--- a/lib/dir_exist_async.h
+++ b/lib/dir_exist_async.h
@@ -47,7 +47,7 @@ std::set<Zstring, LessFilename> getExistingDirsUpdating(const std::set<Zstring,
const boost::system_time endTime = boost::get_system_time() + boost::posix_time::seconds(10); //10 sec should be enough even if Win32 waits much longer
auto itDirname = dirnames.begin();
- for (auto it = dirEx.begin(); it != dirEx.end(); ++it, ++itDirname)
+ for (auto it = dirEx.begin(); it != dirEx.end(); (void)++it, ++itDirname) //void: prevent ADL from dragging in boost's ,-overload: "MSVC warning C4913: user defined binary operator ',' exists but no overload could convert all operands"
{
procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", fmtFileName(*itDirname), false)); //may throw!
diff --git a/lib/dir_lock.cpp b/lib/dir_lock.cpp
index 60d83a67..d7e3ba56 100644
--- a/lib/dir_lock.cpp
+++ b/lib/dir_lock.cpp
@@ -68,7 +68,7 @@ public:
}
catch (const std::exception& e) //exceptions must be catched per thread
{
- wxSafeShowMessage(_("An exception occurred"), utfCvrtTo<wxString>(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads
+ wxSafeShowMessage(L"FreeFileSync - " + _("An exception occurred"), utfCvrtTo<wxString>(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads
}
}
@@ -123,7 +123,7 @@ private:
namespace
{
-UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExisting
+UInt64 getLockFileSize(const Zstring& filename) //throw FileError
{
#ifdef ZEN_WIN
WIN32_FIND_DATA fileInfo = {};
@@ -144,11 +144,7 @@ UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExist
const ErrorCode lastError = getLastError();
const std::wstring errorMsg = replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename));
const std::wstring errorDescr = formatSystemError(functionName, lastError);
-
- if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(errorMsg, errorDescr);
- else
- throw FileError(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
}
@@ -359,9 +355,9 @@ struct LockInformation //throw FileError
//wxGetFullHostName() is a performance killer for some users, so don't touch!
-LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, ErrorNotExisting
+LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError
{
- BinStreamIn streamIn = loadBinStream<BinaryStream>(lockfilename); //throw FileError, ErrorNotExisting
+ BinStreamIn streamIn = loadBinStream<BinaryStream>(lockfilename); //throw FileError
try
{
return LockInformation(streamIn); //throw UnexpectedEndOfStreamError
@@ -374,9 +370,9 @@ LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError,
inline
-std::string retrieveLockId(const Zstring& lockfilename) //throw FileError, ErrorNotExisting
+std::string retrieveLockId(const Zstring& lockfilename) //throw FileError
{
- return retrieveLockInfo(lockfilename).lockId;
+ return retrieveLockInfo(lockfilename).lockId; //throw FileError
}
@@ -408,6 +404,7 @@ ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileErro
const std::int64_t TICKS_PER_SEC = ticksPerSec(); //= 0 on error
+
void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError
{
const std::wstring infoMsg = replaceCpy(_("Waiting while directory is locked (%x)..."), L"%x", fmtFileName(lockfilename));
@@ -422,7 +419,7 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
std::string originalLockId; //empty if it cannot be retrieved
try
{
- const LockInformation& lockInfo = retrieveLockInfo(lockfilename); //throw FileError, ErrorNotExisting
+ const LockInformation& lockInfo = retrieveLockInfo(lockfilename); //throw FileError
originalLockId = lockInfo.lockId;
switch (getProcessStatus(lockInfo)) //throw FileError
{
@@ -443,7 +440,7 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
while (true)
{
const TickVal now = getTicks();
- const UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError, ErrorNotExisting
+ const UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError
if (TICKS_PER_SEC <= 0 || !lastLifeSign.isValid() || !now.isValid())
throw FileError(L"System Timer failed!"); //no i18n: "should" never throw ;)
@@ -462,10 +459,10 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
//now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock!
if (!originalLockId.empty())
- if (retrieveLockId(lockfilename) != originalLockId) //throw FileError, ErrorNotExisting -> since originalLockId is filled, we are not expecting errors!
+ if (retrieveLockId(lockfilename) != originalLockId) //throw FileError -> since originalLockId is filled, we are not expecting errors!
return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over...
- if (::getLockFileSize(lockfilename) != fileSizeOld) //throw FileError, ErrorNotExisting
+ if (::getLockFileSize(lockfilename) != fileSizeOld) //throw FileError
continue; //late life sign
removeFile(lockfilename); //throw FileError
@@ -494,9 +491,11 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr
}
}
}
- catch (const ErrorNotExisting&)
+ catch (FileError&)
{
- return; //what we are waiting for...
+ if (!somethingExists(lockfilename)) //a benign(?) race condition with FileError
+ return; //what we are waiting for...
+ throw;
}
}
@@ -582,7 +581,7 @@ public:
~SharedDirLock()
{
threadObj.interrupt(); //thread lifetime is subset of this instances's life
- threadObj.join(); //we assume precondition "threadObj.joinable()"!!!
+ threadObj.join(); //we assert precondition "threadObj.joinable()"!!!
::releaseLock(lockfilename_); //throw ()
}
@@ -618,7 +617,7 @@ public:
try //check based on lock GUID, deadlock prevention: "lockfilename" may be an alternative name for a lock already owned by this process
{
- const std::string lockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting
+ const std::string lockId = retrieveLockId(lockfilename); //throw FileError
if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(lockId)) //returns null-lock if not found
{
fileToGuid[lockfilename] = lockId; //found an alias for one of our active locks
@@ -629,7 +628,7 @@ public:
//lock not owned by us => create a new one
auto newLock = std::make_shared<SharedDirLock>(lockfilename, callback); //throw FileError
- const std::string& newLockGuid = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting
+ const std::string& newLockGuid = retrieveLockId(lockfilename); //throw FileError
//update registry
fileToGuid[lockfilename] = newLockGuid; //throw()
diff --git a/lib/generate_logfile.h b/lib/generate_logfile.h
index 31f7bd43..ff97b63a 100644
--- a/lib/generate_logfile.h
+++ b/lib/generate_logfile.h
@@ -147,9 +147,9 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError
Utf8String oldStream;
try
{
- oldStream = loadBinStream<Utf8String>(filename); //throw FileError, ErrorNotExisting
+ oldStream = loadBinStream<Utf8String>(filename); //throw FileError
}
- catch (const ErrorNotExisting&) {}
+ catch (FileError&) {}
if (!oldStream.empty())
{
diff --git a/lib/icon_buffer.cpp b/lib/icon_buffer.cpp
index cf916174..b0874d83 100644
--- a/lib/icon_buffer.cpp
+++ b/lib/icon_buffer.cpp
@@ -426,7 +426,7 @@ public:
{
assert(boost::this_thread::get_id() == mainThreadId );
{
- boost::unique_lock<boost::mutex> dummy(lockFiles);
+ boost::lock_guard<boost::mutex> dummy(lockFiles);
filesToLoad = newLoad;
}
conditionNewFiles.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
@@ -437,7 +437,7 @@ public:
{
assert(boost::this_thread::get_id() == mainThreadId );
{
- boost::unique_lock<boost::mutex> dummy(lockFiles);
+ boost::lock_guard<boost::mutex> dummy(lockFiles);
filesToLoad.push_back(newEntry); //set as next item to retrieve
}
conditionNewFiles.notify_all();
diff --git a/lib/parallel_scan.cpp b/lib/parallel_scan.cpp
index df8ff095..2bac5690 100644
--- a/lib/parallel_scan.cpp
+++ b/lib/parallel_scan.cpp
@@ -180,7 +180,7 @@ public:
errorMsg.clear();
errorResponse.reset();
- dummy.unlock(); //optimization for condition_variable::notify_one()
+ dummy.unlock(); //optimization for condition_variable::notify_all()
conditionCanReportError.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
return rv;
@@ -194,7 +194,7 @@ public:
FillBufferCallback::HandleError rv = callback.reportError(copyStringTo<std::wstring>(errorMsg)); //throw!
errorResponse = make_unique<FillBufferCallback::HandleError>(rv);
- dummy.unlock(); //optimization for condition_variable::notify_one()
+ dummy.unlock(); //optimization for condition_variable::notify_all()
conditionGotResponse.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
}
}
@@ -236,7 +236,7 @@ public:
std::wstring statusText = copyStringTo<std::wstring>(textScanning);
const long activeCount = activeWorker;
if (activeCount >= 2)
- statusText += L" " + replaceCpy(_P("[1 Thread]", "[%x Threads]", activeCount), L"%x", numberTo<std::wstring>(activeCount));
+ statusText += L" [" + replaceCpy(_P("1 thread", "%x threads", activeCount), L"%x", numberTo<std::wstring>(activeCount)) + L"]";
statusText += L" " + fmtFileName(filename);
return statusText;
@@ -273,8 +273,8 @@ private:
boost::detail::atomic_count itemsScanned;
boost::detail::atomic_count activeWorker;
};
-//-------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------
struct TraverserShared
{
diff --git a/lib/parse_lng.h b/lib/parse_lng.h
index 8cd8e943..be47b66b 100644
--- a/lib/parse_lng.h
+++ b/lib/parse_lng.h
@@ -294,7 +294,7 @@ private:
static void normalize(std::string& text)
{
- zen::trim(text); //remmove whitespace from end
+ zen::trim(text); //remove whitespace from both ends
//Delimiter:
//----------
@@ -378,8 +378,11 @@ private:
if (token().type == Token::TK_PLURAL_BEGIN)
return parsePlural(pluralOut, pluralInfo);
+ if (token().type != Token::TK_TEXT)
+ throw ParsingError(L"Source text empty", scn.posRow(), scn.posCol());
std::string original = tk.text;
- consumeToken(Token::TK_TEXT);
+ nextToken();
+
consumeToken(Token::TK_SRC_END);
consumeToken(Token::TK_TRG_BEGIN);
diff --git a/lib/perf_check.cpp b/lib/perf_check.cpp
index 33361201..0f5506b3 100644
--- a/lib/perf_check.cpp
+++ b/lib/perf_check.cpp
@@ -16,10 +16,10 @@ using namespace zen;
PerfCheck::PerfCheck(unsigned int windowSizeRemainingTime,
- unsigned int windowSizeBytesPerSecond) :
+ unsigned int windowSizeSpeed) :
windowSizeRemTime(windowSizeRemainingTime),
- windowSizeBPS(windowSizeBytesPerSecond),
- windowMax(std::max(windowSizeRemainingTime, windowSizeBytesPerSecond)) {}
+ windowSizeSpeed_(windowSizeSpeed),
+ windowMax(std::max(windowSizeRemainingTime, windowSizeSpeed)) {}
PerfCheck::~PerfCheck()
@@ -43,9 +43,9 @@ PerfCheck::~PerfCheck()
}
-void PerfCheck::addSample(int objectsCurrent, double dataCurrent, long timeMs)
+void PerfCheck::addSample(int itemsCurrent, double dataCurrent, long timeMs)
{
- samples.insert(samples.end(), std::make_pair(timeMs, Record(objectsCurrent, dataCurrent))); //use fact that time is monotonously ascending
+ samples.insert(samples.end(), std::make_pair(timeMs, Record(itemsCurrent, dataCurrent))); //use fact that time is monotonously ascending
//remove all records earlier than "now - windowMax"
const long newBegin = timeMs - windowMax;
@@ -55,20 +55,32 @@ void PerfCheck::addSample(int objectsCurrent, double dataCurrent, long timeMs)
}
-std::wstring PerfCheck::getRemainingTime(double dataRemaining) const
+inline
+std::pair<const std::multimap<long, PerfCheck::Record>::value_type*, const std::multimap<long, PerfCheck::Record>::value_type*> PerfCheck::getBlockFromEnd(long windowSize) const
{
if (!samples.empty())
{
- const auto& recordBack = *samples.rbegin();
+ auto itBack = samples.rbegin();
//find start of records "window"
- auto itFront = samples.upper_bound(recordBack.first - windowSizeRemTime);
+ auto itFront = samples.upper_bound(itBack->first - windowSize);
if (itFront != samples.begin())
--itFront; //one point before window begin in order to handle "measurement holes"
+ return std::make_pair(&*itFront, &*itBack);
+ }
+ return std::make_pair(nullptr, nullptr);
+}
- const auto& recordFront = *itFront;
+
+zen::Opt<std::wstring> PerfCheck::getRemainingTime(double dataRemaining) const
+{
+ auto blk = getBlockFromEnd(windowSizeRemTime);
+ if (blk.first && blk.second)
+ {
+ const auto& itemFront = *blk.first;
+ const auto& itemBack = *blk.second;
//-----------------------------------------------------------------------------------------------
- const long timeDelta = recordBack.first - recordFront.first;
- const double dataDelta = recordBack.second.data_ - recordFront.second.data_;
+ const long timeDelta = itemBack.first - itemFront.first;
+ const double dataDelta = itemBack.second.data_ - itemFront.second.data_;
//objects model logical operations *NOT* disk accesses, so we better play safe and use "bytes" only!
//http://sourceforge.net/p/freefilesync/feature-requests/197/
@@ -76,29 +88,43 @@ std::wstring PerfCheck::getRemainingTime(double dataRemaining) const
if (!numeric::isNull(dataDelta)) //sign(dataRemaining) != sign(dataDelta) usually an error, so show it!
return remainingTimeToString(dataRemaining * timeDelta / (1000.0 * dataDelta));
}
- return L"-"; //fallback
+ return NoValue();
}
-std::wstring PerfCheck::getBytesPerSecond() const
+zen::Opt<std::wstring> PerfCheck::getBytesPerSecond() const
{
- if (!samples.empty())
+ auto blk = getBlockFromEnd(windowSizeSpeed_);
+ if (blk.first && blk.second)
{
- const auto& recordBack = *samples.rbegin();
- //find start of records "window"
- auto itFront = samples.upper_bound(recordBack.first - windowSizeBPS);
- if (itFront != samples.begin())
- --itFront; //one point before window begin in order to handle "measurement holes"
+ const auto& itemFront = *blk.first;
+ const auto& itemBack = *blk.second;
+ //-----------------------------------------------------------------------------------------------
+ const long timeDelta = itemBack.first - itemFront.first;
+ const double dataDelta = itemBack.second.data_ - itemFront.second.data_;
+
+ if (timeDelta != 0/* && dataDelta > 0*/)
+ return filesizeToShortString(zen::Int64(dataDelta * 1000.0 / timeDelta)) + _("/sec");
+ }
+ return NoValue();
+}
+
- const auto& recordFront = *itFront;
+zen::Opt<std::wstring> PerfCheck::getItemsPerSecond() const
+{
+ auto blk = getBlockFromEnd(windowSizeSpeed_);
+ if (blk.first && blk.second)
+ {
+ const auto& itemFront = *blk.first;
+ const auto& itemBack = *blk.second;
//-----------------------------------------------------------------------------------------------
- const long timeDelta = recordBack.first - recordFront.first;
- const double dataDelta = recordBack.second.data_ - recordFront.second.data_;
+ const long timeDelta = itemBack.first - itemFront.first;
+ const int itemsDelta = itemBack.second.itemCount_ - itemFront.second.itemCount_;
- if (timeDelta != 0 && dataDelta > 0)
- return filesizeToShortString(zen::Int64(dataDelta * 1000 / timeDelta)) + _("/sec");
+ if (timeDelta != 0)
+ return replaceCpy(_("%x items"), L"%x", formatThreeDigitPrecision(itemsDelta * 1000.0 / timeDelta)) + _("/sec");
}
- return L"-"; //fallback
+ return NoValue();
}
diff --git a/lib/perf_check.h b/lib/perf_check.h
index 3e04b778..9d82be57 100644
--- a/lib/perf_check.h
+++ b/lib/perf_check.h
@@ -9,31 +9,37 @@
#include <map>
#include <string>
+#include <zen/optional.h>
class PerfCheck
{
public:
- PerfCheck(unsigned int windowSizeRemainingTime, //unit: [ms]
- unsigned int windowSizeBytesPerSecond); //
+ PerfCheck(unsigned int windowSizeRemainingTime, //unit: [ms]
+ unsigned int windowSizeSpeed); //
~PerfCheck();
- void addSample(int objectsCurrent, double dataCurrent, long timeMs); //timeMs must be ascending!
+ void addSample(int itemsCurrent, double dataCurrent, long timeMs); //timeMs must be ascending!
- std::wstring getRemainingTime(double dataRemaining) const;
- std::wstring getBytesPerSecond() const; //for window
+ zen::Opt<std::wstring> getRemainingTime(double dataRemaining) const;
+ zen::Opt<std::wstring> getBytesPerSecond() const; //for window
+ zen::Opt<std::wstring> getItemsPerSecond() const; //for window
private:
- const long windowSizeRemTime; //unit: [ms]
- const long windowSizeBPS; //
- const long windowMax;
-
struct Record
{
- Record(int objCount, double data) : objCount_(objCount), data_(data) {}
- int objCount_;
+ Record(int itemCount, double data) : itemCount_(itemCount), data_(data) {}
+ int itemCount_;
double data_; //unit: [bytes]
};
- std::multimap<long, Record> samples; //time, unit: [ms]
+
+ std::pair<const std::multimap<long, Record>::value_type*,
+ const std::multimap<long, Record>::value_type*> getBlockFromEnd(long windowSize) const;
+
+ const long windowSizeRemTime; //unit: [ms]
+ const long windowSizeSpeed_; //
+ const long windowMax;
+
+ std::map<long, Record> samples; //time, unit: [ms]
};
#endif // STATISTICS_H_INCLUDED
diff --git a/lib/process_xml.cpp b/lib/process_xml.cpp
index 2422b2ef..49e4c711 100644
--- a/lib/process_xml.cpp
+++ b/lib/process_xml.cpp
@@ -99,6 +99,7 @@ void xmlAccess::OptionalDialogs::resetDialogs()
warningDirectoryLockFailed = true;
popupOnConfigChange = true;
confirmSyncStart = true;
+ confirmExternalCommandMassInvoke = true;
}
@@ -157,36 +158,6 @@ xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatchPreservingExistingBatch(co
}
-xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<Zstring>& filenames) //throw()
-{
- bool guiCfgExists = false;
- bool batchCfgExists = false;
-
- for (auto it = filenames.begin(); it != filenames.end(); ++it)
- {
- switch (xmlAccess::getXmlType(*it)) //throw()
- {
- case XML_TYPE_GUI:
- guiCfgExists = true;
- break;
-
- case XML_TYPE_BATCH:
- batchCfgExists = true;
- break;
-
- case XML_TYPE_GLOBAL:
- case XML_TYPE_OTHER:
- return MERGE_OTHER;
- }
- }
-
- if (guiCfgExists)
- return batchCfgExists ? MERGE_GUI_BATCH : MERGE_GUI;
- else
- return batchCfgExists ? MERGE_BATCH : MERGE_OTHER;
-}
-
-
namespace
{
std::vector<Zstring> splitFilterByLines(const Zstring& filterPhrase)
@@ -243,13 +214,13 @@ void writeText(const SyncDirection& value, std::string& output)
{
switch (value)
{
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
output = "left";
break;
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
output = "right";
break;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
output = "none";
break;
}
@@ -261,11 +232,11 @@ bool readText(const std::string& input, SyncDirection& value)
std::string tmp = input;
zen::trim(tmp);
if (tmp == "left")
- value = SYNC_DIR_LEFT;
+ value = SyncDirection::LEFT;
else if (tmp == "right")
- value = SYNC_DIR_RIGHT;
+ value = SyncDirection::RIGHT;
else if (tmp == "none")
- value = SYNC_DIR_NONE;
+ value = SyncDirection::NONE;
else
return false;
return true;
@@ -1037,6 +1008,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
+ inOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke);
//gui specific global settings (optional)
XmlIn inGui = in["Gui"];
@@ -1415,6 +1387,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warningDirectoryLockFailed);
outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange);
outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart);
+ outOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke);
//gui specific global settings (optional)
XmlOut outGui = out["Gui"];
diff --git a/lib/process_xml.h b/lib/process_xml.h
index 95fa644d..b189e51f 100644
--- a/lib/process_xml.h
+++ b/lib/process_xml.h
@@ -103,6 +103,7 @@ struct OptionalDialogs
bool warningDirectoryLockFailed;
bool popupOnConfigChange;
bool confirmSyncStart;
+ bool confirmExternalCommandMassInvoke;
};
@@ -278,15 +279,6 @@ void writeConfig(const XmlBatchConfig& config, const Zstring& filename); //th
void writeConfig(const XmlGlobalSettings& config); //
//convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure:
-enum MergeType
-{
- MERGE_GUI, //pure gui config files
- MERGE_BATCH, // " batch "
- MERGE_GUI_BATCH, //gui and batch files
- MERGE_OTHER
-};
-MergeType getMergeType(const std::vector<Zstring>& filenames); //noexcept
-
void readAnyConfig(const std::vector<Zstring>& filenames, XmlGuiConfig& config); //throw FfsXmlError
//config conversion utilities
diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp
index 035e1d77..daa99d2a 100644
--- a/lib/resolve_path.cpp
+++ b/lib/resolve_path.cpp
@@ -620,20 +620,28 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio
nullptr, // __in LPCTSTR lpPassword,
nullptr, // __in LPCTSTR lpUsername,
0); //__in DWORD dwFlags
+ //53L ERROR_BAD_NETPATH The network path was not found.
+ //86L ERROR_INVALID_PASSWORD
+ //1219L ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
+ //1326L ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password.
+ //1236L ERROR_CONNECTION_ABORTED
if (somethingExists(trgRes.lpRemoteName)) //blocks!
return; //success: connection usable! -> don't care about "rv"
if (rv == ERROR_BAD_NETPATH || //Windows 7
- rv == ERROR_BAD_NET_NAME) //XP
+ rv == ERROR_BAD_NET_NAME|| //XP
+ rv == ERROR_CONNECTION_ABORTED) //failed to connect to a network that existed not too long ago; will later return ERROR_BAD_NETPATH
return; //no need to show a prompt for an unreachable network device
//2. if first attempt failed, we need to *force* prompt by using CONNECT_PROMPT
if (allowUserInteraction)
{
//avoid problem II.)
- DWORD rv2= WNetCancelConnection2(trgRes.lpRemoteName, //_In_ LPCTSTR lpName,
- 0, //_In_ DWORD dwFlags,
- true); //_In_ BOOL fForce
+ DWORD rv2= ::WNetCancelConnection2(trgRes.lpRemoteName, //_In_ LPCTSTR lpName,
+ 0, //_In_ DWORD dwFlags,
+ true); //_In_ BOOL fForce
+ //2250L ERROR_NOT_CONNECTED
+
//enforce login prompt
DWORD rv3 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
nullptr, // __in LPCTSTR lpPassword,
@@ -641,11 +649,6 @@ void zen::loginNetworkShare(const Zstring& dirnameOrig, bool allowUserInteractio
CONNECT_INTERACTIVE | CONNECT_PROMPT); //__in DWORD dwFlags
(void)rv2;
(void)rv3;
- //Sample error codes:
- //53L ERROR_BAD_NETPATH The network path was not found.
- //86L ERROR_INVALID_PASSWORD
- //1219L ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
- //1326L ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password.
}
};
diff --git a/lib/shadow.cpp b/lib/shadow.cpp
index 1161646e..71372270 100644
--- a/lib/shadow.cpp
+++ b/lib/shadow.cpp
@@ -111,14 +111,11 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function
const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error!
//input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found.
- const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider for example \\?\ prefix!
+ const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider \\?\ prefix!
if (pos == Zstring::npos)
- {
- std::wstring msg = _("Volume name %x not part of file name %y.");
- replace(msg, L"%x", fmtFileName(volumeNamePf), false);
- replace(msg, L"%y", fmtFileName(filenameFinal), false);
- throw FileError(msg);
- }
+ throw FileError(replaceCpy(replaceCpy(_("Volume name %x is not part of file path %y."),
+ L"%x", fmtFileName(volumeNamePf)),
+ L"%y", fmtFileName(filenameFinal)));
//get or create instance of shadow volume
VolNameShadowMap::const_iterator it = shadowVol.find(volumeNamePf);
diff --git a/lib/status_handler.cpp b/lib/status_handler.cpp
index c24c6f50..5fb80161 100644
--- a/lib/status_handler.cpp
+++ b/lib/status_handler.cpp
@@ -5,21 +5,12 @@
// **************************************************************************
#include "status_handler.h"
-#include <wx/app.h>
+//#include <wx/app.h>
#include <zen/tick_count.h>
using namespace zen;
-void zen::updateUiNow()
-{
- //process UI events and prevent application from "not responding" -> NO performance issue!
- wxTheApp->Yield();
-
- // while (wxTheApp->Pending())
- // wxTheApp->Dispatch();
-}
-
namespace
{
const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
diff --git a/lib/status_handler.h b/lib/status_handler.h
index ed496824..c19935b1 100644
--- a/lib/status_handler.h
+++ b/lib/status_handler.h
@@ -16,13 +16,12 @@
namespace zen
{
bool updateUiIsAllowed(); //test if a specific amount of time is over
-void updateUiNow(); //do the updating
/*
Updating GUI is fast!
time per single call to ProcessCallback::forceUiRefresh()
- - Comparison 25 µs
- - Synchronization 0.6 ms (despite complex graph control!)
+ - Comparison 0.025 ms
+ - Synchronization 0.74 ms (despite complex graph control!)
*/
//gui may want to abort process
@@ -72,11 +71,13 @@ protected:
virtual void requestUiRefresh()
{
- if (updateUiIsAllowed()) //test if specific time span between ui updates is over
+ if (abortRequested) //triggered by requestAbortion()
+ {
+ forceUiRefresh();
+ abortThisProcess();
+ }
+ else if (updateUiIsAllowed()) //test if specific time span between ui updates is over
forceUiRefresh();
-
- if (abortRequested) //check *after* GUI update, to have up-to-date screen
- abortThisProcess(); //triggered by requestAbortion()
}
virtual void reportStatus(const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw AbortThisProcess */ }
diff --git a/lib/versioning.cpp b/lib/versioning.cpp
index 1bf5a65e..a6458196 100644
--- a/lib/versioning.cpp
+++ b/lib/versioning.cpp
@@ -129,15 +129,14 @@ void moveObject(const Zstring& sourceFile, //throw FileError
{
assert(!dirExists(sourceFile) || symlinkExists(sourceFile)); //we process files and symlinks only
- auto removeTarget = [&]()
+ auto removeTarget = [&]
{
//remove target object
if (fileExists(targetFile)) //file or symlink
removeFile(targetFile); //throw FileError
else if (dirExists(targetFile)) //directory or symlink
- removeDirectory(targetFile); //throw FileError
- //we do not expect targetFile to be a directory in general => no callback required
- else assert(false);
+ removeDirectory(targetFile); //throw FileError; we do not expect targetFile to be a directory in general => no callback required
+ //else assert(false); -> may simply not exist if ErrorDifferentVolume!
};
//first try to move directly without copying
diff --git a/process_callback.h b/process_callback.h
index 036ad79a..fcf0384e 100644
--- a/process_callback.h
+++ b/process_callback.h
@@ -51,7 +51,7 @@ struct ProcessCallback
//opportunity to abort must be implemented in a frequently executed method like requestUiRefresh()
virtual void requestUiRefresh() = 0; //throw ?
- virtual void forceUiRefresh () = 0; //throw ? - called before starting long running task which doesn't update regularly
+ virtual void forceUiRefresh () = 0; //throw ? - called before starting long running tasks which don't update regularly
//called periodically after data was processed: expected(!) to request GUI update
virtual void reportStatus(const std::wstring& text) = 0; //UI info only, should not be logged!
@@ -71,4 +71,4 @@ struct ProcessCallback
virtual void reportFatalError(const std::wstring& errorMessage) = 0; //non-recoverable error situation
};
-#endif //PROC_HEADER_48257827842345454545 \ No newline at end of file
+#endif //PROC_HEADER_48257827842345454545
diff --git a/structures.cpp b/structures.cpp
index 1bcfd580..3bc2c181 100644
--- a/structures.cpp
+++ b/structures.cpp
@@ -55,21 +55,21 @@ DirectionSet zen::extractDirections(const DirectionConfig& cfg)
throw std::logic_error("there are no predefined directions for automatic mode!");
case DirectionConfig::MIRROR:
- output.exLeftSideOnly = SYNC_DIR_RIGHT;
- output.exRightSideOnly = SYNC_DIR_RIGHT;
- output.leftNewer = SYNC_DIR_RIGHT;
- output.rightNewer = SYNC_DIR_RIGHT;
- output.different = SYNC_DIR_RIGHT;
- output.conflict = SYNC_DIR_RIGHT;
+ output.exLeftSideOnly = SyncDirection::RIGHT;
+ output.exRightSideOnly = SyncDirection::RIGHT;
+ output.leftNewer = SyncDirection::RIGHT;
+ output.rightNewer = SyncDirection::RIGHT;
+ output.different = SyncDirection::RIGHT;
+ output.conflict = SyncDirection::RIGHT;
break;
case DirectionConfig::UPDATE:
- output.exLeftSideOnly = SYNC_DIR_RIGHT;
- output.exRightSideOnly = SYNC_DIR_NONE;
- output.leftNewer = SYNC_DIR_RIGHT;
- output.rightNewer = SYNC_DIR_NONE;
- output.different = SYNC_DIR_RIGHT;
- output.conflict = SYNC_DIR_NONE;
+ output.exLeftSideOnly = SyncDirection::RIGHT;
+ output.exRightSideOnly = SyncDirection::NONE;
+ output.leftNewer = SyncDirection::RIGHT;
+ output.rightNewer = SyncDirection::NONE;
+ output.different = SyncDirection::RIGHT;
+ output.conflict = SyncDirection::NONE;
break;
case DirectionConfig::CUSTOM:
@@ -83,12 +83,12 @@ DirectionSet zen::extractDirections(const DirectionConfig& cfg)
DirectionSet zen::getTwoWaySet()
{
DirectionSet output;
- output.exLeftSideOnly = SYNC_DIR_RIGHT;
- output.exRightSideOnly = SYNC_DIR_LEFT;
- output.leftNewer = SYNC_DIR_RIGHT;
- output.rightNewer = SYNC_DIR_LEFT;
- output.different = SYNC_DIR_NONE;
- output.conflict = SYNC_DIR_NONE;
+ output.exLeftSideOnly = SyncDirection::RIGHT;
+ output.exRightSideOnly = SyncDirection::LEFT;
+ output.leftNewer = SyncDirection::RIGHT;
+ output.rightNewer = SyncDirection::LEFT;
+ output.different = SyncDirection::NONE;
+ output.conflict = SyncDirection::NONE;
return output;
}
diff --git a/structures.h b/structures.h
index 5ffb5620..1d3f5d4c 100644
--- a/structures.h
+++ b/structures.h
@@ -31,11 +31,11 @@ enum SymLinkHandling
};
-enum SyncDirection
+enum class SyncDirection : unsigned char //we need to save space for use in FileSystemObject!
{
- SYNC_DIR_LEFT = 0,
- SYNC_DIR_RIGHT,
- SYNC_DIR_NONE
+ LEFT = 0,
+ RIGHT,
+ NONE
};
@@ -104,12 +104,12 @@ std::wstring getSymbol (SyncOperation op); //method used for exporting .csv
struct DirectionSet
{
DirectionSet() :
- exLeftSideOnly (SYNC_DIR_RIGHT),
- exRightSideOnly(SYNC_DIR_LEFT),
- leftNewer (SYNC_DIR_RIGHT),
- rightNewer (SYNC_DIR_LEFT),
- different (SYNC_DIR_NONE),
- conflict (SYNC_DIR_NONE) {}
+ exLeftSideOnly (SyncDirection::RIGHT),
+ exRightSideOnly(SyncDirection::LEFT),
+ leftNewer (SyncDirection::RIGHT),
+ rightNewer (SyncDirection::LEFT),
+ different (SyncDirection::NONE),
+ conflict (SyncDirection::NONE) {}
SyncDirection exLeftSideOnly;
SyncDirection exRightSideOnly;
diff --git a/synchronization.cpp b/synchronization.cpp
index 783f1e7f..a0f19d27 100644
--- a/synchronization.cpp
+++ b/synchronization.cpp
@@ -316,9 +316,8 @@ public:
try { tryCleanup(false); }
catch (...) {} //always (try to) clean up, even if synchronization is aborted!
/*
- do not allow user callback:
- - make sure this stays non-blocking!
- - avoid throwing user abort exception again, leading to incomplete clean-up!
+ may block heavily, but still do not allow user callback:
+ -> avoid throwing user cancel exception again, leading to incomplete clean-up!
*/
}
@@ -364,8 +363,6 @@ private:
std::wstring txtRemovingFile;
std::wstring txtRemovingSymlink;
std::wstring txtRemovingDirectory;
-
- bool cleanedUp;
};
@@ -380,8 +377,7 @@ DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow!
versioningDir_(versioningDir),
versioningStyle_(versioningStyle),
timeStamp_(timeStamp),
- deletionPolicy_(handleDel),
- cleanedUp(false)
+ deletionPolicy_(handleDel)
{
switch (deletionPolicy_)
{
@@ -480,42 +476,42 @@ Zstring DeletionHandling::getOrCreateRecyclerTempDirPf() //throw FileError
void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError
{
- if (!cleanedUp)
+ switch (deletionPolicy_)
{
- switch (deletionPolicy_)
- {
- case DELETE_PERMANENTLY:
- break;
+ case DELETE_PERMANENTLY:
+ break;
- case DELETE_TO_RECYCLER:
+ case DELETE_TO_RECYCLER:
#ifdef ZEN_WIN
- if (!recyclerTmpDir.empty())
- {
- //move content of temporary directory to recycle bin in a single call
- CallbackMassRecycling cbmr(procCallback_);
- recycleOrDelete(toBeRecycled, allowUserCallback ? &cbmr : nullptr); //throw FileError
+ if (!toBeRecycled.empty())
+ {
+ //move content of temporary directory to recycle bin in a single call
+ CallbackMassRecycling cbmr(procCallback_);
+ recycleOrDelete(toBeRecycled, allowUserCallback ? &cbmr : nullptr); //throw FileError
+ toBeRecycled.clear();
+ }
- //clean up temp directory itself (should contain remnant empty directories only)
- removeDirectory(recyclerTmpDir); //throw FileError
- }
+ //clean up temp directory itself (should contain remnant empty directories only)
+ if (!recyclerTmpDir.empty())
+ {
+ removeDirectory(recyclerTmpDir); //throw FileError
+ recyclerTmpDir.clear();
+ }
#endif
- break;
-
- case DELETE_TO_VERSIONING:
- //if (versioner.get())
- //{
- // if (allowUserCallback)
- // {
- // procCallback_.reportStatus(_("Removing old versions...")); //throw ?
- // versioner->limitVersions([&] { procCallback_.requestUiRefresh(); /*throw ? */ }); //throw FileError
- // }
- // else
- // versioner->limitVersions([] {}); //throw FileError
- //}
- break;
- }
+ break;
- cleanedUp = true;
+ case DELETE_TO_VERSIONING:
+ //if (versioner.get())
+ //{
+ // if (allowUserCallback)
+ // {
+ // procCallback_.reportStatus(_("Removing old versions...")); //throw ?
+ // versioner->limitVersions([&] { procCallback_.requestUiRefresh(); /*throw ? */ }); //throw FileError
+ // }
+ // else
+ // versioner->limitVersions([] {}); //throw FileError
+ //}
+ break;
}
}
@@ -856,9 +852,8 @@ private:
//[...]
//recurse into sub-dirs
- std::for_each(hierObj.refSubDirs().begin(), hierObj.refSubDirs().end(), [&](const HierarchyObject& subDir) { this->recurse(subDir); });
- //for (auto& subDir : hierObj.refSubDirs())
- // recurse(subDir);
+ for (auto& subDir : hierObj.refSubDirs())
+ recurse(subDir);
}
Int64 spaceNeededLeft;
@@ -1063,7 +1058,7 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj,
sourceObj.removeObject<side>(); //remove only *after* evaluating "sourceObj, side"!
//prepare move in second pass
- tempFile.setSyncDir(side == LEFT_SIDE ? SYNC_DIR_LEFT : SYNC_DIR_RIGHT);
+ tempFile.setSyncDir(side == LEFT_SIDE ? SyncDirection::LEFT : SyncDirection::RIGHT);
targetObj.setMoveRef(tempFile .getId());
tempFile .setMoveRef(targetObj.getId());
@@ -1146,7 +1141,8 @@ void SynchronizeFolderPair::manageFileMove(FilePair& sourceObj,
return prepare2StepMove<side>(sourceObj, targetObj); //throw FileError
//finally start move! this should work now:
- synchronizeFile(sourceObj); //throw FileError
+ synchronizeFile(targetObj); //throw FileError
+ //SynchronizeFolderPair::synchronizeFileInt() is *not* expecting SO_MOVE_LEFT_SOURCE/SO_MOVE_RIGHT_SOURCE => start move from targetObj, not sourceObj!
}
//else: sourceObj will not be deleted, and is not standing in the way => delay to second pass
//note: this case may include new "move sources" from two-step sub-routine!!!
@@ -1504,6 +1500,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation
procCallback_.updateProcessedData(1, 0);
}
+ else (assert(false));
break;
case SO_OVERWRITE_LEFT:
@@ -1532,7 +1529,10 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& fileObj, SyncOperation
//fileObj.removeObject<sideTrg>(); -> doesn't make sense for isFollowedSymlink(); "fileObj, sideTrg" evaluated below!
- reportStatus(txtOverwritingFile, targetFile); //restore status text copy file
+ //if fail-safe file copy is active, then the next operation will be a simple "rename"
+ //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated!
+ if (!transactionalFileCopy_)
+ reportStatus(txtOverwritingFile, targetFile); //restore status text copy file
}); //throw FileError
//update FilePair
@@ -1668,12 +1668,14 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& linkObj, SyncOperati
case SO_OVERWRITE_RIGHT:
reportInfo(txtOverwritingLink, linkObj.getFullName<sideTrg>());
- reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), linkObj.getFullName<sideTrg>());
+ //reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), linkObj.getFullName<sideTrg>());
getDelHandling<sideTrg>().removeLinkUpdating(linkObj.getFullName<sideTrg>(), linkObj.getObjRelativeName(), 0, [] {}); //throw FileError
//linkObj.removeObject<sideTrg>(); -> "linkObj, sideTrg" evaluated below!
- reportStatus(txtOverwritingLink, linkObj.getFullName<sideTrg>()); //restore status text
+ //=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated:
+
+ //reportStatus(txtOverwritingLink, linkObj.getFullName<sideTrg>()); //restore status text
zen::copySymlink(linkObj.getFullName<sideSrc>(),
linkObj.getBaseDirPf<sideTrg>() + linkObj.getRelativeName<sideSrc>(), //respect differences in case of source object
copyFilePermissions_); //throw FileError
@@ -2172,14 +2174,12 @@ void zen::synchronize(const TimeComp& timeStamp,
//check if user accidentally selected wrong directories for sync
if (!significantDiff.empty())
{
- std::wstring msg = _("Significant difference detected:");
+ std::wstring msg = _("The following folders are significantly different. Make sure you are matching the correct folders for synchronization.");
for (auto it = significantDiff.begin(); it != significantDiff.end(); ++it)
msg += std::wstring(L"\n\n") +
it->first + L" <-> " + L"\n" +
it->second;
- msg += L"\n\n";
- msg += _("More than 50% of the total number of files will be copied or deleted.");
callback.reportWarning(msg, warnings.warningSignificantDifference);
}
@@ -2245,12 +2245,11 @@ void zen::synchronize(const TimeComp& timeStamp,
//loop through all directory pairs
for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
{
- //exclude some pathological case (leftdir, rightdir are empty)
+ //exclude pathological cases (e.g. leftdir, rightdir are empty)
if (EqualFilename()(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>()))
continue;
//------------------------------------------------------------------------------------------
- //always report folder pairs for log file, even if there is no work to do
callback.reportInfo(_("Synchronizing folder pair:") + L"\n" +
L" " + j->getBaseDirPf<LEFT_SIDE >() + L"\n" +
L" " + j->getBaseDirPf<RIGHT_SIDE>());
diff --git a/ui/Taskbar_Seven/Taskbar_Seven.vcxproj b/ui/Taskbar_Seven/Taskbar_Seven.vcxproj
index 8b8ab5ce..02c4b67b 100644
--- a/ui/Taskbar_Seven/Taskbar_Seven.vcxproj
+++ b/ui/Taskbar_Seven/Taskbar_Seven.vcxproj
@@ -85,7 +85,7 @@
</BuildLog>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -120,7 +120,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
- <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;_DEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -153,7 +153,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
@@ -190,7 +190,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>ZEN_WIN;WXINTL_NO_GETTEXT_MACRO;NDEBUG;_WINDOWS;_USRDLL;TASKBAR_SEVEN_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp
index 0dfd6957..a72aab3e 100644
--- a/ui/batch_status_handler.cpp
+++ b/ui/batch_status_handler.cpp
@@ -8,8 +8,9 @@
#include <zen/file_handling.h>
#include <zen/file_traverser.h>
#include <zen/format_unit.h>
-#include <wx+/app_main.h>
+//#include <wx+/app_main.h>
#include <wx+/shell_execute.h>
+#include <wx/app.h>
#include "msg_popup.h"
#include "exec_finished_box.h"
#include "../lib/ffs_paths.h"
@@ -217,7 +218,7 @@ BatchStatusHandler::~BatchStatusHandler()
}
else
{
- if (progressDlg->getAsWindow()->IsShown())
+ if (progressDlg->getWindowIfVisible())
showFinalResults = true;
//execute "on completion" command (even in case of ignored errors)
@@ -236,7 +237,7 @@ BatchStatusHandler::~BatchStatusHandler()
if (showFinalResults) //warning: wxWindow::Show() is called within processHasFinished()!
{
//notify about (logical) application main window => program won't quit, but stay on this dialog
- setMainWindow(progressDlg->getAsWindow());
+ //setMainWindow(progressDlg->getAsWindow()); -> not required anymore since we block waiting until dialog is closed below
//notify to progressDlg that current process has ended
if (abortIsRequested())
@@ -257,7 +258,7 @@ BatchStatusHandler::~BatchStatusHandler()
//-> nicely manages dialog lifetime
while (progressDlg)
{
- updateUiNow(); //*first* refresh GUI (removing flicker) before sleeping!
+ wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping!
boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL));
}
}
@@ -306,7 +307,7 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool&
forceUiRefresh();
bool dontWarnAgain = false;
- switch (showWarningDlg(progressDlg->getAsWindow(),
+ switch (showWarningDlg(progressDlg->getWindowIfVisible(),
ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_SWITCH | ReturnWarningDlg::BUTTON_CANCEL,
warningMessage + L"\n\n" + _("Press \"Switch\" to resolve issues in FreeFileSync main dialog."),
dontWarnAgain))
@@ -352,7 +353,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
forceUiRefresh();
bool ignoreNextErrors = false;
- switch (showErrorDlg(progressDlg->getAsWindow(),
+ switch (showErrorDlg(progressDlg->getWindowIfVisible(),
ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL,
errorMessage, &ignoreNextErrors))
{
@@ -399,7 +400,7 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage)
forceUiRefresh();
bool ignoreNextErrors = false;
- switch (showFatalErrorDlg(progressDlg->getAsWindow(),
+ switch (showFatalErrorDlg(progressDlg->getWindowIfVisible(),
ReturnFatalErrorDlg::BUTTON_IGNORE | ReturnFatalErrorDlg::BUTTON_CANCEL,
errorMessage, &ignoreNextErrors))
{
diff --git a/ui/check_version.cpp b/ui/check_version.cpp
index 2e4c387a..e51c1784 100644
--- a/ui/check_version.cpp
+++ b/ui/check_version.cpp
@@ -233,11 +233,11 @@ void zen::checkForUpdateNow(wxWindow* parent)
wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/get_latest.php");
}
else
- wxMessageBox(_("FreeFileSync is up to date."), _("Information"), wxOK | wxICON_INFORMATION, parent);
+ wxMessageBox(_("FreeFileSync is up to date."), L"FreeFileSync - " + _("Information"), wxOK | wxICON_INFORMATION, parent);
break;
case GET_VER_NO_CONNECTION:
- wxMessageBox(_("Unable to connect to sourceforge.net."), _("Error"), wxOK | wxICON_ERROR, parent);
+ wxMessageBox(_("Unable to connect to sourceforge.net."), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR, parent);
break;
case GET_VER_PAGE_NOT_FOUND:
diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp
index 58e7a7e4..59e80100 100644
--- a/ui/custom_grid.cpp
+++ b/ui/custom_grid.cpp
@@ -811,19 +811,19 @@ public:
break;
case BLOCKPOS_LEFT:
{
- SyncDirectionEvent evt(rowFirst, rowLast, SYNC_DIR_LEFT);
+ SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::LEFT);
evtHandler->ProcessEvent(evt);
}
break;
case BLOCKPOS_MIDDLE:
{
- SyncDirectionEvent evt(rowFirst, rowLast, SYNC_DIR_NONE);
+ SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::NONE);
evtHandler->ProcessEvent(evt);
}
break;
case BLOCKPOS_RIGHT:
{
- SyncDirectionEvent evt(rowFirst, rowLast, SYNC_DIR_RIGHT);
+ SyncDirectionEvent evt(rowFirst, rowLast, SyncDirection::RIGHT);
evtHandler->ProcessEvent(evt);
}
break;
@@ -983,13 +983,13 @@ private:
case BLOCKPOS_CHECK_BOX:
break;
case BLOCKPOS_LEFT:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer);
break;
case BLOCKPOS_MIDDLE:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_NONE)), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::NONE)), rect, wxALIGN_CENTER, buffer);
break;
case BLOCKPOS_RIGHT:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, buffer);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, buffer);
break;
}
else //default
diff --git a/ui/dir_name.cpp b/ui/dir_name.cpp
index 4a3939e9..7a068343 100644
--- a/ui/dir_name.cpp
+++ b/ui/dir_name.cpp
@@ -227,7 +227,7 @@ void DirectoryName<NameControl>::onSelectDir(wxCommandEvent& event)
errorMsg); //out, optional: call freeString() after use!
if (errorMsg)
{
- wxMessageBox(errorMsg, _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(errorMsg, /*L"FreeFileSync - " +*/ _("Error"), wxOK | wxICON_ERROR); //component used by FFS *and* RTS!
return;
}
if (cancelled || !selectedFolder)
diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp
index 3549519c..21f5f82d 100644
--- a/ui/gui_generated.cpp
+++ b/ui/gui_generated.cpp
@@ -1098,6 +1098,9 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi
bSizer42 = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer162StretchSpeedAndRemTimeIndependently;
+ bSizer162StretchSpeedAndRemTimeIndependently = new wxBoxSizer( wxHORIZONTAL );
+
wxBoxSizer* bSizer157;
bSizer157 = new wxBoxSizer( wxVERTICAL );
@@ -1142,10 +1145,10 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi
bSizer157->Add( bSizerFilesRemaining, 0, 0, 5 );
- bSizer42->Add( bSizer157, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer162StretchSpeedAndRemTimeIndependently->Add( bSizer157, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer42->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer162StretchSpeedAndRemTimeIndependently->Add( 0, 0, 1, wxEXPAND, 5 );
sSizerSpeed = new wxBoxSizer( wxHORIZONTAL );
@@ -1160,11 +1163,17 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi
sSizerSpeed->Add( m_staticTextSpeed, 0, wxLEFT|wxALIGN_BOTTOM, 5 );
- bSizer42->Add( sSizerSpeed, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer162StretchSpeedAndRemTimeIndependently->Add( sSizerSpeed, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer42->Add( bSizer162StretchSpeedAndRemTimeIndependently, 1, wxALIGN_CENTER_VERTICAL, 5 );
bSizer42->Add( 10, 0, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ wxBoxSizer* bSizer163;
+ bSizer163 = new wxBoxSizer( wxHORIZONTAL );
+
sSizerTimeRemaining = new wxBoxSizer( wxHORIZONTAL );
m_staticTextTimeRemFixed = new wxStaticText( this, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 );
@@ -1178,10 +1187,10 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi
sSizerTimeRemaining->Add( m_staticTextRemTime, 0, wxLEFT|wxALIGN_BOTTOM, 5 );
- bSizer42->Add( sSizerTimeRemaining, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer163->Add( sSizerTimeRemaining, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer42->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer163->Add( 0, 0, 1, wxEXPAND, 5 );
sSizerTimeElapsed = new wxBoxSizer( wxHORIZONTAL );
@@ -1197,7 +1206,10 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi
sSizerTimeElapsed->Add( m_staticTextTimeElapsed, 0, wxLEFT|wxALIGN_BOTTOM, 5 );
- bSizer42->Add( sSizerTimeElapsed, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer163->Add( sSizerTimeElapsed, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer42->Add( bSizer163, 1, wxALIGN_CENTER_VERTICAL, 5 );
bSizer40->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
@@ -1215,17 +1227,20 @@ CompareProgressDlgGenerated::~CompareProgressDlgGenerated()
{
}
-SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
+SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style )
{
- this->SetSizeHints( wxSize( 470,260 ), wxDefaultSize );
- this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
-
bSizerRoot = new wxBoxSizer( wxVERTICAL );
bSizer42 = new wxBoxSizer( wxHORIZONTAL );
+
+ bSizer42->Add( 32, 0, 0, 0, 5 );
+
+
+ bSizer42->Add( 0, 0, 1, wxEXPAND, 5 );
+
m_bitmapStatus = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 );
- bSizer42->Add( m_bitmapStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+ bSizer42->Add( m_bitmapStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 );
m_staticTextPhase = new wxStaticText( this, wxID_ANY, _("Synchronizing..."), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextPhase->Wrap( -1 );
@@ -1236,11 +1251,32 @@ SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID
m_animCtrlSyncing = new wxAnimationCtrl( this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 32,32 ), wxAC_DEFAULT_STYLE );
m_animCtrlSyncing->SetMinSize( wxSize( 32,32 ) );
- bSizer42->Add( m_animCtrlSyncing, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+ bSizer42->Add( m_animCtrlSyncing, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 );
- bSizerRoot->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 5 );
+ bSizer42->Add( 0, 0, 1, wxEXPAND, 5 );
+
+ m_bpButtonMinimizeToTray = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), wxBU_AUTODRAW );
+ m_bpButtonMinimizeToTray->SetToolTip( _("Minimize to notification area") );
+
+ bSizer42->Add( m_bpButtonMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizerRoot->Add( bSizer42, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 );
+
+ bSizerStatusText = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextStatus = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextStatus->Wrap( -1 );
+ bSizerStatusText->Add( m_staticTextStatus, 0, wxEXPAND|wxLEFT, 10 );
+
+ bSizerStatusText->Add( 0, 5, 0, 0, 5 );
+
+
+ bSizerRoot->Add( bSizerStatusText, 0, wxEXPAND, 5 );
+
+ wxStaticLine* m_staticlineHeader;
m_staticlineHeader = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizerRoot->Add( m_staticlineHeader, 0, wxEXPAND, 5 );
@@ -1250,101 +1286,181 @@ SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID
wxBoxSizer* bSizer173;
bSizer173 = new wxBoxSizer( wxVERTICAL );
- m_staticTextStatus = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextStatus->Wrap( -1 );
- bSizer173->Add( m_staticTextStatus, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
-
- m_gauge1 = new wxGauge( m_panelProgress, wxID_ANY, 100, wxDefaultPosition, wxSize( -1,14 ), wxGA_HORIZONTAL );
- bSizer173->Add( m_gauge1, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
-
bSizer171 = new wxBoxSizer( wxHORIZONTAL );
bSizer171->Add( 10, 0, 0, 0, 5 );
- wxFlexGridSizer* fgSizer10;
- fgSizer10 = new wxFlexGridSizer( 0, 2, 2, 5 );
- fgSizer10->SetFlexibleDirection( wxBOTH );
- fgSizer10->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
+ wxBoxSizer* bSizer164;
+ bSizer164 = new wxBoxSizer( wxVERTICAL );
+
+ m_panelItemsProcessed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panelItemsProcessed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* bSizer165;
+ bSizer165 = new wxBoxSizer( wxVERTICAL );
- m_staticTextLabelItemsProc = new wxStaticText( m_panelProgress, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextLabelItemsProc->Wrap( -1 );
- fgSizer10->Add( m_staticTextLabelItemsProc, 0, wxALIGN_BOTTOM, 5 );
- bSizerItemsProc = new wxBoxSizer( wxHORIZONTAL );
+ bSizer165->Add( 0, 5, 0, 0, 5 );
- m_staticTextProcessedObj = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ wxStaticText* m_staticText96;
+ m_staticText96 = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText96->Wrap( -1 );
+ bSizer165->Add( m_staticText96, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ wxBoxSizer* bSizer169;
+ bSizer169 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_staticTextProcessedObj = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
m_staticTextProcessedObj->Wrap( -1 );
m_staticTextProcessedObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
- bSizerItemsProc->Add( m_staticTextProcessedObj, 0, wxALIGN_BOTTOM, 5 );
+ bSizer169->Add( m_staticTextProcessedObj, 0, wxALIGN_BOTTOM, 5 );
- m_staticTextDataProcessed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextDataProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextDataProcessed->Wrap( -1 );
- bSizerItemsProc->Add( m_staticTextDataProcessed, 0, wxALIGN_BOTTOM|wxLEFT, 5 );
+ bSizer169->Add( m_staticTextDataProcessed, 0, wxLEFT|wxALIGN_BOTTOM, 5 );
+
+
+ bSizer165->Add( bSizer169, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
+
+
+ bSizer165->Add( 0, 5, 0, 0, 5 );
+
+ m_panelItemsProcessed->SetSizer( bSizer165 );
+ m_panelItemsProcessed->Layout();
+ bSizer165->Fit( m_panelItemsProcessed );
+ bSizer164->Add( m_panelItemsProcessed, 0, wxEXPAND|wxTOP, 7 );
- fgSizer10->Add( bSizerItemsProc, 0, wxALIGN_BOTTOM, 5 );
+ m_panelItemsRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panelItemsRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
- m_staticTextLabelItemsRem = new wxStaticText( m_panelProgress, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextLabelItemsRem->Wrap( -1 );
- fgSizer10->Add( m_staticTextLabelItemsRem, 0, wxALIGN_BOTTOM, 5 );
+ wxBoxSizer* bSizer166;
+ bSizer166 = new wxBoxSizer( wxVERTICAL );
+
+
+ bSizer166->Add( 0, 5, 0, 0, 5 );
+
+ wxStaticText* m_staticText97;
+ m_staticText97 = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("Items remaining:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText97->Wrap( -1 );
+ bSizer166->Add( m_staticText97, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizerItemsRem = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer170;
+ bSizer170 = new wxBoxSizer( wxHORIZONTAL );
- m_staticTextRemainingObj = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_staticTextRemainingObj = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
m_staticTextRemainingObj->Wrap( -1 );
m_staticTextRemainingObj->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
- bSizerItemsRem->Add( m_staticTextRemainingObj, 0, wxALIGN_BOTTOM, 5 );
+ bSizer170->Add( m_staticTextRemainingObj, 0, wxALIGN_BOTTOM, 5 );
- m_staticTextDataRemaining = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextDataRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextDataRemaining->Wrap( -1 );
- bSizerItemsRem->Add( m_staticTextDataRemaining, 0, wxALIGN_BOTTOM|wxLEFT, 5 );
+ bSizer170->Add( m_staticTextDataRemaining, 0, wxLEFT|wxALIGN_BOTTOM, 5 );
- fgSizer10->Add( bSizerItemsRem, 0, wxALIGN_BOTTOM, 5 );
+ bSizer166->Add( bSizer170, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
- m_staticText84 = new wxStaticText( m_panelProgress, wxID_ANY, _("Speed:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText84->Wrap( -1 );
- fgSizer10->Add( m_staticText84, 0, wxALIGN_BOTTOM, 5 );
- m_staticTextSpeed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextSpeed->Wrap( -1 );
- m_staticTextSpeed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+ bSizer166->Add( 0, 5, 0, 0, 5 );
- fgSizer10->Add( m_staticTextSpeed, 0, wxALIGN_BOTTOM, 5 );
- m_staticTextLabelRemTime = new wxStaticText( m_panelProgress, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextLabelRemTime->Wrap( -1 );
- fgSizer10->Add( m_staticTextLabelRemTime, 0, wxALIGN_BOTTOM, 5 );
+ m_panelItemsRemaining->SetSizer( bSizer166 );
+ m_panelItemsRemaining->Layout();
+ bSizer166->Fit( m_panelItemsRemaining );
+ bSizer164->Add( m_panelItemsRemaining, 0, wxTOP|wxEXPAND, 7 );
+
+ m_panelTimeRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panelTimeRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* bSizer167;
+ bSizer167 = new wxBoxSizer( wxVERTICAL );
- m_staticTextRemTime = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+
+ bSizer167->Add( 0, 5, 0, 0, 5 );
+
+ wxStaticText* m_staticText98;
+ m_staticText98 = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("Time remaining:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText98->Wrap( -1 );
+ bSizer167->Add( m_staticText98, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ m_staticTextRemTime = new wxStaticText( m_panelTimeRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextRemTime->Wrap( -1 );
m_staticTextRemTime->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
- fgSizer10->Add( m_staticTextRemTime, 0, wxALIGN_BOTTOM, 5 );
+ bSizer167->Add( m_staticTextRemTime, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
+
- m_staticTextLabelElapsedTime = new wxStaticText( m_panelProgress, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextLabelElapsedTime->Wrap( -1 );
- fgSizer10->Add( m_staticTextLabelElapsedTime, 0, wxALIGN_BOTTOM, 5 );
+ bSizer167->Add( 0, 5, 0, 0, 5 );
- m_staticTextTimeElapsed = new wxStaticText( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+
+ m_panelTimeRemaining->SetSizer( bSizer167 );
+ m_panelTimeRemaining->Layout();
+ bSizer167->Fit( m_panelTimeRemaining );
+ bSizer164->Add( m_panelTimeRemaining, 0, wxTOP|wxEXPAND, 7 );
+
+ wxPanel* m_panelTimeElapsed;
+ m_panelTimeElapsed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panelTimeElapsed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* bSizer168;
+ bSizer168 = new wxBoxSizer( wxVERTICAL );
+
+
+ bSizer168->Add( 0, 5, 0, 0, 5 );
+
+ wxStaticText* m_staticText961;
+ m_staticText961 = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("Time elapsed:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText961->Wrap( -1 );
+ bSizer168->Add( m_staticText961, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ m_staticTextTimeElapsed = new wxStaticText( m_panelTimeElapsed, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextTimeElapsed->Wrap( -1 );
m_staticTextTimeElapsed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
- fgSizer10->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM, 5 );
+ bSizer168->Add( m_staticTextTimeElapsed, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
+
+
+ bSizer168->Add( 0, 5, 0, 0, 5 );
+
+ m_panelTimeElapsed->SetSizer( bSizer168 );
+ m_panelTimeElapsed->Layout();
+ bSizer168->Fit( m_panelTimeElapsed );
+ bSizer164->Add( m_panelTimeElapsed, 0, wxTOP|wxEXPAND, 7 );
- bSizer171->Add( fgSizer10, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+ bSizer171->Add( bSizer164, 0, wxALIGN_CENTER_VERTICAL, 5 );
bSizer171->Add( 10, 0, 0, 0, 5 );
- m_panelGraph = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( 340,150 ), wxTAB_TRAVERSAL );
- m_panelGraph->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ wxBoxSizer* bSizer161;
+ bSizer161 = new wxBoxSizer( wxVERTICAL );
+
+
+ bSizer161->Add( 0, 15, 0, 0, 5 );
+
+ m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ bSizer161->Add( m_panelGraphBytes, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
+
+ m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_panelGraphItems->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ bSizer161->Add( m_panelGraphItems, 1, wxEXPAND, 5 );
- bSizer171->Add( m_panelGraph, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 10 );
+
+ bSizer161->Add( 420, 0, 0, 0, 5 );
+
+
+ bSizer171->Add( bSizer161, 1, wxEXPAND, 5 );
+
+
+ bSizer171->Add( 0, 230, 0, 0, 5 );
bSizer173->Add( bSizer171, 1, wxEXPAND, 5 );
@@ -1404,26 +1520,10 @@ SyncProgressDlgGenerated::SyncProgressDlgGenerated( wxWindow* parent, wxWindowID
this->SetSizer( bSizerRoot );
this->Layout();
bSizerRoot->Fit( this );
-
- this->Centre( wxBOTH );
-
- // Connect Events
- this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncProgressDlgGenerated::OnClose ) );
- this->Connect( wxEVT_ICONIZE, wxIconizeEventHandler( SyncProgressDlgGenerated::OnIconize ) );
- m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnOkay ), NULL, this );
- m_buttonPause->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnPause ), NULL, this );
- m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnCancel ), NULL, this );
}
-SyncProgressDlgGenerated::~SyncProgressDlgGenerated()
+SyncProgressPanelGenerated::~SyncProgressPanelGenerated()
{
- // Disconnect Events
- this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncProgressDlgGenerated::OnClose ) );
- this->Disconnect( wxEVT_ICONIZE, wxIconizeEventHandler( SyncProgressDlgGenerated::OnIconize ) );
- m_buttonClose->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnOkay ), NULL, this );
- m_buttonPause->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnPause ), NULL, this );
- m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncProgressDlgGenerated::OnCancel ), NULL, this );
-
}
LogPanelGenerated::LogPanelGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style )
@@ -1439,13 +1539,13 @@ LogPanelGenerated::LogPanelGenerated( wxWindow* parent, wxWindowID id, const wxP
wxBoxSizer* bSizer154;
bSizer154 = new wxBoxSizer( wxVERTICAL );
- m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), wxBU_AUTODRAW );
+ m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW );
bSizer154->Add( m_bpButtonErrors, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
- m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), wxBU_AUTODRAW );
+ m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW );
bSizer154->Add( m_bpButtonWarnings, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
- m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), wxBU_AUTODRAW );
+ m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW );
bSizer154->Add( m_bpButtonInfo, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
@@ -1481,6 +1581,200 @@ LogPanelGenerated::~LogPanelGenerated()
}
+SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( 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 );
+ this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* bSizer134;
+ bSizer134 = new wxBoxSizer( wxVERTICAL );
+
+ m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ wxBoxSizer* bSizer169;
+ bSizer169 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_bitmapSync = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer169->Add( m_bitmapSync, 0, wxALL, 10 );
+
+ m_staticline39 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
+ bSizer169->Add( m_staticline39, 0, wxEXPAND, 5 );
+
+ wxBoxSizer* bSizer162;
+ bSizer162 = new wxBoxSizer( wxVERTICAL );
+
+ wxBoxSizer* bSizer172;
+ bSizer172 = new wxBoxSizer( wxVERTICAL );
+
+ m_staticText84 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Variant"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText84->Wrap( -1 );
+ bSizer172->Add( m_staticText84, 0, wxALL, 5 );
+
+ m_staticTextVariant = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextVariant->Wrap( -1 );
+ m_staticTextVariant->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+
+ bSizer172->Add( m_staticTextVariant, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+
+ bSizer172->Add( 0, 10, 0, 0, 5 );
+
+
+ bSizer162->Add( bSizer172, 0, wxEXPAND, 5 );
+
+ m_staticline14 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer162->Add( m_staticline14, 0, wxEXPAND, 5 );
+
+ m_staticText83 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Statistics"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText83->Wrap( -1 );
+ bSizer162->Add( m_staticText83, 0, wxALL, 5 );
+
+ wxFlexGridSizer* fgSizer11;
+ fgSizer11 = new wxFlexGridSizer( 2, 7, 2, 5 );
+ fgSizer11->SetFlexibleDirection( wxBOTH );
+ fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
+
+ m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") );
+
+ fgSizer11->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be overwritten") );
+
+ fgSizer11->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") );
+
+ fgSizer11->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ m_bitmapData->SetToolTip( _("Total bytes to copy") );
+
+ fgSizer11->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") );
+
+ fgSizer11->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ m_bitmapUpdateRight->SetToolTip( _("Number of files that will be overwritten") );
+
+ fgSizer11->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") );
+
+ fgSizer11->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextCreateLeft->Wrap( -1 );
+ m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") );
+
+ fgSizer11->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextUpdateLeft->Wrap( -1 );
+ m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be overwritten") );
+
+ fgSizer11->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextDeleteLeft->Wrap( -1 );
+ m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") );
+
+ fgSizer11->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextData->Wrap( -1 );
+ m_staticTextData->SetToolTip( _("Total bytes to copy") );
+
+ fgSizer11->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextDeleteRight->Wrap( -1 );
+ m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") );
+
+ fgSizer11->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextUpdateRight->Wrap( -1 );
+ m_staticTextUpdateRight->SetToolTip( _("Number of files that will be overwritten") );
+
+ fgSizer11->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextCreateRight->Wrap( -1 );
+ m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") );
+
+ fgSizer11->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer162->Add( fgSizer11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 25 );
+
+
+ bSizer162->Add( 0, 5, 0, 0, 5 );
+
+
+ bSizer169->Add( bSizer162, 0, 0, 5 );
+
+
+ m_panelStatistics->SetSizer( bSizer169 );
+ m_panelStatistics->Layout();
+ bSizer169->Fit( m_panelStatistics );
+ bSizer134->Add( m_panelStatistics, 0, wxEXPAND, 5 );
+
+ m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 );
+
+ wxBoxSizer* bSizer164;
+ bSizer164 = new wxBoxSizer( wxVERTICAL );
+
+ m_checkBoxDontShowAgain = new wxCheckBox( this, wxID_ANY, _("Don't show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer164->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 );
+
+ bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
+
+ m_buttonStartSync = new wxButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1,30 ), 0 );
+ m_buttonStartSync->SetDefault();
+ m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+
+ bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+ m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 );
+ bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
+
+
+ bSizer164->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 );
+
+
+ bSizer134->Add( bSizer164, 1, wxEXPAND, 5 );
+
+
+ this->SetSizer( bSizer134 );
+ this->Layout();
+ bSizer134->Fit( this );
+
+ this->Centre( wxBOTH );
+
+ // Connect Events
+ this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) );
+ m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this );
+ m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this );
+}
+
+SyncConfirmationDlgGenerated::~SyncConfirmationDlgGenerated()
+{
+ // Disconnect Events
+ this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) );
+ m_buttonStartSync->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this );
+ m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this );
+
+}
+
CmpCfgDlgGenerated::CmpCfgDlgGenerated( 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 );
@@ -3191,200 +3485,6 @@ GlobalSettingsDlgGenerated::~GlobalSettingsDlgGenerated()
}
-SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( 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 );
- this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
-
- wxBoxSizer* bSizer134;
- bSizer134 = new wxBoxSizer( wxVERTICAL );
-
- m_panelStatistics = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
- m_panelStatistics->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
-
- wxBoxSizer* bSizer169;
- bSizer169 = new wxBoxSizer( wxHORIZONTAL );
-
- m_bitmapSync = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- bSizer169->Add( m_bitmapSync, 0, wxALL, 10 );
-
- m_staticline39 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
- bSizer169->Add( m_staticline39, 0, wxEXPAND, 5 );
-
- wxBoxSizer* bSizer162;
- bSizer162 = new wxBoxSizer( wxVERTICAL );
-
- wxBoxSizer* bSizer172;
- bSizer172 = new wxBoxSizer( wxVERTICAL );
-
- m_staticText84 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Variant"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText84->Wrap( -1 );
- bSizer172->Add( m_staticText84, 0, wxALL, 5 );
-
- m_staticTextVariant = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextVariant->Wrap( -1 );
- m_staticTextVariant->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
-
- bSizer172->Add( m_staticTextVariant, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
-
-
- bSizer172->Add( 0, 10, 0, 0, 5 );
-
-
- bSizer162->Add( bSizer172, 0, wxEXPAND, 5 );
-
- m_staticline14 = new wxStaticLine( m_panelStatistics, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer162->Add( m_staticline14, 0, wxEXPAND, 5 );
-
- m_staticText83 = new wxStaticText( m_panelStatistics, wxID_ANY, _("Statistics"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText83->Wrap( -1 );
- bSizer162->Add( m_staticText83, 0, wxALL, 5 );
-
- wxFlexGridSizer* fgSizer11;
- fgSizer11 = new wxFlexGridSizer( 2, 7, 2, 5 );
- fgSizer11->SetFlexibleDirection( wxBOTH );
- fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
-
- m_bitmapCreateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- m_bitmapCreateLeft->SetToolTip( _("Number of files and folders that will be created") );
-
- fgSizer11->Add( m_bitmapCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- m_bitmapUpdateLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- m_bitmapUpdateLeft->SetToolTip( _("Number of files that will be overwritten") );
-
- fgSizer11->Add( m_bitmapUpdateLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_bitmapDeleteLeft = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- m_bitmapDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") );
-
- fgSizer11->Add( m_bitmapDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_bitmapData = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- m_bitmapData->SetToolTip( _("Total bytes to copy") );
-
- fgSizer11->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- m_bitmapDeleteRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- m_bitmapDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") );
-
- fgSizer11->Add( m_bitmapDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_bitmapUpdateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- m_bitmapUpdateRight->SetToolTip( _("Number of files that will be overwritten") );
-
- fgSizer11->Add( m_bitmapUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- m_bitmapCreateRight = new wxStaticBitmap( m_panelStatistics, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- m_bitmapCreateRight->SetToolTip( _("Number of files and folders that will be created") );
-
- fgSizer11->Add( m_bitmapCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- m_staticTextCreateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextCreateLeft->Wrap( -1 );
- m_staticTextCreateLeft->SetToolTip( _("Number of files and folders that will be created") );
-
- fgSizer11->Add( m_staticTextCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- m_staticTextUpdateLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextUpdateLeft->Wrap( -1 );
- m_staticTextUpdateLeft->SetToolTip( _("Number of files that will be overwritten") );
-
- fgSizer11->Add( m_staticTextUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
-
- m_staticTextDeleteLeft = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextDeleteLeft->Wrap( -1 );
- m_staticTextDeleteLeft->SetToolTip( _("Number of files and folders that will be deleted") );
-
- fgSizer11->Add( m_staticTextDeleteLeft, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_staticTextData = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextData->Wrap( -1 );
- m_staticTextData->SetToolTip( _("Total bytes to copy") );
-
- fgSizer11->Add( m_staticTextData, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_staticTextDeleteRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextDeleteRight->Wrap( -1 );
- m_staticTextDeleteRight->SetToolTip( _("Number of files and folders that will be deleted") );
-
- fgSizer11->Add( m_staticTextDeleteRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_staticTextUpdateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextUpdateRight->Wrap( -1 );
- m_staticTextUpdateRight->SetToolTip( _("Number of files that will be overwritten") );
-
- fgSizer11->Add( m_staticTextUpdateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_staticTextCreateRight = new wxStaticText( m_panelStatistics, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextCreateRight->Wrap( -1 );
- m_staticTextCreateRight->SetToolTip( _("Number of files and folders that will be created") );
-
- fgSizer11->Add( m_staticTextCreateRight, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
-
- bSizer162->Add( fgSizer11, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 25 );
-
-
- bSizer162->Add( 0, 5, 0, 0, 5 );
-
-
- bSizer169->Add( bSizer162, 0, 0, 5 );
-
-
- m_panelStatistics->SetSizer( bSizer169 );
- m_panelStatistics->Layout();
- bSizer169->Fit( m_panelStatistics );
- bSizer134->Add( m_panelStatistics, 0, wxEXPAND, 5 );
-
- m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 );
-
- wxBoxSizer* bSizer164;
- bSizer164 = new wxBoxSizer( wxVERTICAL );
-
- m_checkBoxDontShowAgain = new wxCheckBox( this, wxID_ANY, _("Don't show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer164->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 );
-
- bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
-
- m_buttonStartSync = new wxButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1,30 ), 0 );
- m_buttonStartSync->SetDefault();
- m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
-
- bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
-
- m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 );
- bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
-
-
- bSizer164->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 );
-
-
- bSizer134->Add( bSizer164, 1, wxEXPAND, 5 );
-
-
- this->SetSizer( bSizer134 );
- this->Layout();
- bSizer134->Fit( this );
-
- this->Centre( wxBOTH );
-
- // Connect Events
- this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) );
- m_buttonStartSync->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this );
- m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this );
-}
-
-SyncConfirmationDlgGenerated::~SyncConfirmationDlgGenerated()
-{
- // Disconnect Events
- this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SyncConfirmationDlgGenerated::OnClose ) );
- m_buttonStartSync->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnStartSync ), NULL, this );
- m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncConfirmationDlgGenerated::OnCancel ), NULL, this );
-
-}
-
PopupDialogGenerated::PopupDialogGenerated( 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 );
diff --git a/ui/gui_generated.h b/ui/gui_generated.h
index 27395e33..f06ebf93 100644
--- a/ui/gui_generated.h
+++ b/ui/gui_generated.h
@@ -45,9 +45,9 @@ namespace zen{ class TripleSplitter; }
#include <wx/gauge.h>
#include <wx/animate.h>
#include <wx/notebook.h>
+#include <wx/dialog.h>
#include <wx/tglbtn.h>
#include <wx/choice.h>
-#include <wx/dialog.h>
#include <wx/spinctrl.h>
#include <wx/hyperlink.h>
#include <wx/grid.h>
@@ -290,61 +290,48 @@ class CompareProgressDlgGenerated : public wxPanel
};
///////////////////////////////////////////////////////////////////////////////
-/// Class SyncProgressDlgGenerated
+/// Class SyncProgressPanelGenerated
///////////////////////////////////////////////////////////////////////////////
-class SyncProgressDlgGenerated : public wxFrame
+class SyncProgressPanelGenerated : public wxPanel
{
private:
protected:
- wxBoxSizer* bSizerRoot;
wxBoxSizer* bSizer42;
+ wxBoxSizer* bSizer171;
+ wxStaticText* m_staticText87;
+
+ public:
+ wxBoxSizer* bSizerRoot;
wxStaticBitmap* m_bitmapStatus;
wxStaticText* m_staticTextPhase;
wxAnimationCtrl* m_animCtrlSyncing;
- wxStaticLine* m_staticlineHeader;
- wxPanel* m_panelProgress;
+ wxBitmapButton* m_bpButtonMinimizeToTray;
+ wxBoxSizer* bSizerStatusText;
wxStaticText* m_staticTextStatus;
- wxBoxSizer* bSizer171;
- wxStaticText* m_staticTextLabelItemsProc;
- wxBoxSizer* bSizerItemsProc;
+ wxPanel* m_panelProgress;
+ wxPanel* m_panelItemsProcessed;
wxStaticText* m_staticTextProcessedObj;
wxStaticText* m_staticTextDataProcessed;
- wxStaticText* m_staticTextLabelItemsRem;
- wxBoxSizer* bSizerItemsRem;
+ wxPanel* m_panelItemsRemaining;
wxStaticText* m_staticTextRemainingObj;
wxStaticText* m_staticTextDataRemaining;
- wxStaticText* m_staticText84;
- wxStaticText* m_staticTextSpeed;
- wxStaticText* m_staticTextLabelRemTime;
+ wxPanel* m_panelTimeRemaining;
wxStaticText* m_staticTextRemTime;
- wxStaticText* m_staticTextLabelElapsedTime;
wxStaticText* m_staticTextTimeElapsed;
- zen::Graph2D* m_panelGraph;
+ zen::Graph2D* m_panelGraphBytes;
+ zen::Graph2D* m_panelGraphItems;
wxNotebook* m_notebookResult;
wxStaticLine* m_staticlineFooter;
wxBoxSizer* bSizerStdButtons;
wxBoxSizer* bSizerExecFinished;
- wxStaticText* m_staticText87;
ExecFinishedBox* m_comboBoxExecFinished;
wxButton* m_buttonClose;
wxButton* m_buttonPause;
wxButton* m_buttonCancel;
- // Virtual event handlers, overide them in your derived class
- virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
- virtual void OnIconize( wxIconizeEvent& event ) { event.Skip(); }
- virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnPause( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
-
-
- public:
- wxGauge* m_gauge1;
-
- SyncProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
-
- ~SyncProgressDlgGenerated();
+ SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL );
+ ~SyncProgressPanelGenerated();
};
@@ -376,6 +363,54 @@ class LogPanelGenerated : public wxPanel
};
///////////////////////////////////////////////////////////////////////////////
+/// Class SyncConfirmationDlgGenerated
+///////////////////////////////////////////////////////////////////////////////
+class SyncConfirmationDlgGenerated : public wxDialog
+{
+ private:
+
+ protected:
+ wxPanel* m_panelStatistics;
+ wxStaticBitmap* m_bitmapSync;
+ wxStaticLine* m_staticline39;
+ wxStaticText* m_staticText84;
+ wxStaticText* m_staticTextVariant;
+ wxStaticLine* m_staticline14;
+ wxStaticText* m_staticText83;
+ wxStaticBitmap* m_bitmapCreateLeft;
+ wxStaticBitmap* m_bitmapUpdateLeft;
+ wxStaticBitmap* m_bitmapDeleteLeft;
+ wxStaticBitmap* m_bitmapData;
+ wxStaticBitmap* m_bitmapDeleteRight;
+ wxStaticBitmap* m_bitmapUpdateRight;
+ wxStaticBitmap* m_bitmapCreateRight;
+ wxStaticText* m_staticTextCreateLeft;
+ wxStaticText* m_staticTextUpdateLeft;
+ wxStaticText* m_staticTextDeleteLeft;
+ wxStaticText* m_staticTextData;
+ wxStaticText* m_staticTextDeleteRight;
+ wxStaticText* m_staticTextUpdateRight;
+ wxStaticText* m_staticTextCreateRight;
+ wxStaticLine* m_staticline12;
+ wxCheckBox* m_checkBoxDontShowAgain;
+ wxBoxSizer* bSizerStdButtons;
+ wxButton* m_buttonStartSync;
+ wxButton* m_buttonCancel;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
+ virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
+
+
+ public:
+
+ SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Start synchronization"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
+ ~SyncConfirmationDlgGenerated();
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
/// Class CmpCfgDlgGenerated
///////////////////////////////////////////////////////////////////////////////
class CmpCfgDlgGenerated : public wxDialog
@@ -571,7 +606,7 @@ class BatchDlgGenerated : public wxDialog
public:
FolderHistoryBox* m_logfileDir;
- BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as batch job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
+ BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as batch job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~BatchDlgGenerated();
};
@@ -664,7 +699,7 @@ class MessageDlgGenerated : public wxDialog
public:
- MessageDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
+ MessageDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~MessageDlgGenerated();
};
@@ -698,7 +733,7 @@ class DeleteDlgGenerated : public wxDialog
public:
- DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
+ DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER );
~DeleteDlgGenerated();
};
@@ -756,7 +791,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 = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
+ FilterDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Configure filter"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER );
~FilterDlgGenerated();
};
@@ -810,54 +845,6 @@ class GlobalSettingsDlgGenerated : public wxDialog
};
///////////////////////////////////////////////////////////////////////////////
-/// Class SyncConfirmationDlgGenerated
-///////////////////////////////////////////////////////////////////////////////
-class SyncConfirmationDlgGenerated : public wxDialog
-{
- private:
-
- protected:
- wxPanel* m_panelStatistics;
- wxStaticBitmap* m_bitmapSync;
- wxStaticLine* m_staticline39;
- wxStaticText* m_staticText84;
- wxStaticText* m_staticTextVariant;
- wxStaticLine* m_staticline14;
- wxStaticText* m_staticText83;
- wxStaticBitmap* m_bitmapCreateLeft;
- wxStaticBitmap* m_bitmapUpdateLeft;
- wxStaticBitmap* m_bitmapDeleteLeft;
- wxStaticBitmap* m_bitmapData;
- wxStaticBitmap* m_bitmapDeleteRight;
- wxStaticBitmap* m_bitmapUpdateRight;
- wxStaticBitmap* m_bitmapCreateRight;
- wxStaticText* m_staticTextCreateLeft;
- wxStaticText* m_staticTextUpdateLeft;
- wxStaticText* m_staticTextDeleteLeft;
- wxStaticText* m_staticTextData;
- wxStaticText* m_staticTextDeleteRight;
- wxStaticText* m_staticTextUpdateRight;
- wxStaticText* m_staticTextCreateRight;
- wxStaticLine* m_staticline12;
- wxCheckBox* m_checkBoxDontShowAgain;
- wxBoxSizer* bSizerStdButtons;
- wxButton* m_buttonStartSync;
- wxButton* m_buttonCancel;
-
- // Virtual event handlers, overide them in your derived class
- virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
- virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
-
-
- public:
-
- SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Start synchronization"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
- ~SyncConfirmationDlgGenerated();
-
-};
-
-///////////////////////////////////////////////////////////////////////////////
/// Class PopupDialogGenerated
///////////////////////////////////////////////////////////////////////////////
class PopupDialogGenerated : public wxDialog
diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp
index db92c24c..0131af3f 100644
--- a/ui/gui_status_handler.cpp
+++ b/ui/gui_status_handler.cpp
@@ -8,6 +8,7 @@
#include <wx/wupdlock.h>
#include <wx+/shell_execute.h>
#include <wx+/button.h>
+#include <wx/app.h>
#include "msg_popup.h"
#include "main_dlg.h"
#include "exec_finished_box.h"
@@ -169,13 +170,12 @@ void CompareStatusHandler::abortThisProcess()
//########################################################################################################
-SyncStatusHandler::SyncStatusHandler(wxTopLevelWindow* parentDlg,
+SyncStatusHandler::SyncStatusHandler(wxFrame* parentDlg,
size_t lastSyncsLogFileSizeMax,
OnGuiError handleError,
const std::wstring& jobName,
const std::wstring& execWhenFinished,
std::vector<std::wstring>& execFinishedHistory) :
- parentDlg_(parentDlg),
progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, parentDlg, true, jobName, execWhenFinished, execFinishedHistory)),
lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax),
handleError_(handleError),
@@ -267,7 +267,7 @@ SyncStatusHandler::~SyncStatusHandler()
//-> nicely manages dialog lifetime
while (progressDlg)
{
- updateUiNow(); //*first* refresh GUI (removing flicker) before sleeping!
+ wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping!
boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL));
}
}
@@ -313,7 +313,7 @@ ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& err
forceUiRefresh();
bool ignoreNextErrors = false;
- switch (showErrorDlg(parentDlg_,
+ switch (showErrorDlg(progressDlg->getWindowIfVisible(),
ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL,
errorMessage,
&ignoreNextErrors))
@@ -357,7 +357,7 @@ void SyncStatusHandler::reportFatalError(const std::wstring& errorMessage)
forceUiRefresh();
bool ignoreNextErrors = false;
- switch (showFatalErrorDlg(parentDlg_,
+ switch (showFatalErrorDlg(progressDlg->getWindowIfVisible(),
ReturnFatalErrorDlg::BUTTON_IGNORE | ReturnFatalErrorDlg::BUTTON_CANCEL,
errorMessage, &ignoreNextErrors))
{
@@ -395,7 +395,7 @@ void SyncStatusHandler::reportWarning(const std::wstring& warningMessage, bool&
forceUiRefresh();
bool dontWarnAgain = false;
- switch (showWarningDlg(parentDlg_,
+ switch (showWarningDlg(progressDlg->getWindowIfVisible(),
ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_CANCEL,
warningMessage, dontWarnAgain))
{
diff --git a/ui/gui_status_handler.h b/ui/gui_status_handler.h
index f7b58f05..30efe3fb 100644
--- a/ui/gui_status_handler.h
+++ b/ui/gui_status_handler.h
@@ -49,7 +49,7 @@ private:
class SyncStatusHandler : public zen::StatusHandler
{
public:
- SyncStatusHandler(wxTopLevelWindow* parentDlg,
+ SyncStatusHandler(wxFrame* parentDlg,
size_t lastSyncsLogFileSizeMax,
xmlAccess::OnGuiError handleError,
const std::wstring& jobName,
@@ -70,7 +70,6 @@ private:
virtual void abortThisProcess(); //throw GuiAbortProcess
void onProgressDialogTerminate();
- wxTopLevelWindow* parentDlg_;
SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler!
const size_t lastSyncsLogFileSizeMax_;
xmlAccess::OnGuiError handleError_;
diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp
index ead99c6a..eaae0545 100644
--- a/ui/main_dlg.cpp
+++ b/ui/main_dlg.cpp
@@ -8,7 +8,6 @@
#include <zen/format_unit.h>
#include <zen/file_handling.h>
#include <zen/serialize.h>
-//#include <zen/file_id.h>
//#include <zen/perf.h>
#include <zen/thread.h>
#include <wx/clipbrd.h>
@@ -97,23 +96,26 @@ public:
virtual bool acceptDrop(const std::vector<wxString>& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd)
{
- if (droppedFiles.empty())
+ if (std::any_of(droppedFiles.begin(), droppedFiles.end(), [](const wxString& filename)
+ {
+ using namespace xmlAccess;
+ switch (getXmlType(utfCvrtTo<Zstring>(filename))) //throw()
+ {
+ case XML_TYPE_GUI:
+ case XML_TYPE_BATCH:
+ return true;
+ case XML_TYPE_GLOBAL:
+ case XML_TYPE_OTHER:
+ break;
+ }
return false;
-
- switch (xmlAccess::getMergeType(toZ(droppedFiles))) //throw()
+ }))
{
- case xmlAccess::MERGE_BATCH:
- case xmlAccess::MERGE_GUI:
- case xmlAccess::MERGE_GUI_BATCH:
- mainDlg_.loadConfiguration(toZ(droppedFiles));
- return false;
-
- case xmlAccess::MERGE_OTHER:
- //=> return true: change directory selection via drag and drop
- break;
+ mainDlg_.loadConfiguration(toZ(droppedFiles));
+ return false;
}
- //mainDlg_.clearGrid();
+ //=> return true: change directory selection via drag and drop
return true;
}
@@ -321,51 +323,43 @@ xmlAccess::XmlGlobalSettings retrieveGlobalCfgFromDisk() //blocks on GUI on erro
{
assert(false);
if (e.getSeverity() != FfsXmlError::WARNING) //ignore parsing errors: should be migration problems only *cross-fingers*
- wxMessageBox(e.toString(), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(e.toString(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
}
return globalCfg;
}
}
-void MainDialog::create(const std::vector<Zstring>& cfgFileNames)
+void MainDialog::create()
{
using namespace xmlAccess;
const XmlGlobalSettings globalSettings = retrieveGlobalCfgFromDisk();
- std::vector<Zstring> filenames;
- if (!cfgFileNames.empty()) //1. this one has priority
- filenames = cfgFileNames;
- else //FFS default startup: use last used selection
- {
- filenames = globalSettings.gui.lastUsedConfigFiles; //2. now try last used files
-
- //------------------------------------------------------------------------------------------
- //check existence of all directories in parallel!
+ std::vector<Zstring> filenames = globalSettings.gui.lastUsedConfigFiles; //2. now try last used files
- RunUntilFirstHit<NullType> findFirstMissing;
+ //------------------------------------------------------------------------------------------
+ //check existence of all files in parallel:
+ RunUntilFirstHit<NullType> findFirstMissing;
- std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename)
- {
- findFirstMissing.addJob([=] { return filename.empty() /*ever empty??*/ || !fileExists(filename) ? zen::make_unique<NullType>() : nullptr; });
- });
- //potentially slow network access: give all checks 500ms to finish
- const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //false: time elapsed
- !findFirstMissing.get(); //no missing
- if (!allFilesExist)
- filenames.clear(); //we do NOT want to show an error due to last config file missing on application start!
- //------------------------------------------------------------------------------------------
+ std::for_each(filenames.begin(), filenames.end(), [&](const Zstring& filename)
+ {
+ findFirstMissing.addJob([=] { return filename.empty() /*ever empty??*/ || !fileExists(filename) ? zen::make_unique<NullType>() : nullptr; });
+ });
+ //potentially slow network access: give all checks 500ms to finish
+ const bool allFilesExist = findFirstMissing.timedWait(boost::posix_time::milliseconds(500)) && //false: time elapsed
+ !findFirstMissing.get(); //no missing
+ if (!allFilesExist)
+ filenames.clear(); //we do NOT want to show an error due to last config file missing on application start!
+ //------------------------------------------------------------------------------------------
- if (filenames.empty())
- {
- if (zen::fileExists(lastRunConfigName())) //3. try to load auto-save config
- filenames.push_back(lastRunConfigName());
- }
+ if (filenames.empty())
+ {
+ if (zen::fileExists(lastRunConfigName())) //3. try to load auto-save config
+ filenames.push_back(lastRunConfigName());
}
XmlGuiConfig guiCfg; //structure to receive gui settings with default values
- bool loadCfgSuccess = false;
if (filenames.empty())
{
//add default exclusion filter: this is only ever relevant when creating new configurations!
@@ -379,58 +373,40 @@ void MainDialog::create(const std::vector<Zstring>& cfgFileNames)
try
{
readAnyConfig(filenames, guiCfg); //throw FfsXmlError
- loadCfgSuccess = true;
}
catch (const FfsXmlError& error)
{
if (error.getSeverity() == FfsXmlError::WARNING)
- wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING);
+ wxMessageBox(error.toString(), L"FreeFileSync - " + _("Warning"), wxOK | wxICON_WARNING);
//what about simulating changed config on parsing errors????
else
- wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(error.toString(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
}
- const bool startComparisonImmediately = !cfgFileNames.empty() && loadCfgSuccess;
-
//------------------------------------------------------------------------------------------
- create_impl(guiCfg, filenames, globalSettings, startComparisonImmediately);
-}
-
-
-void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg,
- bool startComparison)
-{
- create_impl(guiCfg, std::vector<Zstring>(), retrieveGlobalCfgFromDisk(), startComparison);
+ create(guiCfg, filenames, &globalSettings, false);
}
void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg,
const std::vector<Zstring>& referenceFiles,
- const xmlAccess::XmlGlobalSettings& globalSettings,
+ const xmlAccess::XmlGlobalSettings* globalSettings,
bool startComparison)
{
- create_impl(guiCfg, referenceFiles, globalSettings, startComparison);
-}
-
-
-void MainDialog::create_impl(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<Zstring>& referenceFiles,
- const xmlAccess::XmlGlobalSettings& globalSettings,
- bool startComparison)
-{
+ const xmlAccess::XmlGlobalSettings& globSett = globalSettings ? *globalSettings : retrieveGlobalCfgFromDisk();
try
{
//we need to set language *before* creating MainDialog!
- setLanguage(globalSettings.programLanguage); //throw FileError
+ setLanguage(globSett.programLanguage); //throw FileError
}
catch (const FileError& e)
{
- wxMessageBox(e.toString().c_str(), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(e.toString().c_str(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
//continue!
}
- MainDialog* frame = new MainDialog(guiCfg, referenceFiles, globalSettings, startComparison);
+ MainDialog* frame = new MainDialog(guiCfg, referenceFiles, globSett, startComparison);
frame->Show();
}
@@ -757,7 +733,7 @@ MainDialog::~MainDialog()
}
catch (const xmlAccess::FfsXmlError& e)
{
- wxMessageBox(e.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(e.toString().c_str(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR, this);
}
try //save "LastRun.ffs_gui"
@@ -1026,7 +1002,7 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe
}
catch (const std::bad_alloc& e)
{
- wxMessageBox(_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR, this);
}
}
@@ -1064,10 +1040,9 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const
if (auto root = dynamic_cast<const TreeView::RootNode*>(node.get()))
{
//select first level of child elements
- std::transform(root->baseDirObj_.refSubDirs ().begin(), root->baseDirObj_.refSubDirs ().end(), std::back_inserter(output), [](FileSystemObject& fsObj) { return &fsObj; });
- std::transform(root->baseDirObj_.refSubFiles().begin(), root->baseDirObj_.refSubFiles().end(), std::back_inserter(output), [](FileSystemObject& fsObj) { return &fsObj; });
- std::transform(root->baseDirObj_.refSubLinks().begin(), root->baseDirObj_.refSubLinks().end(), std::back_inserter(output), [](FileSystemObject& fsObj) { return &fsObj; });
- //for (auto& fsObj : root->baseDirObj_.refSubLinks()) output.push_back(&fsObj); -> seriously MSVC, stop this disgrace and implement "range for"!
+ for (auto& fsObj : root->baseDirObj_.refSubDirs ()) output.push_back(&fsObj);
+ for (auto& fsObj : root->baseDirObj_.refSubFiles()) output.push_back(&fsObj);
+ for (auto& fsObj : root->baseDirObj_.refSubLinks()) output.push_back(&fsObj);
}
else if (auto dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
output.push_back(&(dir->dirObj_));
@@ -1170,7 +1145,7 @@ private:
{
//std::wstring msg = toGuiString(deletionCount) +
mainDlg.setStatusBarFullText(statusMsg);
- updateUiNow();
+ wxTheApp->Yield();
}
//context: C callstack message loop => throw()!
@@ -1314,6 +1289,31 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std:
}
}
+ const size_t massInvokeThreshold = 10; //more than this is likely a user mistake
+
+ if (selectionTmp.size() > massInvokeThreshold)
+ if (globalCfg.optDialogs.confirmExternalCommandMassInvoke)
+ {
+ bool dontAskAgain = false;
+ switch (showQuestionDlg(this,
+ ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_CANCEL,
+ replaceCpy(replaceCpy(_P("Do you really want to execute the command %y for 1 item?",
+ "Do you really want to execute the command %y for %x items?", selectionTmp.size()),
+ L"%x", toGuiString(selectionTmp.size())), L"%y", L'\'' + commandline + L'\''),
+ QuestConfig().setCaption(_("Confirm")).setLabelYes(_("&Execute")).
+ showCheckBox(dontAskAgain, _("&Don't show this dialog again"), 0)))
+ {
+ case ReturnQuestionDlg::BUTTON_YES:
+ globalCfg.optDialogs.confirmExternalCommandMassInvoke = !dontAskAgain;
+ break;
+
+ case ReturnQuestionDlg::BUTTON_NO:
+ assert(false);
+ case ReturnQuestionDlg::BUTTON_CANCEL:
+ return;
+ }
+ }
+
//regular command evaluation
for (auto it = selectionTmp.begin(); it != selectionTmp.end(); ++it) //context menu calls this function only if selection is not empty!
{
@@ -1338,7 +1338,8 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std:
replace(command, Zstr("%item2_folder%"), dir2 );
auto cmdExp = expandMacros(command);
- zen::shellExecute(cmdExp); //shows error message if command is malformed
+ //caveat: spawning too many threads asynchronously can easily kill a user's desktop session!
+ zen::shellExecute(cmdExp, selectionTmp.size() > massInvokeThreshold ? EXEC_TYPE_SYNC : EXEC_TYPE_ASYNC);
}
}
@@ -1448,27 +1449,26 @@ void MainDialog::onProcessAsyncTasks(wxEvent& event)
void MainDialog::disableAllElements(bool enableAbort)
{
+ //disables all elements (except abort button) that might receive user input during long-running processes:
//when changing consider: comparison, synchronization, manual deletion
EnableCloseButton(false); //not allowed for synchronization! progress indicator is top window! -> not honored on OS X!
- //disables all elements (except abort button) that might receive user input during long-running processes: comparison, deletion
- m_panelViewFilter ->Disable();
+ //OS X: wxWidgets portability promise is again a mess: http://wxwidgets.10942.n7.nabble.com/Disable-panel-and-appropriate-children-windows-linux-macos-td35357.html
+
+ m_menubar1->EnableTop(0, false);
+ m_menubar1->EnableTop(1, false);
+ m_menubar1->EnableTop(2, false);
m_bpButtonCmpConfig ->Disable();
- m_panelFilter ->Disable();
- m_panelConfig ->Disable();
m_bpButtonSyncConfig ->Disable();
m_buttonSync ->Disable();
- m_gridMainL ->Disable();
- m_gridMainC ->Disable();
- m_gridMainR ->Disable();
+ m_panelDirectoryPairs->Disable();
+ m_panelCenter ->Disable();
+ m_panelViewFilter ->Disable();
+ m_panelFilter ->Disable();
+ m_panelConfig ->Disable();
m_panelStatistics ->Disable();
m_gridNavi ->Disable();
- m_panelDirectoryPairs->Disable();
- m_splitterMain ->Disable();
- m_menubar1->EnableTop(0, false);
- m_menubar1->EnableTop(1, false);
- m_menubar1->EnableTop(2, false);
if (enableAbort)
{
@@ -1492,22 +1492,19 @@ void MainDialog::enableAllElements()
EnableCloseButton(true);
- m_panelViewFilter ->Enable();
+ m_menubar1->EnableTop(0, true);
+ m_menubar1->EnableTop(1, true);
+ m_menubar1->EnableTop(2, true);
m_bpButtonCmpConfig ->Enable();
- m_panelFilter ->Enable();
- m_panelConfig ->Enable();
m_bpButtonSyncConfig ->Enable();
m_buttonSync ->Enable();
- m_gridMainL ->Enable();
- m_gridMainC ->Enable();
- m_gridMainR ->Enable();
+ m_panelDirectoryPairs->Enable();
+ m_panelCenter ->Enable();
+ m_panelViewFilter ->Enable();
+ m_panelFilter ->Enable();
+ m_panelConfig ->Enable();
m_panelStatistics ->Enable();
m_gridNavi ->Enable();
- m_panelDirectoryPairs->Enable();
- m_splitterMain ->Enable();
- m_menubar1->EnableTop(0, true);
- m_menubar1->EnableTop(1, true);
- m_menubar1->EnableTop(2, true);
//show compare button
m_buttonCancel->Disable();
@@ -1517,6 +1514,9 @@ void MainDialog::enableAllElements()
m_panelTopButtons->Layout();
m_panelTopButtons->Enable();
+
+ //at least wxWidgets on OS X fails to do this after enabling:
+ Refresh();
}
@@ -1540,12 +1540,14 @@ void MainDialog::OnResizeConfigPanel(wxEvent& event)
event.Skip();
}
+
void MainDialog::OnResizeViewPanel(wxEvent& event)
{
updateSizerOrientation(*bSizerViewFilter, *m_panelViewFilter, 1.0);
event.Skip();
}
+
void MainDialog::OnResizeStatisticsPanel(wxEvent& event)
{
//updateSizerOrientation(*bSizerStatistics, *m_panelStatistics);
@@ -1619,19 +1621,19 @@ void MainDialog::onTreeButtonEvent(wxKeyEvent& event)
{
case WXK_NUMPAD_LEFT:
case WXK_LEFT: //ALT + <-
- setSyncDirManually(getTreeSelection(), SYNC_DIR_LEFT);
+ setSyncDirManually(getTreeSelection(), SyncDirection::LEFT);
return;
case WXK_NUMPAD_RIGHT:
case WXK_RIGHT: //ALT + ->
- setSyncDirManually(getTreeSelection(), SYNC_DIR_RIGHT);
+ setSyncDirManually(getTreeSelection(), SyncDirection::RIGHT);
return;
case WXK_NUMPAD_UP:
case WXK_NUMPAD_DOWN:
case WXK_UP: /* ALT + /|\ */
case WXK_DOWN: /* ALT + \|/ */
- setSyncDirManually(getTreeSelection(), SYNC_DIR_NONE);
+ setSyncDirManually(getTreeSelection(), SyncDirection::NONE);
return;
}
@@ -1704,19 +1706,19 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide)
{
case WXK_NUMPAD_LEFT:
case WXK_LEFT: //ALT + <-
- setSyncDirManually(getGridSelection(), SYNC_DIR_LEFT);
+ setSyncDirManually(getGridSelection(), SyncDirection::LEFT);
return;
case WXK_NUMPAD_RIGHT:
case WXK_RIGHT: //ALT + ->
- setSyncDirManually(getGridSelection(), SYNC_DIR_RIGHT);
+ setSyncDirManually(getGridSelection(), SyncDirection::RIGHT);
return;
case WXK_NUMPAD_UP:
case WXK_NUMPAD_DOWN:
case WXK_UP: /* ALT + /|\ */
case WXK_DOWN: /* ALT + \|/ */
- setSyncDirManually(getGridSelection(), SYNC_DIR_NONE);
+ setSyncDirManually(getGridSelection(), SyncDirection::NONE);
return;
}
@@ -1920,18 +1922,18 @@ void MainDialog::onNaviGridContext(GridClickEvent& event)
return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ?
selection[0]->testSyncOperation(dir) : soDefault));
};
- const wxBitmap opRight = getImage(SYNC_DIR_RIGHT, SO_OVERWRITE_RIGHT);
- const wxBitmap opNone = getImage(SYNC_DIR_NONE, SO_DO_NOTHING );
- const wxBitmap opLeft = getImage(SYNC_DIR_LEFT, SO_OVERWRITE_LEFT );
+ const wxBitmap opRight = getImage(SyncDirection::RIGHT, SO_OVERWRITE_RIGHT);
+ const wxBitmap opNone = getImage(SyncDirection::NONE, SO_DO_NOTHING );
+ const wxBitmap opLeft = getImage(SyncDirection::LEFT, SO_OVERWRITE_LEFT );
wxString shortCutLeft = L"\tAlt+Left";
wxString shortCutRight = L"\tAlt+Right";
if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
std::swap(shortCutLeft, shortCutRight);
- menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight);
- menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone);
- menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft);
+ menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SyncDirection::RIGHT); }, &opRight);
+ menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SyncDirection::NONE); }, &opNone);
+ menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SyncDirection::LEFT); }, &opLeft);
//Gtk needs a direction, "<-", because it has no context menu icons!
//Gtk requires "no spaces" for shortcut identifiers!
menu.addSeparator();
@@ -2012,18 +2014,18 @@ void MainDialog::onMainGridContextRim(bool leftSide)
return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ?
selection[0]->testSyncOperation(dir) : soDefault));
};
- const wxBitmap opRight = getImage(SYNC_DIR_RIGHT, SO_OVERWRITE_RIGHT);
- const wxBitmap opNone = getImage(SYNC_DIR_NONE, SO_DO_NOTHING );
- const wxBitmap opLeft = getImage(SYNC_DIR_LEFT, SO_OVERWRITE_LEFT );
+ const wxBitmap opRight = getImage(SyncDirection::RIGHT, SO_OVERWRITE_RIGHT);
+ const wxBitmap opNone = getImage(SyncDirection::NONE, SO_DO_NOTHING );
+ const wxBitmap opLeft = getImage(SyncDirection::LEFT, SO_OVERWRITE_LEFT );
wxString shortCutLeft = L"\tAlt+Left";
wxString shortCutRight = L"\tAlt+Right";
if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
std::swap(shortCutLeft, shortCutRight);
- menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight);
- menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone);
- menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft);
+ menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SyncDirection::RIGHT); }, &opRight);
+ menu.addItem(_("Set direction:") + L" -" L"\tAlt+Down", [this, &selection] { setSyncDirManually(selection, SyncDirection::NONE); }, &opNone);
+ menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SyncDirection::LEFT); }, &opLeft);
//Gtk needs a direction, "<-", because it has no context menu icons!
//Gtk requires "no spaces" for shortcut identifiers!
menu.addSeparator();
@@ -2482,7 +2484,7 @@ void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filen
std::vector<Zstring> missingFiles;
auto itFut = fileEx.begin();
- for (auto it = filenames.begin(); it != filenames.end(); ++it, ++itFut)
+ for (auto it = filenames.begin(); it != filenames.end(); ++it, (void)++itFut) //void: prevent ADL from dragging in boost's ,-overload: "MSVC warning C4913: user defined binary operator ',' exists but no overload could convert all operands"
if (itFut->is_ready() && !itFut->get()) //remove only files that are confirmed to be non-existent
missingFiles.push_back(*it);
@@ -2606,15 +2608,16 @@ bool MainDialog::trySaveConfig(const Zstring* fileNameGui) //return true if save
}
else
{
- wxString defaultFileName = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? toWx(activeConfigFiles[0]) : L"SyncSettings.ffs_gui";
+ Zstring defaultFileName = activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName() ? activeConfigFiles[0] : Zstr("SyncSettings.ffs_gui");
//attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config!
- if (endsWith(defaultFileName, L".ffs_batch"))
- replace(defaultFileName, L".ffs_batch", L".ffs_gui", false);
+ if (endsWith(defaultFileName, Zstr(".ffs_batch")))
+ replace(defaultFileName, Zstr(".ffs_batch"), Zstr(".ffs_gui"), false);
wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak!
wxEmptyString,
- wxEmptyString,
- defaultFileName,
+ //OS X really needs dir/file separated like this:
+ utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found
+ utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found
wxString(L"FreeFileSync (*.ffs_gui)|*.ffs_gui") + L"|" +_("All files") + L" (*.*)|*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
@@ -2634,7 +2637,7 @@ bool MainDialog::trySaveConfig(const Zstring* fileNameGui) //return true if save
}
catch (const xmlAccess::FfsXmlError& e)
{
- wxMessageBox(e.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(e.toString().c_str(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR, this);
return false;
}
}
@@ -2667,15 +2670,16 @@ bool MainDialog::trySaveBatchConfig(const Zstring* fileNameBatch)
globalCfg.gui.onCompletionHistoryMax))
return false;
- wxString defaultFileName = !activeCfgFilename.empty() ? toWx(activeCfgFilename) : L"BatchRun.ffs_batch";
+ Zstring defaultFileName = !activeCfgFilename.empty() ? activeCfgFilename : Zstr("BatchRun.ffs_batch");
//attention: activeConfigFiles may be a *.ffs_gui file! We don't want to overwrite it with a BATCH config!
- if (endsWith(defaultFileName, L".ffs_gui"))
- replace(defaultFileName, L".ffs_gui", L".ffs_batch");
+ if (endsWith(defaultFileName, Zstr(".ffs_gui")))
+ replace(defaultFileName, Zstr(".ffs_gui"), Zstr(".ffs_batch"));
wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak!
wxEmptyString,
- wxEmptyString,
- defaultFileName,
+ //OS X really needs dir/file separated like this:
+ utfCvrtTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR)), //default dir; empty string if / not found
+ utfCvrtTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR)), //default file; whole string if / not found
_("FreeFileSync batch") + L" (*.ffs_batch)|*.ffs_batch" + L"|" +_("All files") + L" (*.*)|*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
@@ -2693,7 +2697,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* fileNameBatch)
}
catch (const xmlAccess::FfsXmlError& e)
{
- wxMessageBox(e.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(e.toString().c_str(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR, this);
return false;
}
}
@@ -2710,7 +2714,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
if (!activeCfgFilename.empty())
//only if check is active and non-default config file loaded
{
- bool neverSave = !globalCfg.optDialogs.popupOnConfigChange;
+ bool dontAskAgain = false;
switch (showQuestionDlg(this,
ReturnQuestionDlg::BUTTON_YES | ReturnQuestionDlg::BUTTON_NO | ReturnQuestionDlg::BUTTON_CANCEL,
@@ -2718,7 +2722,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
QuestConfig().setCaption(toWx(activeCfgFilename)).
setLabelYes(_("&Save")).
setLabelNo(_("Do&n't save")).
- showCheckBox(neverSave, _("Never save changes"), ReturnQuestionDlg::BUTTON_YES)))
+ showCheckBox(dontAskAgain, _("Never save changes"), ReturnQuestionDlg::BUTTON_YES)))
{
case ReturnQuestionDlg::BUTTON_YES:
@@ -2736,7 +2740,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
}
case ReturnQuestionDlg::BUTTON_NO:
- globalCfg.optDialogs.popupOnConfigChange = !neverSave;
+ globalCfg.optDialogs.popupOnConfigChange = !dontAskAgain;
break;
case ReturnQuestionDlg::BUTTON_CANCEL:
@@ -2759,7 +2763,7 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event)
wxFileDialog filePicker(this,
wxEmptyString,
- toWx(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR)), //set default dir: empty string if "activeConfigFiles" is empty or has no path separator
+ utfCvrtTo<wxString>(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR)), //set default dir: empty string if "activeConfigFiles" is empty or has no path separator
wxEmptyString,
wxString(L"FreeFileSync (*.ffs_gui;*.ffs_batch)|*.ffs_gui;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*",
wxFD_OPEN | wxFD_MULTIPLE);
@@ -2866,12 +2870,12 @@ bool MainDialog::loadConfiguration(const std::vector<Zstring>& filenames)
{
if (error.getSeverity() == xmlAccess::FfsXmlError::WARNING)
{
- wxMessageBox(error.toString(), _("Warning"), wxOK | wxICON_WARNING, this);
+ wxMessageBox(error.toString(), L"FreeFileSync - " + _("Warning"), wxOK | wxICON_WARNING, this);
setConfig(newGuiCfg, filenames);
setLastUsedConfig(filenames, xmlAccess::XmlGuiConfig()); //simulate changed config due to parsing errors
}
else
- wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(error.toString(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR, this);
return false;
}
}
@@ -3308,7 +3312,8 @@ void MainDialog::OnCompare(wxCommandEvent& event)
clearGrid(); //avoid memory peak by clearing old data first
disableAllElements(true); //CompareStatusHandler will internally process Window messages, so avoid unexpected callbacks!
- ZEN_ON_SCOPE_EXIT(updateUiNow(); enableAllElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks
+ auto app = wxTheApp; //fix lambda/wxWigets/VC fuck up
+ ZEN_ON_SCOPE_EXIT(app->Yield(); enableAllElements()); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks
try
{
@@ -4093,12 +4098,11 @@ void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event)
void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
{
//get a filename
- const wxString defaultFileName = L"FileList.csv"; //proposal
wxFileDialog filePicker(this, //creating this on freestore leads to memleak!
wxEmptyString,
wxEmptyString,
- defaultFileName,
- _("Comma separated list") + L" (*.csv)|*.csv" + L"|" +_("All files") + L" (*.*)|*",
+ L"FileList.csv", //default file name
+ _("Comma-separated values") + L" (*.csv)|*.csv" + L"|" +_("All files") + L" (*.*)|*",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (filePicker.ShowModal() != wxID_OK)
return;
@@ -4220,7 +4224,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
}
catch (const FileError& e)
{
- wxMessageBox(e.toString(), _("Error"), wxOK | wxICON_ERROR, this);
+ wxMessageBox(e.toString(), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR, this);
}
}
}
@@ -4288,7 +4292,7 @@ void MainDialog::switchProgramLanguage(int langID)
newGlobalCfg.programLanguage = langID;
//show new dialog, then delete old one
- MainDialog::create(getConfig(), activeConfigFiles, newGlobalCfg, false);
+ MainDialog::create(getConfig(), activeConfigFiles, &newGlobalCfg, false);
//we don't use Close():
//1. we don't want to show the prompt to save current config in OnClose()
diff --git a/ui/main_dlg.h b/ui/main_dlg.h
index 5575cefb..b655d95e 100644
--- a/ui/main_dlg.h
+++ b/ui/main_dlg.h
@@ -29,16 +29,15 @@ class CompareProgressDialog;
class MainDialog : public MainDialogGenerated
{
public:
- //default behavior, application start
- static void create(const std::vector<Zstring>& cfgFileNames); //cfgFileNames empty: restore last config; non-empty load/merge given set of config files
+ //default behavior, application start, restores last used config
+ static void create();
- //load dynamically assembled config
- static void create(const xmlAccess::XmlGuiConfig& guiCfg, bool startComparison);
-
- //when switching language or switching from batch run to GUI on warnings
+ //when loading dynamically assembled config,
+ //when switching language,
+ //or switching from batch run to GUI on warnings
static void create(const xmlAccess::XmlGuiConfig& guiCfg,
const std::vector<Zstring>& referenceFiles,
- const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit
+ const xmlAccess::XmlGlobalSettings* globalSettings, //optional: take over ownership => save on exit
bool startComparison);
void disableAllElements(bool enableAbort); //dis-/enables all elements (except abort button) that might receive user input
@@ -47,11 +46,6 @@ public:
void onQueryEndSession(); //last chance to do something useful before killing the application!
private:
- static void create_impl(const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<Zstring>& referenceFiles,
- const xmlAccess::XmlGlobalSettings& globalSettings,
- bool startComparison);
-
MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit
diff --git a/ui/msg_popup.h b/ui/msg_popup.h
index 5e7ea97f..d93b5002 100644
--- a/ui/msg_popup.h
+++ b/ui/msg_popup.h
@@ -7,6 +7,7 @@
#ifndef MESSAGEPOPUP_H_820780154723456
#define MESSAGEPOPUP_H_820780154723456
+#include <zen/string_tools.h>
#include <wx/window.h>
#include <wx/string.h>
@@ -66,9 +67,11 @@ class QuestConfig
public:
QuestConfig() : checkBoxValue(), disabledButtonsWhenChecked_() {}
QuestConfig& setCaption (const wxString& label) { caption = label; return *this; }
- QuestConfig& setLabelYes(const wxString& label) { labelYes = label; return *this; }
- QuestConfig& setLabelNo (const wxString& label) { labelNo = label; return *this; }
- QuestConfig& showCheckBox(bool& value, const wxString& label, int disabledButtonsWhenChecked) { checkBoxValue = &value; checkBoxLabel = label; disabledButtonsWhenChecked_ = disabledButtonsWhenChecked; return *this; }
+ QuestConfig& setLabelYes(const wxString& label) { assert(contains(label, L"&")); labelYes = label; return *this; }
+ QuestConfig& setLabelNo (const wxString& label) { assert(contains(label, L"&")); labelNo = label; return *this; }
+ QuestConfig& showCheckBox(bool& value,
+ const wxString& label,
+ int disabledButtonsWhenChecked) { assert(contains(label, L"&")); checkBoxValue = &value; checkBoxLabel = label; disabledButtonsWhenChecked_ = disabledButtonsWhenChecked; return *this; }
private:
friend class ::QuestionDlg;
diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp
index 732b21c7..fd1c305d 100644
--- a/ui/progress_indicator.cpp
+++ b/ui/progress_indicator.cpp
@@ -26,6 +26,7 @@
#include <wx+/font_size.h>
#include <wx+/std_button_order.h>
#include <zen/file_handling.h>
+#include <zen/thread.h>
#include "gui_generated.h"
#include "../lib/ffs_paths.h"
#include "../lib/resources.h"
@@ -33,10 +34,14 @@
#include "tray_icon.h"
#include "taskbar.h"
#include "exec_finished_box.h"
-//#include <wx/msgdlg.h>
+
+#include <wx/msgdlg.h>
using namespace zen;
+warn_static("remove after test")
+#include <zen/perf.h>
+
namespace
{
@@ -51,7 +56,7 @@ const int WINDOW_BYTES_PER_SEC = 5000; //
class CompareProgressDialog::Pimpl : public CompareProgressDlgGenerated
{
public:
- Pimpl(wxTopLevelWindow& parentWindow);
+ Pimpl(wxFrame& parentWindow);
void init(const Statistics& syncStat); //constructor/destructor semantics, but underlying Window is reused
void finalize(); //
@@ -60,7 +65,7 @@ public:
void updateStatusPanelNow();
private:
- wxTopLevelWindow& parentWindow_;
+ wxFrame& parentWindow_;
wxString titleTextBackup;
wxStopWatch timeElapsed;
@@ -75,7 +80,7 @@ private:
};
-CompareProgressDialog::Pimpl::Pimpl(wxTopLevelWindow& parentWindow) :
+CompareProgressDialog::Pimpl::Pimpl(wxFrame& parentWindow) :
CompareProgressDlgGenerated(&parentWindow),
parentWindow_(parentWindow),
binCompStartMs(0),
@@ -214,11 +219,13 @@ void CompareProgressDialog::Pimpl::updateStatusPanelNow()
perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow);
//current speed -> Win 7 copy uses 1 sec update interval instead
- setText(*m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged);
+ Opt<std::wstring> bps = perf->getBytesPerSecond();
+ setText(*m_staticTextSpeed, bps ? *bps : L"-", &layoutChanged);
//remaining time: display with relative error of 10% - based on samples taken every 0.5 sec only
//-> call more often than once per second to correctly show last few seconds countdown, but don't call too often to avoid occasional jitter
- setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged);
+ Opt<std::wstring> rt = perf->getRemainingTime(to<double>(dataTotal - dataCurrent));
+ setText(*m_staticTextRemTime, rt ? *rt : L"-", &layoutChanged);
}
}
break;
@@ -238,17 +245,17 @@ void CompareProgressDialog::Pimpl::updateStatusPanelNow()
wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") :
wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged);
- //do the ui update
if (layoutChanged)
bSizer42->Layout();
- updateUiNow();
+ //do the ui update
+ wxTheApp->Yield();
}
//########################################################################################
//redirect to implementation
-CompareProgressDialog::CompareProgressDialog(wxTopLevelWindow& parentWindow) :
+CompareProgressDialog::CompareProgressDialog(wxFrame& parentWindow) :
pimpl(new Pimpl(parentWindow)) {} //owned by parentWindow
wxWindow* CompareProgressDialog::getAsWindow()
@@ -715,7 +722,7 @@ private:
}
catch (const std::bad_alloc& e)
{
- wxMessageBox(_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()), _("Error"), wxOK | wxICON_ERROR);
+ wxMessageBox(_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()), L"FreeFileSync - " + _("Error"), wxOK | wxICON_ERROR);
}
}
@@ -726,88 +733,212 @@ private:
namespace
{
-class GraphDataBytes : public GraphData
+class CurveDataStatistics : public SparseCurveData
{
public:
- void addRecord(double currentBytes, long timeMs)
+ CurveDataStatistics() : SparseCurveData(true), timeNow(0) {}
+
+ void clear() { samples.clear(); timeNow = 0; }
+
+ void addRecord(long timeNowMs, double value)
{
- data.insert(data.end(), std::make_pair(timeMs, currentBytes));
+ warn_static("review")
+ timeNow = timeNowMs;
+ if (!samples.empty() && samples.rbegin()->second == value)
+ return; //don't insert duplicate values
+
+ samples.insert(samples.end(), std::make_pair(timeNowMs, value)); //use fact that time is monotonously ascending
//documentation differs about whether "hint" should be before or after the to be inserted element!
//however "std::map<>::end()" is interpreted correctly by GCC and VS2010
- if (data.size() > MAX_BUFFER_SIZE) //limit buffer size
- data.erase(data.begin());
+ if (samples.size() > MAX_BUFFER_SIZE) //limit buffer size
+ samples.erase(samples.begin());
}
- void clear() { data.clear(); }
+private:
+ virtual std::pair<double, double> getRangeX() const final
+ {
+ if (samples.empty()) return std::make_pair(0.0, 0.0);
- virtual double getXBegin() const { return data.empty() ? 0 : data.begin()->first / 1000.0; } //need not start with 0, e.g. "binary comparison, graph reset, followed by sync"
- virtual double getXEnd () const { return data.empty() ? 0 : (--data.end())->first / 1000.0; }
+ double upperEndMs = std::max(timeNow, samples.rbegin()->first);
-private:
- static const size_t MAX_BUFFER_SIZE = 2500000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte
+ //request some additional width by 5% elapsed time to make graph recalibrate before hitting the right border
+ //caveat: graph for batch mode binary comparison does NOT start at elapsed time 0!! PHASE_COMPARING_CONTENT and PHASE_SYNCHRONIZING!
+ //=> consider width of current sample set!
+ upperEndMs += 0.05 *(upperEndMs - samples.begin()->first);
- virtual double getValue(double x) const //x: seconds since begin
+ return std::make_pair(samples.begin()->first / 1000.0, //need not start with 0, e.g. "binary comparison, graph reset, followed by sync"
+ upperEndMs / 1000.0);
+ }
+
+ virtual Opt<CurvePoint> getLessEq(double x) const final //x: seconds since begin
{
- auto it = data.lower_bound(x * 1000);
- if (it == data.end())
- return data.empty() ? 0 : (--data.end())->second;
- return it->second;
+ const long timex = std::floor(x * 1000);
+ //------ add artifical last sample value -------
+ if (!samples.empty() && samples.rbegin()->first < timeNow)
+ if (timeNow <= timex)
+ return CurvePoint(timeNow / 1000.0, samples.rbegin()->second);
+ //--------------------------------------------------
+
+ //find first key > x, then go one step back: => samples must be a std::map, NOT std::multimap!!!
+ auto it = samples.upper_bound(timex);
+ if (it == samples.begin())
+ return NoValue();
+ //=> samples not empty in this context
+ --it;
+ return CurvePoint(it->first / 1000.0, it->second);
}
- //example: two-element range is accessible within [0, 2)
- std::map<long, double> data;
+ virtual Opt<CurvePoint> getGreaterEq(double x) const final
+ {
+ const long timex = std::ceil(x * 1000);
+ //------ add artifical last sample value -------
+ if (!samples.empty() && samples.rbegin()->first < timeNow)
+ if (samples.rbegin()->first < timex && timex <= timeNow)
+ return CurvePoint(timeNow / 1000.0, samples.rbegin()->second);
+ //--------------------------------------------------
+
+ auto it = samples.lower_bound(timex);
+ if (it == samples.end())
+ return NoValue();
+ return CurvePoint(it->first / 1000.0, it->second);
+ }
+
+ static const size_t MAX_BUFFER_SIZE = 2500000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte
+
+ std::map<long, double> samples; //time, unit: [ms] !don't use std::multimap, see getLessEq()
+ long timeNow; //help create an artificial record at the end of samples to visualize current time!
};
-class GraphDataConstLine : public GraphData //a constant line
+class CurveDataCurrentValue : public CurveData
{
public:
- GraphDataConstLine() : value_(0) {}
+ CurveDataCurrentValue() : x(0), yCurrent_(0), yTotal_(0) {}
- void setValue(double val) { value_ = val; }
+ void setValue(long xTimeNowMs, double yCurrent, double yTotal) { x = xTimeNowMs / 1000.0; yCurrent_ = yCurrent; yTotal_ = yTotal; }
private:
- virtual double getValue(double x) const { return value_; }
- virtual double getXBegin() const { return -std::numeric_limits<double>::infinity(); }
- virtual double getXEnd () const { return std::numeric_limits<double>::infinity(); }
+ virtual std::pair<double, double> getRangeX() const final { return std::make_pair(x, x); } //conceptually just a vertical line!
- double value_;
+ virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const final
+ {
+
+ warn_static("remove after test")
+#if 0
+ if (yTotal_ > 100)
+ {
+points.push_back(CurvePoint(0.38552801074951426,0.3861846045528107));
+points.push_back(CurvePoint(-0.5565680734345084,1.793989720937398));
+points.push_back(CurvePoint(2.85210684934041,3.339141677944872));
+points.push_back(CurvePoint(0.45404408959926135,0.7810567713436095));
+points.push_back(CurvePoint(2.303978218542433,-0.6610850551966995));
+points.push_back(CurvePoint(-2.5606633797896112,-0.4035597290287872));
+points.push_back(CurvePoint(-0.5394390537220716,0.40335295963067147));
+
+
+ //points.push_back(CurvePoint(0.2885508231771302,-1.9264175407823294));
+ //points.push_back(CurvePoint(-1.9332518577512143,0.6244007597162101));
+ //points.push_back(CurvePoint(3.116299689813205,1.6973640131005165));
+ //points.push_back(CurvePoint(0.0,0.0));
+ //points.push_back(CurvePoint(-5.993091301993007,0.5231778112837284));
+ return;
+ }
+#endif
+
+
+
+
+
+
+ if (x <= maxX)
+ {
+ points.push_back(CurvePoint(x, 0));
+ points.push_back(CurvePoint(x, yCurrent_));
+ points.push_back(CurvePoint(maxX, yCurrent_));
+ points.push_back(CurvePoint(x, yCurrent_));
+ points.push_back(CurvePoint(x, yTotal_));
+ }
+ }
+
+ double x; //time elapsed in seconds
+ double yCurrent_; //current bytes/items
+ double yTotal_;
};
-inline
-double bestFit(double val, double low, double high) { return val < (high + low) / 2 ? low : high; }
+class CurveDataTotalValue : public CurveData
+{
+public:
+ CurveDataTotalValue () : x(0), yTotal_(0) {}
+
+ void setValue(long xTimeNowMs, double yTotal) { x = xTimeNowMs / 1000.0; yTotal_ = yTotal; }
+
+private:
+ virtual std::pair<double, double> getRangeX() const final { return std::make_pair(x, x); } //conceptually just a vertical line!
+
+ virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const final
+ {
+ if (x <= maxX)
+ {
+ points.push_back(CurvePoint(x, yTotal_));
+ points.push_back(CurvePoint(maxX, yTotal_));
+ }
+ }
+
+ double x; //time elapsed in seconds
+ double yTotal_;
+};
struct LabelFormatterBytes : public LabelFormatter
{
virtual double getOptimalBlockSize(double bytesProposed) const
{
- if (bytesProposed <= 0)
- return 0;
+ if (bytesProposed <= 1) //never smaller than 1 byte
+ return 1;
- bytesProposed *= 1.5; //enlarge block default size
+ bytesProposed *= 1.4; //enlarge block default size
//round to next number which is a convenient to read block size
const double k = std::floor(std::log(bytesProposed) / std::log(2.0));
const double e = std::pow(2.0, k);
if (numeric::isNull(e))
return 0;
- const double a = bytesProposed / e; //bytesProposed = a * 2^k with a in (1, 2)
- assert(1 < a && a < 2);
- return bestFit(a, 1, 2) * e;
+ const double a = bytesProposed / e; //bytesProposed = a * 2^k with a in [1, 2)
+ assert(1 <= a && a < 2);
+ const double steps[] = { 1, 2 };
+ return e * numeric::nearMatch(a, std::begin(steps), std::end(steps));
}
virtual wxString formatText(double value, double optimalBlockSize) const { return filesizeToShortString(Int64(value)); };
};
+struct LabelFormatterItemCount : public LabelFormatter
+{
+ virtual double getOptimalBlockSize(double itemsProposed) const
+ {
+ const double steps[] = { 1, 2, 5, 10 };
+ if (itemsProposed <= 10)
+ return numeric::nearMatch(itemsProposed, std::begin(steps), std::end(steps)); //similar to nextNiceNumber(), but without the 2.5 step!
+ return nextNiceNumber(itemsProposed);
+ }
+
+ virtual wxString formatText(double value, double optimalBlockSize) const
+ {
+ return toGuiString(numeric::round(value)); //not enough room for a "%x items" representation
+ };
+};
+
+
struct LabelFormatterTimeElapsed : public LabelFormatter
{
+ LabelFormatterTimeElapsed(bool drawLabel) : drawLabel_(drawLabel) {}
+
virtual double getOptimalBlockSize(double secProposed) const
{
- const double stepsSec[] = { 10, 20, 30, 60 }; //10 sec: minimum block size; no 15: avoid flicker between 10<->15<->20 sec blocks
+ const double stepsSec[] = { 20, 30, 60 }; //20 sec: minimum block size; (no 15: avoid flicker between 10<->15<->20 sec blocks)
if (secProposed <= 60)
return numeric::nearMatch(secProposed, std::begin(stepsSec), std::end(stepsSec));
@@ -816,215 +947,302 @@ struct LabelFormatterTimeElapsed : public LabelFormatter
return 60.0 * numeric::nearMatch(secProposed / 60, std::begin(stepsMin), std::end(stepsMin));
if (secProposed <= 3600 * 24)
- return nextNiceNumber(secProposed / 3600) * 3600;
+ return nextNiceNumber(secProposed / 3600) * 3600; //round up to full hours
- return nextNiceNumber(secProposed / (24 * 3600)) * 24 * 3600; //round up to full days
+ return nextNiceNumber(secProposed / (24 * 3600)) * 24 * 3600; //round to full days
}
virtual wxString formatText(double timeElapsed, double optimalBlockSize) const
{
+ if (!drawLabel_) return wxString();
return timeElapsed < 60 ?
replaceCpy(_P("1 sec", "%x sec", numeric::round(timeElapsed)), L"%x", zen::numberTo<std::wstring>(numeric::round(timeElapsed))) :
timeElapsed < 3600 ?
wxTimeSpan::Seconds(timeElapsed).Format( L"%M:%S") :
wxTimeSpan::Seconds(timeElapsed).Format(L"%H:%M:%S");
}
-};
-//void fitHeight(wxTopLevelWindow& wnd)
-//{
-// if (wnd.IsMaximized())
-// return;
-// //Fit() height only:
-// int width = wnd.GetSize().GetWidth();
-// wnd.Fit();
-// int height = wnd.GetSize().GetHeight();
-// wnd.SetSize(wxSize(width, height));
-//}
+private:
+ bool drawLabel_;
+};
}
-class SyncProgressDialogImpl : private SyncProgressDlgGenerated, public SyncProgressDialog
+template <class TopLevelDialog> //can be a wxFrame or wxDialog
+class SyncProgressDialogImpl : private TopLevelDialog, public SyncProgressDialog
+/*we need derivation, not composition!
+ 1. SyncProgressDialogImpl IS a wxFrame/wxDialog
+ 2. implement virtual ~wxFrame()
+ 3. event handling below assumes lifetime is larger-equal than wxFrame's
+*/
{
public:
- SyncProgressDialogImpl(AbortCallback& abortCb,
+ SyncProgressDialogImpl(long style, //wxFrame/wxDialog style
+ const std::function<wxFrame*(TopLevelDialog& progDlg)>& getTaskbarFrame,
+ AbortCallback& abortCb,
const std::function<void()>& notifyWindowTerminate,
const Statistics& syncStat,
- wxTopLevelWindow* parentWindow,
+ wxFrame* parentFrame,
bool showProgress,
const wxString& jobName,
const std::wstring& execWhenFinished,
std::vector<std::wstring>& execFinishedHistory);
- ~SyncProgressDialogImpl();
+ virtual ~SyncProgressDialogImpl();
//call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater
virtual void processHasFinished(SyncResult resultId, const ErrorLog& log);
virtual void closeWindowDirectly();
- virtual wxWindow* getAsWindow() { return this; }
+ virtual wxWindow* getWindowIfVisible() { return this->IsShown() ? this : nullptr; }
+ //workaround OS X bug: if "this" is used as parent window for a modal dialog then this dialog will erroneously un-hide its parent!
+
virtual void initNewPhase();
virtual void notifyProgressChange();
- virtual void updateGui() { updateGuiInt(true); }
-
- virtual std::wstring getExecWhenFinishedCommand() const;
+ virtual void updateGui() {
+
+
+ warn_static("remove after test")
+#if 0
+ static bool init = false;
+ static wxStopWatch sw;
+ if (!init)
+ {
+ init = true;
+ sw.Start();
+ }
+ if (sw.Time() > 1000 * 10)
+ {
+ PERF_START
+ for (int i = 0; i < 10000; ++i)
+ //for (int i = 0; i < 1000000; ++i)
+ updateGuiInt(true);
+ sw.Start();
+ sw.Pause();
+ }
+#endif
+
+
+
+
+
+
+ updateGuiInt(true); }
+
+ virtual std::wstring getExecWhenFinishedCommand() const { return pnl.m_comboBoxExecFinished->getValue(); }
virtual void stopTimer() //halt all internal counters!
{
- m_animCtrlSyncing->Stop();
+ pnl.m_animCtrlSyncing->Stop();
timeElapsed.Pause ();
}
virtual void resumeTimer()
{
- m_animCtrlSyncing->Play();
+ pnl.m_animCtrlSyncing->Play();
timeElapsed.Resume();
}
private:
void updateGuiInt(bool allowYield);
- void minimizeToTray();
void OnKeyPressed(wxKeyEvent& event);
- virtual void OnOkay (wxCommandEvent& event);
- virtual void OnPause (wxCommandEvent& event);
- virtual void OnCancel (wxCommandEvent& event);
- virtual void OnClose (wxCloseEvent& event);
- virtual void OnIconize(wxIconizeEvent& event);
-
- void updateDialogStatus();
+ void OnOkay (wxCommandEvent& event);
+ void OnPause (wxCommandEvent& event);
+ void OnCancel (wxCommandEvent& event);
+ void OnClose (wxCloseEvent& event);
+ void OnIconize(wxIconizeEvent& event);
+ void OnMinimizeToTray(wxCommandEvent& event) { minimizeToTray(); }
+ void minimizeToTray();
void resumeFromSystray();
- void OnResumeFromTray(wxCommandEvent& event);
+ void updateDialogStatus();
void setExternalStatus(const wxString& status, const wxString& progress); //progress may be empty!
+ SyncProgressPanelGenerated& pnl; //wxPanel containing the GUI controls of *this
+
const wxString jobName_;
wxStopWatch timeElapsed;
- wxTopLevelWindow* mainDialog; //optional
+ wxFrame* parentFrame_; //optional
std::function<void()> notifyWindowTerminate_; //call once in OnClose(), NOT in destructor which is called far too late somewhere in wxWidgets main loop!
+ bool wereDead; //after wxWindow::Delete, which is equal to "delete this" on OS X!
+
//status variables
const Statistics* syncStat_; //
AbortCallback* abortCb_; //valid only while sync is running
bool paused_; //valid only while sync is running
SyncResult finalResult; //set after sync
- bool isZombie; //wxGTK sends iconize event *after* wxWindow::Destroy, sigh...
-
//remaining time
std::unique_ptr<PerfCheck> perf;
long lastStatCallSpeed; //used for calculating intervals between collecting perf samples
//help calculate total speed
long phaseStartMs; //begin of current phase in [ms]
- std::shared_ptr<GraphDataBytes> graphDataBytes;
- std::shared_ptr<GraphDataConstLine> graphDataBytesTotal;
+ std::shared_ptr<CurveDataStatistics> curveDataBytes;
+ std::shared_ptr<CurveDataStatistics> curveDataItems;
- wxString titelTextBackup;
+ std::shared_ptr<CurveDataTotalValue > curveDataBytesTotal;
+ std::shared_ptr<CurveDataCurrentValue> curveDataBytesCurrent;
+ std::shared_ptr<CurveDataTotalValue > curveDataItemsTotal;
+ std::shared_ptr<CurveDataCurrentValue> curveDataItemsCurrent;
+
+ wxString parentFrameTitleBackup;
std::unique_ptr<FfsTrayIcon> trayIcon; //optional: if filled all other windows should be hidden and conversely
std::unique_ptr<Taskbar> taskbar_;
};
-SyncProgressDialogImpl::SyncProgressDialogImpl(AbortCallback& abortCb,
- const std::function<void()>& notifyWindowTerminate,
- const Statistics& syncStat,
- wxTopLevelWindow* parentWindow,
- bool showProgress,
- const wxString& jobName,
- const std::wstring& execWhenFinished,
- std::vector<std::wstring>& execFinishedHistory) :
- SyncProgressDlgGenerated(parentWindow,
- wxID_ANY,
- parentWindow ? wxString() : (wxString(L"FreeFileSync - ") + _("Folder Comparison and Synchronization")),
- wxDefaultPosition, wxDefaultSize,
- parentWindow ?
- wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT : //wxTAB_TRAVERSAL is needed for standard button handling: wxID_OK/wxID_CANCEL
- wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL),
+template <class TopLevelDialog>
+SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxFrame/wxDialog style
+ const std::function<wxFrame*(TopLevelDialog& progDlg)>& getTaskbarFrame,
+ AbortCallback& abortCb,
+ const std::function<void()>& notifyWindowTerminate,
+ const Statistics& syncStat,
+ wxFrame* parentFrame,
+ bool showProgress,
+ const wxString& jobName,
+ const std::wstring& execWhenFinished,
+ std::vector<std::wstring>& execFinishedHistory) :
+ TopLevelDialog(parentFrame, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, style), //title is overwritten anyway in setExternalStatus()
+ pnl(*new SyncProgressPanelGenerated(this)), //ownership passed to "this"
jobName_ (jobName),
- mainDialog(parentWindow),
+ parentFrame_(parentFrame),
notifyWindowTerminate_(notifyWindowTerminate),
+ wereDead(false),
syncStat_ (&syncStat),
abortCb_ (&abortCb),
paused_ (false),
finalResult(RESULT_ABORTED), //dummy value
- isZombie(false),
lastStatCallSpeed(-1000000), //some big number
phaseStartMs(0)
{
+ assert_static((IsSameType<TopLevelDialog, wxFrame >::value ||
+ IsSameType<TopLevelDialog, wxDialog>::value));
+ assert((IsSameType<TopLevelDialog, wxFrame>::value == !parentFrame));
+
+ //finish construction of this dialog:
+ this->SetMinSize(wxSize(470, 280)); //== minimum size! no idea why SetMinSize() is not used...
+ wxBoxSizer* bSizer170 = new wxBoxSizer(wxVERTICAL);
+ bSizer170->Add(&pnl, 1, wxEXPAND);
+ this->SetSizer(bSizer170); //pass ownership
+ //this->Layout();
+ //bSizer170->Fit(this);
+ this->Centre(wxBOTH);
+
+ //lifetime of event sources is subset of this instance's lifetime => no wxEvtHandler::Disconnect() needed
+ this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler (SyncProgressDialogImpl<TopLevelDialog>::OnClose));
+ this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(SyncProgressDialogImpl<TopLevelDialog>::OnIconize));
+ this->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressed), nullptr, this);
+ pnl.m_buttonClose ->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnOkay ), NULL, this);
+ pnl.m_buttonPause ->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnPause ), NULL, this);
+ pnl.m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnCancel), NULL, this);
+ pnl.m_bpButtonMinimizeToTray->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnMinimizeToTray), NULL, this);
+
#ifdef ZEN_WIN
new MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this"
#endif
- assert(m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug??
+ assert(pnl.m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug??
- setRelativeFontSize(*m_staticTextPhase, 1.5);
+ setRelativeFontSize(*pnl.m_staticTextPhase, 1.5);
- if (mainDialog)
- titelTextBackup = mainDialog->GetTitle(); //save old title (will be used as progress indicator)
+ if (parentFrame_)
+ parentFrameTitleBackup = parentFrame_->GetTitle(); //save old title (will be used as progress indicator)
- m_animCtrlSyncing->SetAnimation(GlobalResources::instance().aniWorking);
- m_animCtrlSyncing->Play();
+ pnl.m_animCtrlSyncing->SetAnimation(GlobalResources::instance().aniWorking);
+ pnl.m_animCtrlSyncing->Play();
- SetIcon(GlobalResources::instance().programIconFFS);
-
- //initialize gauge
- m_gauge1->SetRange(GAUGE_FULL_RANGE);
- m_gauge1->SetValue(0);
+ this->SetIcon(GlobalResources::instance().programIconFFS);
- EnableCloseButton(false); //this is NOT honored on OS X or during system shutdown on Windows!
-
- if (IsShown()) //don't steal focus when starting in sys-tray!
- m_buttonCancel->SetFocus();
+ this->EnableCloseButton(false); //this is NOT honored on OS X or during system shutdown on Windows!
timeElapsed.Start(); //measure total time
- try //try to get access to Windows 7/Ubuntu taskbar
- {
- taskbar_ = make_unique<Taskbar>(mainDialog ? *static_cast<wxTopLevelWindow*>(mainDialog) : *this);
- }
- catch (const TaskbarNotAvailable&) {}
+ if (wxFrame* frame = getTaskbarFrame(*this))
+ try //try to get access to Windows 7/Ubuntu taskbar
+ {
+ taskbar_ = make_unique<Taskbar>(*frame); //throw TaskbarNotAvailable
+ }
+ catch (const TaskbarNotAvailable&) {}
//hide "processed" statistics until end of process
- m_notebookResult ->Hide();
- m_staticTextLabelItemsProc->Show(false);
- bSizerItemsProc ->Show(false);
- m_buttonClose ->Show(false);
+ pnl.m_notebookResult ->Hide();
+ pnl.m_panelItemsProcessed->Hide();
+ pnl.m_buttonClose ->Show(false);
//set std order after button visibility was set
- setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonPause).setCancel(m_buttonCancel));
+ setStandardButtonOrder(*pnl.bSizerStdButtons, StdButtons().setAffirmative(pnl.m_buttonPause).setCancel(pnl.m_buttonCancel));
- //register key event
- Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressed), nullptr, this);
+ pnl.m_bpButtonMinimizeToTray->SetBitmapLabel(getResourceImage(L"minimize_to_tray"));
//init graph
- graphDataBytes = std::make_shared<GraphDataBytes>();
- graphDataBytesTotal = std::make_shared<GraphDataConstLine>();
+ curveDataBytes = std::make_shared<CurveDataStatistics>();
+ curveDataItems = std::make_shared<CurveDataStatistics>();
+ curveDataBytesTotal = std::make_shared<CurveDataTotalValue>();
+ curveDataBytesCurrent = std::make_shared<CurveDataCurrentValue>();
+ curveDataItemsTotal = std::make_shared<CurveDataTotalValue>();
+ curveDataItemsCurrent = std::make_shared<CurveDataCurrentValue>();
+
+ const int xLabelHeight = 18; //we need to use the same height for both graphs to make sure they stretch evenly
+ const int yLabelWidth = 70;
+ pnl.m_panelGraphBytes->setAttributes(Graph2D::MainAttributes().
+ setLabelX(Graph2D::X_LABEL_BOTTOM, xLabelHeight, std::make_shared<LabelFormatterTimeElapsed>(true)).
+ setLabelY(Graph2D::Y_LABEL_RIGHT, yLabelWidth, std::make_shared<LabelFormatterBytes>()).
+ setSelectionMode(Graph2D::SELECT_NONE));
+
+ pnl.m_panelGraphItems->setAttributes(Graph2D::MainAttributes().
+ setLabelX(Graph2D::X_LABEL_BOTTOM, xLabelHeight, std::make_shared<LabelFormatterTimeElapsed>(false)).
+ setLabelY(Graph2D::Y_LABEL_RIGHT, yLabelWidth, std::make_shared<LabelFormatterItemCount>()).
+ setSelectionMode(Graph2D::SELECT_NONE));
+
+ pnl.m_panelGraphBytes->setCurve(curveDataBytes, Graph2D::CurveAttributes().setLineWidth(2).
+ fillCurveArea(wxColor(205, 255, 202)). //faint green
+ setColor (wxColor( 20, 200, 0))); //medium green
+
+ pnl.m_panelGraphItems->setCurve(curveDataItems, Graph2D::CurveAttributes().setLineWidth(2).
+ fillCurveArea(wxColor(198, 206, 255)). //faint blue
+ setColor (wxColor( 90, 120, 255))); //medium blue
+
+ pnl.m_panelGraphBytes->addCurve(curveDataBytesTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(12, 128, 0))); //dark green
+ pnl.m_panelGraphBytes->addCurve(curveDataBytesCurrent, Graph2D::CurveAttributes().setLineWidth(1).setColor(wxColor(12, 128, 0))); //
+
+ pnl.m_panelGraphItems->addCurve(curveDataItemsTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(53, 25, 255))); //dark blue
+ pnl.m_panelGraphItems->addCurve(curveDataItemsCurrent, Graph2D::CurveAttributes().setLineWidth(1).setColor(wxColor(53, 25, 255))); //
+
+ //allow changing on completion command
+ pnl.m_comboBoxExecFinished->initHistory(execFinishedHistory, execFinishedHistory.size()); //-> we won't use addItemHistory() later
+ pnl.m_comboBoxExecFinished->setValue(execWhenFinished);
+
+ updateDialogStatus(); //null-status will be shown while waiting for dir locks
+
+
+ warn_static("remove after test")
+#if 0
+ pnl.m_panelGraphBytes->setAttributes(Graph2D::MainAttributes().setMinX(-1).setMaxX(1).setMinY(-1).setMaxY(1));
+ pnl.m_panelGraphBytes->setCurve(curveDataBytesCurrent, Graph2D::CurveAttributes().setLineWidth(6).setColor(wxColor(12, 128, 0))
+ .fillCurveArea(wxColor(198, 206, 255)) //faint blue
+ ); //
+#endif
+
+
+
- m_panelGraph->setAttributes(Graph2D::MainAttributes().
- setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()).
- setLabelY(Graph2D::Y_LABEL_RIGHT, 70, std::make_shared<LabelFormatterBytes>()).
- setSelectionMode(Graph2D::SELECT_NONE));
- m_panelGraph->setData(graphDataBytes,
- Graph2D::CurveAttributes().setLineWidth(2).
- setColor (wxColor( 0, 192, 0)). //medium green
- fillCurveArea(wxColor(192, 255, 192))); //faint green
- m_panelGraph->addData(graphDataBytesTotal, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(0, 64, 0))); //dark green
- //allow changing on completion command
- m_comboBoxExecFinished->initHistory(execFinishedHistory, execFinishedHistory.size()); //-> we won't use addItemHistory() later
- m_comboBoxExecFinished->setValue(execWhenFinished);
- updateDialogStatus(); //null-status will be shown while waiting for dir locks (if at all)
- Fit();
- Layout();
+
+ this->Fit();
+ pnl.Layout();
if (showProgress)
{
- Show();
+ this->Show();
+ pnl.m_buttonCancel->SetFocus(); //don't steal focus when starting in sys-tray!
+
//clear gui flicker, remove dummy texts: window must be visible to make this work!
updateGuiInt(true); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough
}
@@ -1033,24 +1251,25 @@ SyncProgressDialogImpl::SyncProgressDialogImpl(AbortCallback& abortCb,
}
-SyncProgressDialogImpl::~SyncProgressDialogImpl()
+template <class TopLevelDialog>
+SyncProgressDialogImpl<TopLevelDialog>::~SyncProgressDialogImpl()
{
- if (mainDialog)
+ if (parentFrame_)
{
- mainDialog->SetTitle(titelTextBackup); //restore title text
+ parentFrame_->SetTitle(parentFrameTitleBackup); //restore title text
//make sure main dialog is shown again if still "minimized to systray"! see SyncProgressDialog::closeWindowDirectly()
- if (mainDialog->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
- mainDialog->Iconize(false);
-
- mainDialog->Show();
+ parentFrame_->Show();
+ //if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
+ // parentFrame_->Iconize(false);
}
//our client is NOT expecting a second call via notifyWindowTerminate_()!
}
-void SyncProgressDialogImpl::OnKeyPressed(wxKeyEvent& event)
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::OnKeyPressed(wxKeyEvent& event)
{
const int keyCode = event.GetKeyCode();
if (keyCode == WXK_ESCAPE)
@@ -1058,15 +1277,15 @@ void SyncProgressDialogImpl::OnKeyPressed(wxKeyEvent& event)
wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED);
//simulate click on abort button
- if (m_buttonCancel->IsShown()) //delegate to "cancel" button if available
+ if (pnl.m_buttonCancel->IsShown()) //delegate to "cancel" button if available
{
- if (wxEvtHandler* handler = m_buttonCancel->GetEventHandler())
+ if (wxEvtHandler* handler = pnl.m_buttonCancel->GetEventHandler())
handler->ProcessEvent(dummy);
return;
}
- else if (m_buttonClose->IsShown())
+ else if (pnl.m_buttonClose->IsShown())
{
- if (wxEvtHandler* handler = m_buttonClose->GetEventHandler())
+ if (wxEvtHandler* handler = pnl.m_buttonClose->GetEventHandler())
handler->ProcessEvent(dummy);
return;
}
@@ -1076,29 +1295,33 @@ void SyncProgressDialogImpl::OnKeyPressed(wxKeyEvent& event)
}
-void SyncProgressDialogImpl::initNewPhase()
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::initNewPhase()
{
updateDialogStatus(); //evaluates "syncStat_->currentPhase()"
- //reset graph (e.g. after binary comparison)
- graphDataBytes->clear();
+ //reset graphs (e.g. after binary comparison)
+ curveDataBytesTotal ->setValue(0, 0);
+ curveDataBytesCurrent->setValue(0, 0, 0);
+ curveDataItemsTotal ->setValue(0, 0);
+ curveDataItemsCurrent->setValue(0, 0, 0);
+ curveDataBytes->clear();
+ curveDataItems->clear();
+
notifyProgressChange();
//start new measurement
perf = make_unique<PerfCheck>(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC);
- lastStatCallSpeed = -1000000; //some big number
+ lastStatCallSpeed = -1000000; //some big number
phaseStartMs = timeElapsed.Time();
- //set to 0 even if totalDataToProcess is 0: due to a bug in wxGauge::SetValue, it doesn't change to determinate mode when setting the old value again
- //so give updateGui() a chance to set a different value
- m_gauge1->SetValue(0);
-
updateGuiInt(false);
}
-void SyncProgressDialogImpl::notifyProgressChange() //noexcept!
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::notifyProgressChange() //noexcept!
{
if (syncStat_) //sync running
switch (syncStat_->currentPhase())
@@ -1109,13 +1332,9 @@ void SyncProgressDialogImpl::notifyProgressChange() //noexcept!
break;
case ProcessCallback::PHASE_COMPARING_CONTENT:
case ProcessCallback::PHASE_SYNCHRONIZING:
- {
- const double currentData = to<double>(syncStat_->getDataCurrent(syncStat_->currentPhase()));
-
- //add sample for perf measurements + calc. of remaining time
- graphDataBytes->addRecord(currentData, timeElapsed.Time());
- }
- break;
+ curveDataBytes->addRecord(timeElapsed.Time(), to<double>(syncStat_->getDataCurrent (syncStat_->currentPhase())));
+ curveDataItems->addRecord(timeElapsed.Time(), syncStat_->getObjectsCurrent(syncStat_->currentPhase()));
+ break;
}
}
@@ -1140,7 +1359,7 @@ Zorder evaluateZorder(const wxWindow& top, const wxWindow& bottom)
if (hAbove == hTop)
return ZORDER_CORRECT;
- for (HWND hAbove = ::GetNextWindow(hTop, GW_HWNDPREV); hAbove; hAbove = ::GetNextWindow(hAbove, GW_HWNDPREV))
+ for (HWND hAbove = hTop; hAbove; hAbove = ::GetNextWindow(hAbove, GW_HWNDPREV))
if (hAbove == hBottom)
return ZORDER_WRONG;
@@ -1183,7 +1402,8 @@ std::wstring getDialogPhaseText(const Statistics* syncStat, bool paused, SyncPro
}
-void SyncProgressDialogImpl::setExternalStatus(const wxString& status, const wxString& progress) //progress may be empty!
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::setExternalStatus(const wxString& status, const wxString& progress) //progress may be empty!
{
//sys tray: order "top-down": jobname, status, progress
wxString newTrayInfo = jobName_.empty() ? status : L"\"" + jobName_ + L"\"\n" + status;
@@ -1200,10 +1420,10 @@ void SyncProgressDialogImpl::setExternalStatus(const wxString& status, const wxS
trayIcon->setToolTip(newTrayInfo);
//show text in dialog title (and at the same time in taskbar)
- if (mainDialog)
+ if (parentFrame_)
{
- if (mainDialog->GetTitle() != newCaption)
- mainDialog->SetTitle(newCaption);
+ if (parentFrame_->GetTitle() != newCaption)
+ parentFrame_->SetTitle(newCaption);
}
//always set a title: we don't wxGTK to show "nameless window" instead
@@ -1212,7 +1432,8 @@ void SyncProgressDialogImpl::setExternalStatus(const wxString& status, const wxS
}
-void SyncProgressDialogImpl::updateGuiInt(bool allowYield)
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::updateGuiInt(bool allowYield)
{
if (!syncStat_) //sync not running
return;
@@ -1223,7 +1444,7 @@ void SyncProgressDialogImpl::updateGuiInt(bool allowYield)
const long timeNow = timeElapsed.Time();
//sync status text
- setText(*m_staticTextStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts!
+ setText(*pnl.m_staticTextStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts!
switch (syncStat_->currentPhase()) //no matter if paused or not
{
@@ -1233,33 +1454,30 @@ void SyncProgressDialogImpl::updateGuiInt(bool allowYield)
setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), toGuiString(syncStat_->getObjectsCurrent(ProcessCallback::PHASE_SCANNING))); //status text may be "paused"!
//progress indicators
- m_gauge1->Pulse();
- if (trayIcon.get()) trayIcon->setProgress(1); //1 = regular FFS logo
+ if (trayIcon.get()) trayIcon->setProgress(1); //100% = regular FFS logo
- //taskbar_ status is Taskbar::STATUS_INDETERMINATE
-
- //constant line graph
- graphDataBytesTotal->setValue(0);
+ //ignore graphs: should already have been cleared in initNewPhase()
//remaining objects and data
- setText(*m_staticTextRemainingObj , L"-", &layoutChanged);
- setText(*m_staticTextDataRemaining, L"", &layoutChanged);
+ setText(*pnl.m_staticTextRemainingObj , L"-", &layoutChanged);
+ setText(*pnl.m_staticTextDataRemaining, L"", &layoutChanged);
//remaining time and speed
- setText(*m_staticTextSpeed, L"-", &layoutChanged);
- setText(*m_staticTextRemTime, L"-", &layoutChanged);
+ setText(*pnl.m_staticTextRemTime, L"-", &layoutChanged);
+ pnl.m_panelGraphBytes->setAttributes(pnl.m_panelGraphBytes->getAttributes().setCornerText(wxString(), Graph2D::CORNER_TOP_LEFT));
+ pnl.m_panelGraphItems->setAttributes(pnl.m_panelGraphItems->getAttributes().setCornerText(wxString(), Graph2D::CORNER_TOP_LEFT));
break;
case ProcessCallback::PHASE_COMPARING_CONTENT:
case ProcessCallback::PHASE_SYNCHRONIZING:
{
- auto objectsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase());
- auto objectsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase());
- auto dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase());
- auto dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase());
+ const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase());
+ const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase());
+ const Int64 dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase());
+ const Int64 dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase());
//add both data + obj-count, to handle "deletion-only" cases
- const double fraction = dataTotal + objectsTotal == 0 ? 1 : std::max(0.0, to<double>(dataCurrent + objectsCurrent) / to<double>(dataTotal + objectsTotal));
+ const double fraction = dataTotal + itemsTotal == 0 ? 1 : std::max(0.0, to<double>(dataCurrent + itemsCurrent) / to<double>(dataTotal + itemsTotal));
//yes, this may legitimately become < 0: failed rename operation falls-back to copy + delete, reducing "dataCurrent" to potentially < 0!
//----------------------------------------------------------------------------------------------------
@@ -1267,16 +1485,22 @@ void SyncProgressDialogImpl::updateGuiInt(bool allowYield)
setExternalStatus(getDialogPhaseText(syncStat_, paused_, finalResult), fractionToString(fraction)); //status text may be "paused"!
//progress indicators
- m_gauge1->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE));
if (trayIcon.get()) trayIcon->setProgress(fraction);
if (taskbar_.get()) taskbar_->setProgress(fraction);
//constant line graph
- graphDataBytesTotal->setValue(to<double>(dataTotal));
-
+ curveDataBytesTotal ->setValue(timeNow, to<double>(dataTotal));
+ curveDataBytesCurrent->setValue(timeNow, to<double>(dataCurrent), to<double>(dataTotal));
+ curveDataItemsTotal ->setValue(timeNow, itemsTotal);
+ curveDataItemsCurrent->setValue(timeNow, itemsCurrent, itemsTotal);
+ //even though notifyProgressChange() already set the latest data, let's add another sample to have all curves consider "timeNow"
+ //no problem with adding too many records: CurveDataStatistics will remove duplicate entries!
+ curveDataBytes->addRecord(timeNow, to<double>(dataCurrent));
+ curveDataItems->addRecord(timeNow, itemsCurrent);
+
//remaining objects and data
- setText(*m_staticTextRemainingObj, toGuiString(objectsTotal - objectsCurrent), &layoutChanged);
- setText(*m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged);
+ setText(*pnl.m_staticTextRemainingObj, toGuiString(itemsTotal - itemsCurrent), &layoutChanged);
+ setText(*pnl.m_staticTextDataRemaining, L"(" + filesizeToShortString(dataTotal - dataCurrent) + L")", &layoutChanged);
//it's possible data remaining becomes shortly negative if last file synced has ADS data and the dataTotal was not yet corrected!
//remaining time and speed
@@ -1287,48 +1511,52 @@ void SyncProgressDialogImpl::updateGuiInt(bool allowYield)
lastStatCallSpeed = timeNow;
if (numeric::dist(phaseStartMs, timeNow) >= 1000) //discard stats for first second: probably messy
- perf->addSample(objectsCurrent, to<double>(dataCurrent), timeNow);
+ perf->addSample(itemsCurrent, to<double>(dataCurrent), timeNow);
//current speed -> Win 7 copy uses 1 sec update interval instead
- setText(*m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged);
+ Opt<std::wstring> bps = perf->getBytesPerSecond();
+ Opt<std::wstring> ips = perf->getItemsPerSecond();
+ pnl.m_panelGraphBytes->setAttributes(pnl.m_panelGraphBytes->getAttributes().setCornerText(bps ? *bps : L"", Graph2D::CORNER_TOP_LEFT));
+ pnl.m_panelGraphItems->setAttributes(pnl.m_panelGraphItems->getAttributes().setCornerText(ips ? *ips : L"", Graph2D::CORNER_TOP_LEFT));
+ //setText(*pnl.m_staticTextSpeed, perf->getBytesPerSecond(), &layoutChanged);
//remaining time: display with relative error of 10% - based on samples taken every 0.5 sec only
//-> call more often than once per second to correctly show last few seconds countdown, but don't call too often to avoid occasional jitter
- setText(*m_staticTextRemTime, perf->getRemainingTime(to<double>(dataTotal - dataCurrent)), &layoutChanged);
+ Opt<std::wstring> rt = perf->getRemainingTime(to<double>(dataTotal - dataCurrent));
+ setText(*pnl.m_staticTextRemTime, rt ? *rt : L"-", &layoutChanged);
}
}
break;
}
- m_panelGraph->setAttributes(m_panelGraph->getAttributes().setMinX(graphDataBytes->getXBegin()));
- m_panelGraph->setAttributes(m_panelGraph->getAttributes().setMaxX(graphDataBytes->getXEnd()));
- m_panelGraph->Refresh();
+ pnl.m_panelGraphBytes->Refresh();
+ pnl.m_panelGraphItems->Refresh();
//time elapsed
const long timeElapSec = timeNow / 1000;
- setText(*m_staticTextTimeElapsed,
+ setText(*pnl.m_staticTextTimeElapsed,
timeElapSec < 3600 ?
wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") :
wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), &layoutChanged);
- //do the ui update
+ //adapt layout after content changes above
if (layoutChanged)
{
- // Layout();
- // bSizerItemsRem->Layout();
- // bSizer171->Layout();
- //bSizerProgressStat->Layout(); //
- m_panelProgress->Layout(); //both needed
- //m_panelBackground->Layout(); //we use a dummy panel as actual background: replaces simple "Layout()" call
- //-> it seems this layout is not required, and even harmful: resets m_comboBoxExecFinished dropdown while user is selecting!
+ pnl.m_panelProgress->Layout();
+ //small statistics panels:
+ //pnl.m_panelItemsProcessed->Layout();
+ pnl.m_panelItemsRemaining->Layout();
+ pnl.m_panelTimeRemaining ->Layout();
+ //pnl.m_panelTimeElapsed->Layout(); -> needed?
}
#ifdef ZEN_WIN
//workaround Windows 7 bug messing up z-order after temporary application hangs: https://sourceforge.net/tracker/index.php?func=detail&aid=3376523&group_id=234430&atid=1093080
- if (mainDialog)
- if (evaluateZorder(*this, *mainDialog) == ZORDER_WRONG)
+ //This is still needed no matter if wxDialog or wxPanel is used! (2013-07)
+ if (parentFrame_)
+ if (evaluateZorder(*this, *parentFrame_) == ZORDER_WRONG)
{
- HWND hProgress = static_cast<HWND>(GetHWND());
+ HWND hProgress = static_cast<HWND>(this->GetHWND());
if (::IsWindowVisible(hProgress))
{
::ShowWindow(hProgress, SW_HIDE); //make Windows recalculate z-order
@@ -1342,47 +1570,58 @@ void SyncProgressDialogImpl::updateGuiInt(bool allowYield)
//support for pause button
if (paused_)
{
+ /*
+ ZEN_ON_SCOPE_EXIT(resumeTimer()); -> crashes on Fedora; WHY???
+ => likely compiler bug!!!
+ 1. no crash on Fedora for: ZEN_ON_SCOPE_EXIT(this->resumeTimer());
+ 1. no crash if we derive from wxFrame instead of template "TopLevelDialog"
+ 2. no crash on Ubuntu GCC
+ 3. following makes GCC crash already during compilation: auto dfd = zen::makeGuard([this]{ resumeTimer(); });
+ */
+
stopTimer();
- ZEN_ON_SCOPE_EXIT(resumeTimer());
+
while (paused_)
{
- wxMilliSleep(UI_UPDATE_INTERVAL);
- updateUiNow(); //receive UI message that end pause
+ wxTheApp->Yield(); //receive UI message that end pause OR forceful termination!
+ //*first* refresh GUI (removing flicker) before sleeping!
+ boost::this_thread::sleep(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL));
}
+ //if SyncProgressDialogImpl::OnClose() already wxWindow::Destroy() on OS X then this instance is already toast!
+ if (wereDead)
+ return; //GTFO and don't call this->resumeTimer()
+
+ resumeTimer();
}
- /*
- /|\
- | keep this sequence to ensure one full progress update before entering pause mode!
- \|/
- */
- updateUiNow(); //receive UI message that sets pause status
+ else
+ /*
+ /|\
+ | keep this sequence to ensure one full progress update before entering pause mode!
+ \|/
+ */
+ wxTheApp->Yield(); //receive UI message that sets pause status OR forceful termination!
}
else
- Update(); //don't wait until next idle event (who knows what blocking process comes next?)
+ this->Update(); //don't wait until next idle event (who knows what blocking process comes next?)
}
-std::wstring SyncProgressDialogImpl::getExecWhenFinishedCommand() const
-{
- return m_comboBoxExecFinished->getValue();
-}
-
-
-void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, paused_, finalResult"
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::updateDialogStatus() //depends on "syncStat_, paused_, finalResult"
{
const wxString dlgStatusTxt = getDialogPhaseText(syncStat_, paused_, finalResult);
- m_staticTextPhase->SetLabel(dlgStatusTxt);
+ pnl.m_staticTextPhase->SetLabel(dlgStatusTxt);
//status bitmap
if (syncStat_) //sync running
{
auto setStatusBitmap = [&](const wchar_t* bmpName)
{
- m_animCtrlSyncing->Hide();
- m_bitmapStatus->SetBitmap(getResourceImage(bmpName));
- m_bitmapStatus->SetToolTip(dlgStatusTxt);
- m_bitmapStatus->Show();
+ pnl.m_animCtrlSyncing->Hide();
+ pnl.m_bitmapStatus->SetBitmap(getResourceImage(bmpName));
+ pnl.m_bitmapStatus->SetToolTip(dlgStatusTxt);
+ pnl.m_bitmapStatus->Show();
};
if (paused_)
@@ -1391,8 +1630,8 @@ void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, pause
switch (syncStat_->currentPhase())
{
case ProcessCallback::PHASE_NONE:
- m_animCtrlSyncing->Hide();
- m_bitmapStatus->Hide();
+ pnl.m_animCtrlSyncing->Hide();
+ pnl.m_bitmapStatus->Hide();
break;
case ProcessCallback::PHASE_SCANNING:
@@ -1404,11 +1643,11 @@ void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, pause
break;
case ProcessCallback::PHASE_SYNCHRONIZING:
- m_bitmapStatus->SetBitmap(getResourceImage(L"status_syncing"));
- m_bitmapStatus->SetToolTip(dlgStatusTxt);
- m_bitmapStatus->Show();
- m_animCtrlSyncing->Show();
- m_animCtrlSyncing->SetToolTip(dlgStatusTxt);
+ pnl.m_bitmapStatus->SetBitmap(getResourceImage(L"status_syncing"));
+ pnl.m_bitmapStatus->SetToolTip(dlgStatusTxt);
+ pnl.m_bitmapStatus->Show();
+ pnl.m_animCtrlSyncing->Show();
+ pnl.m_animCtrlSyncing->SetToolTip(dlgStatusTxt);
break;
}
}
@@ -1416,10 +1655,10 @@ void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, pause
{
auto setStatusBitmap = [&](const wchar_t* bmpName, const std::wstring& tooltip)
{
- m_animCtrlSyncing->Hide();
- m_bitmapStatus->SetBitmap(getResourceImage(bmpName));
- m_bitmapStatus->Show();
- m_bitmapStatus->SetToolTip(tooltip);
+ pnl.m_animCtrlSyncing->Hide();
+ pnl.m_bitmapStatus->SetBitmap(getResourceImage(bmpName));
+ pnl.m_bitmapStatus->Show();
+ pnl.m_bitmapStatus->SetToolTip(tooltip);
};
switch (finalResult)
{
@@ -1479,21 +1718,15 @@ void SyncProgressDialogImpl::updateDialogStatus() //depends on "syncStat_, pause
//pause button
if (syncStat_) //sync running
- {
- if (paused_)
- m_buttonPause->SetLabel(_("Continue"));
- else
- m_buttonPause->SetLabel(_("Pause"));
- }
+ pnl.m_buttonPause->SetLabel(paused_ ? _("&Continue") : _("&Pause"));
- Layout();
- Refresh(); //a few pixels below the status text need refreshing
+ pnl.Layout();
+ this->Refresh(); //a few pixels below the status text need refreshing
}
-warn_static("osx: minimize to systray?")
-
-void SyncProgressDialogImpl::closeWindowDirectly() //this should really be called: do not call back + schedule deletion
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::closeWindowDirectly() //this should really be called: do not call back + schedule deletion
{
paused_ = false; //you never know?
//ATTENTION: dialog may live a little longer, so watch callbacks!
@@ -1502,11 +1735,12 @@ void SyncProgressDialogImpl::closeWindowDirectly() //this should really be calle
abortCb_ = nullptr;
//resumeFromSystray(); -> NO, instead ~SyncProgressDialogImpl() makes sure that main dialog is shown again!
- Close(); //generate close event: do NOT destroy window unconditionally!
+ this->Close(); //generate close event: do NOT destroy window unconditionally!
}
-void SyncProgressDialogImpl::processHasFinished(SyncResult resultId, const ErrorLog& log) //essential to call this in StatusHandler derived class destructor
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::processHasFinished(SyncResult resultId, const ErrorLog& log) //essential to call this in StatusHandler derived class destructor
{
//at the LATEST(!) to prevent access to currentStatusHandler
//enable okay and close events; may be set in this method ONLY
@@ -1530,36 +1764,30 @@ void SyncProgressDialogImpl::processHasFinished(SyncResult resultId, const Error
case ProcessCallback::PHASE_COMPARING_CONTENT:
case ProcessCallback::PHASE_SYNCHRONIZING:
{
- auto objectsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase());
- auto objectsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase());
- auto dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase());
- auto dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase());
+ const int itemsCurrent = syncStat_->getObjectsCurrent(syncStat_->currentPhase());
+ const int itemsTotal = syncStat_->getObjectsTotal (syncStat_->currentPhase());
+ const Int64 dataCurrent = syncStat_->getDataCurrent (syncStat_->currentPhase());
+ const Int64 dataTotal = syncStat_->getDataTotal (syncStat_->currentPhase());
assert(dataCurrent <= dataTotal);
//set overall speed (instead of current speed)
- auto getOverallBytesPerSecond = [&]() -> wxString
- {
- const long timeDelta = timeElapsed.Time() - phaseStartMs; //we need to consider "time within current phase" not total "timeElapsed"!
- if (timeDelta != 0)
- return filesizeToShortString(dataCurrent * 1000 / timeDelta) + _("/sec");
- return L"-"; //fallback
- };
+ const long timeDelta = timeElapsed.Time() - phaseStartMs; //we need to consider "time within current phase" not total "timeElapsed"!
+
+ const wxString overallBytesPerSecond = timeDelta == 0 ? wxString() : filesizeToShortString(dataCurrent * 1000 / timeDelta) + _("/sec");
+ const wxString overallItemsPerSecond = timeDelta == 0 ? wxString() : replaceCpy(_("%x items"), L"%x", formatThreeDigitPrecision(itemsCurrent * 1000.0 / timeDelta)) + _("/sec");
- m_staticTextSpeed->SetLabel(getOverallBytesPerSecond());
+ pnl.m_panelGraphBytes->setAttributes(pnl.m_panelGraphBytes->getAttributes().setCornerText(overallBytesPerSecond, Graph2D::CORNER_TOP_LEFT));
+ pnl.m_panelGraphItems->setAttributes(pnl.m_panelGraphItems->getAttributes().setCornerText(overallItemsPerSecond, Graph2D::CORNER_TOP_LEFT));
//show new element "items processed"
- m_staticTextLabelItemsProc->Show(true);
- bSizerItemsProc ->Show(true);
- m_staticTextProcessedObj ->SetLabel(toGuiString(objectsCurrent));
- m_staticTextDataProcessed->SetLabel(L"(" + filesizeToShortString(dataCurrent) + L")");
+ pnl.m_panelItemsProcessed->Show();
+ pnl.m_staticTextProcessedObj ->SetLabel(toGuiString(itemsCurrent));
+ pnl.m_staticTextDataProcessed->SetLabel(L"(" + filesizeToShortString(dataCurrent) + L")");
//hide remaining elements...
- if (objectsCurrent == objectsTotal && //...if everything was processed successfully
- dataCurrent == dataTotal)
- {
- m_staticTextLabelItemsRem->Show(false);
- bSizerItemsRem ->Show(false);
- }
+ if (itemsCurrent == itemsTotal && //...if everything was processed successfully
+ dataCurrent == dataTotal)
+ pnl.m_panelItemsRemaining->Hide();
}
break;
}
@@ -1576,62 +1804,64 @@ void SyncProgressDialogImpl::processHasFinished(SyncResult resultId, const Error
resumeFromSystray(); //if in tray mode...
- EnableCloseButton(true);
+ this->EnableCloseButton(true);
- m_buttonCancel->Disable();
- m_buttonCancel->Hide();
- m_buttonPause->Disable();
- m_buttonPause->Hide();
- m_buttonClose->Show();
- m_buttonClose->Enable();
+ pnl.m_bpButtonMinimizeToTray->Hide();
+ pnl.m_buttonCancel->Disable();
+ pnl.m_buttonCancel->Hide();
+ pnl.m_buttonPause->Disable();
+ pnl.m_buttonPause->Hide();
+ pnl.m_buttonClose->Show();
+ pnl.m_buttonClose->Enable();
- bSizerExecFinished->Show(false);
+ pnl.m_buttonClose->SetFocus();
- //set std order after button visibility was set
- setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonClose));
-
- if (IsShown()) //don't steal focus when residing in sys-tray!
- m_buttonClose->SetFocus();
+ pnl.bSizerExecFinished->Show(false);
- //m_animCtrlSyncing->Stop();
- //m_animCtrlSyncing->Hide();
+ //set std order after button visibility was set
+ setStandardButtonOrder(*pnl.bSizerStdButtons, StdButtons().setAffirmative(pnl.m_buttonClose));
//hide current operation status
- m_staticTextStatus->Hide();
+ pnl.bSizerStatusText->Show(false);
- //show and prepare final statistics
- m_notebookResult->Show();
+ //show and prepare final statistics
+ pnl.m_notebookResult->Show();
#if defined ZEN_WIN || defined ZEN_LINUX
- m_staticlineFooter->Hide(); //win: m_notebookResult already has a window frame
+ pnl.m_staticlineFooter->Hide(); //win: m_notebookResult already has a window frame
#endif
- //show total time
- m_staticTextLabelElapsedTime->SetLabel(_("Total time:")); //it's not "elapsed time" anymore
-
//hide remaining time
- m_staticTextLabelRemTime->Show(false);
- m_staticTextRemTime ->Show(false);
+ pnl.m_panelTimeRemaining->Hide();
//1. re-arrange graph into results listbook
- bSizerRoot->Detach(m_panelProgress);
- m_panelProgress->Reparent(m_notebookResult);
+ pnl.bSizerRoot->Detach(pnl.m_panelProgress);
+ pnl.m_panelProgress->Reparent(pnl.m_notebookResult);
#ifdef ZEN_LINUX //does not seem to be required on Win or OS X
wxTheApp->Yield(); //wxGTK 2.9.3 fails miserably at "reparent" whithout this
#endif
- m_notebookResult->AddPage(m_panelProgress, _("Statistics"), true); //AddPage() takes ownership!
+ pnl.m_notebookResult->AddPage(pnl.m_panelProgress, _("Statistics"), true); //AddPage() takes ownership!
//2. log file
const size_t posLog = 1;
- LogPanel* logPanel = new LogPanel(m_notebookResult, log); //owned by m_notebookResult
- m_notebookResult->AddPage(logPanel, _("Logging"), false);
+ LogPanel* logPanel = new LogPanel(pnl.m_notebookResult, log); //owned by m_notebookResult
+ pnl.m_notebookResult->AddPage(logPanel, _("Log"), false);
//bSizerHoldStretch->Insert(0, logPanel, 1, wxEXPAND);
//show log instead of graph if errors occurred! (not required for ignored warnings)
if (log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR) > 0)
- m_notebookResult->ChangeSelection(posLog);
+ pnl.m_notebookResult->ChangeSelection(posLog);
+
+ //this->Fit(); //not a good idea: will shrink even if window is maximized or was enlarged by the user
+ pnl.Layout();
+
+ pnl.m_panelProgress->Layout();
+ //small statistics panels:
+ pnl.m_panelItemsProcessed->Layout();
+ pnl.m_panelItemsRemaining->Layout();
+ //pnl.m_panelTimeRemaining->Layout();
+ //pnl.m_panelTimeElapsed->Layout(); -> needed?
- Layout();
//play (optional) sound notification after sync has completed -> only play when waiting on results dialog, seems to be pointless otherwise!
switch (finalResult)
{
@@ -1648,19 +1878,19 @@ void SyncProgressDialogImpl::processHasFinished(SyncResult resultId, const Error
break;
}
- //Raise(); -> don't! user may be watching a movie in the meantime ;)
- warn_static("was ist mit resumeFromSystray:: Raise()??")
+ //Raise(); -> don't! user may be watching a movie in the meantime ;) note: resumeFromSystray() also calls Raise()!
}
-void SyncProgressDialogImpl::OnOkay(wxCommandEvent& event)
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::OnOkay(wxCommandEvent& event)
{
- isZombie = true; //on Fedora an iconize event is issued *before* entering SyncProgressDialogImpl::OnClose()!!!
- Close(); //generate close event: do NOT destroy window unconditionally!
+ this->Close(); //generate close event: do NOT destroy window unconditionally!
}
-void SyncProgressDialogImpl::OnCancel(wxCommandEvent& event)
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::OnCancel(wxCommandEvent& event)
{
paused_ = false;
updateDialogStatus(); //update status + pause button
@@ -1671,14 +1901,16 @@ void SyncProgressDialogImpl::OnCancel(wxCommandEvent& event)
}
-void SyncProgressDialogImpl::OnPause(wxCommandEvent& event)
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::OnPause(wxCommandEvent& event)
{
paused_ = !paused_;
updateDialogStatus(); //update status + pause button
}
-void SyncProgressDialogImpl::OnClose(wxCloseEvent& event)
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::OnClose(wxCloseEvent& event)
{
//this event handler may be called *during* sync, e.g. due to a system shutdown (Windows), anytime (OS X)
//try to stop sync gracefully and cross fingers:
@@ -1695,68 +1927,86 @@ void SyncProgressDialogImpl::OnClose(wxCloseEvent& event)
syncStat_ = nullptr;
abortCb_ = nullptr;
- isZombie = true; //it "lives" until cleanup in next idle event
- Destroy();
-}
-
-
-void SyncProgressDialogImpl::OnIconize(wxIconizeEvent& event)
-{
- if (isZombie) return; //wxGTK sends iconize event *after* wxWindow::Destroy, sigh...
-
- if (event.IsIconized()) //ATTENTION: iconize event is also triggered on "Restore"! (at least under Linux)
- minimizeToTray();
- else
- resumeFromSystray(); //may be initiated by "show desktop" although all windows are hidden!
+ wereDead = true;
+ this->Destroy(); //wxWidgets OS X: simple "delete"!!!!!!!
}
-void SyncProgressDialogImpl::OnResumeFromTray(wxCommandEvent& event)
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::OnIconize(wxIconizeEvent& event)
{
- resumeFromSystray();
+ /*
+ propagate progress dialog minimize/maximize to parent
+ -----------------------------------------------------
+ Fedora/Debian/Ubuntu:
+ - wxDialog cannot be minimized
+ - worse, wxGTK sends stray iconize events *after* wxDialog::Destroy()
+ - worse, on Fedora an iconize event is issued directly after calling Close()
+ - worse, even wxDialog::Hide() causes iconize event!
+ => nothing to do
+ SUSE:
+ - wxDialog can be minimized (it just vanishes!) and in general also minimizes parent: except for our progress wxDialog!!!
+ - worse, wxDialog::Hide() causes iconize event
+ - probably the same issues with stray iconize events like Fedora/Debian/Ubuntu
+ - minimize button is always shown, even if wxMINIMIZE_BOX is omitted!
+ => nothing to do
+ Mac OS X:
+ - wxDialog can be minimized and automatically minimizes parent
+ - no iconize events seen by wxWidgets!
+ => nothing to do
+ Windows:
+ - wxDialog can be minimized but does not also minimize parent
+ - iconize events only seen for manual minimize
+ => propagate event to parent
+ */
+#ifdef ZEN_WIN
+ if (parentFrame_)
+ if (parentFrame_->IsIconized() != event.IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
+ parentFrame_->Iconize(event.IsIconized());
+#endif
+ event.Skip();
}
-void SyncProgressDialogImpl::minimizeToTray()
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::minimizeToTray()
{
-// wxMessageBox(L"hi");
-
if (!trayIcon.get())
{
- trayIcon = make_unique<FfsTrayIcon>();
- trayIcon->Connect(FFS_REQUEST_RESUME_TRAY_EVENT, wxCommandEventHandler(SyncProgressDialogImpl::OnResumeFromTray), nullptr, this);
- //tray icon has shorter lifetime than this => no need to disconnect event later
+ trayIcon = make_unique<FfsTrayIcon>([this] { this->resumeFromSystray(); }); //FfsTrayIcon lifetime is a subset of "this"'s lifetime!
+ //we may destroy FfsTrayIcon even while in the FfsTrayIcon callback!!!!
updateGuiInt(false); //set tray tooltip + progress: e.g. no updates while paused
- Hide();
- if (mainDialog)
- mainDialog->Hide();
+ this->Hide();
+ if (parentFrame_)
+ parentFrame_->Hide();
}
}
-void SyncProgressDialogImpl::resumeFromSystray()
+template <class TopLevelDialog>
+void SyncProgressDialogImpl<TopLevelDialog>::resumeFromSystray()
{
if (trayIcon)
{
trayIcon.reset();
- if (mainDialog)
+ if (parentFrame_)
{
- if (mainDialog->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
- mainDialog->Iconize(false);
- mainDialog->Show();
- mainDialog->Raise();
+ //if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
+ // parentFrame_->Iconize(false);
+ parentFrame_->Show();
+ parentFrame_->Raise();
}
- if (IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
- Iconize(false);
- Show();
- Raise();
- SetFocus();
+ //if (IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
+ // Iconize(false);
+ this->Show();
+ this->Raise();
+ this->SetFocus();
- updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode)
+ updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode)
updateGuiInt(false); //restore Windows 7 task bar progress (e.g. required in pause mode)
}
}
@@ -1766,11 +2016,18 @@ void SyncProgressDialogImpl::resumeFromSystray()
SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb,
const std::function<void()>& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!)
const zen::Statistics& syncStat,
- wxTopLevelWindow* parentWindow, //may be nullptr
+ wxFrame* parentWindow, //may be nullptr
bool showProgress,
const wxString& jobName,
const std::wstring& execWhenFinished,
std::vector<std::wstring>& execFinishedHistory)
{
- return new SyncProgressDialogImpl(abortCb, notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory);
+ if (parentWindow) //sync from GUI
+ return new SyncProgressDialogImpl<wxDialog>(wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxRESIZE_BORDER,
+ [&](wxDialog& progDlg) { return parentWindow; }, abortCb,
+ notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory);
+ else //FFS batch job
+ return new SyncProgressDialogImpl<wxFrame>(wxDEFAULT_FRAME_STYLE,
+ [](wxFrame& progDlg) { return &progDlg; }, abortCb,
+ notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, execWhenFinished, execFinishedHistory);
}
diff --git a/ui/progress_indicator.h b/ui/progress_indicator.h
index 0789ca80..e2dbb99c 100644
--- a/ui/progress_indicator.h
+++ b/ui/progress_indicator.h
@@ -10,7 +10,7 @@
#include <functional>
#include <zen/error_log.h>
//#include <zen/zstring.h>
-#include <wx/toplevel.h>
+#include <wx/frame.h>
#include "../lib/status_handler.h"
//#include "main_dlg.h"
@@ -18,7 +18,7 @@
class CompareProgressDialog
{
public:
- CompareProgressDialog(wxTopLevelWindow& parentWindow); //CompareProgressDialog will be owned by parentWindow!
+ CompareProgressDialog(wxFrame& parentWindow); //CompareProgressDialog will be owned by parentWindow!
wxWindow* getAsWindow(); //convenience! don't abuse!
@@ -52,7 +52,7 @@ struct SyncProgressDialog
//---------------------------------------------------------------------------
- virtual wxWindow* getAsWindow() = 0; //convenience! don't abuse!
+ virtual wxWindow* getWindowIfVisible() = 0; //may be nullptr; don't abuse, use as parent for modal dialogs only!
virtual void initNewPhase() = 0; //call after "StatusHandler::initNewPhase"
virtual void notifyProgressChange() = 0; //throw (), required by graph!
@@ -71,7 +71,7 @@ protected:
SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb,
const std::function<void()>& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!)
const zen::Statistics& syncStat,
- wxTopLevelWindow* parentWindow, //may be nullptr
+ wxFrame* parentWindow, //may be nullptr
bool showProgress,
const wxString& jobName,
const std::wstring& execWhenFinished,
diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp
index 304e4269..b7b7828b 100644
--- a/ui/small_dlgs.cpp
+++ b/ui/small_dlgs.cpp
@@ -384,10 +384,9 @@ void DeleteDialog::updateGui()
{
wxWindowUpdateLocker dummy(this); //avoid display distortion
- const std::pair<Zstring, int> delInfo = zen::deleteFromGridAndHDPreview(
- rowsToDeleteOnLeft,
- rowsToDeleteOnRight,
- m_checkBoxDeleteBothSides->GetValue());
+ const std::pair<Zstring, int> delInfo = zen::deleteFromGridAndHDPreview(rowsToDeleteOnLeft,
+ rowsToDeleteOnRight,
+ m_checkBoxDeleteBothSides->GetValue());
wxString header;
if (m_checkBoxUseRecycler->GetValue())
{
@@ -406,7 +405,13 @@ void DeleteDialog::updateGui()
const wxString& fileList = utfCvrtTo<wxString>(delInfo.first);
m_textCtrlFileList->ChangeValue(fileList);
-
+ /*
+ There is a nasty bug on wxGTK under Ubuntu: If a multi-line wxTextCtrl contains so many lines that scrollbars are shown,
+ it re-enables all windows that are supposed to be disabled during the current modal loop!
+ This only affects Ubuntu/wxGTK! No such issue on Debian/wxGTK or Suse/wxGTK
+ => another Unity problem like the following?
+ http://trac.wxwidgets.org/ticket/14823 "Menu not disabled when showing modal dialogs in wxGTK under Unity"
+ */
Layout();
}
diff --git a/ui/switch_to_gui.h b/ui/switch_to_gui.h
index a3cf6827..20fe81de 100644
--- a/ui/switch_to_gui.h
+++ b/ui/switch_to_gui.h
@@ -27,7 +27,7 @@ public:
void execute() const
{
- MainDialog::create(guiCfg, referenceFiles, globalSettings_, true); //new toplevel window
+ MainDialog::create(guiCfg, referenceFiles, &globalSettings_, true); //new toplevel window
}
private:
diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp
index 806914a3..97518be1 100644
--- a/ui/sync_cfg.cpp
+++ b/ui/sync_cfg.cpp
@@ -107,15 +107,15 @@ void updateConfigIcons(const DirectionConfig& directionCfg,
switch (dirCfg.exLeftSideOnly)
{
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_create_right")));
buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_CREATE_NEW_RIGHT));
break;
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_delete_left")));
buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_DELETE_LEFT));
break;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
buttonLeftOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none")));
buttonLeftOnly->SetToolTip(getSyncOpDescription(SO_DO_NOTHING));
break;
@@ -123,15 +123,15 @@ void updateConfigIcons(const DirectionConfig& directionCfg,
switch (dirCfg.exRightSideOnly)
{
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_delete_right")));
buttonRightOnly->SetToolTip(getSyncOpDescription(SO_DELETE_RIGHT));
break;
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_create_left")));
buttonRightOnly->SetToolTip(getSyncOpDescription(SO_CREATE_NEW_LEFT));
break;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
buttonRightOnly->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none")));
buttonRightOnly->SetToolTip(getSyncOpDescription(SO_DO_NOTHING));
break;
@@ -139,15 +139,15 @@ void updateConfigIcons(const DirectionConfig& directionCfg,
switch (dirCfg.leftNewer)
{
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right")));
buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT));
break;
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left")));
buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT));
break;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
buttonLeftNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none")));
buttonLeftNewer->SetToolTip(getSyncOpDescription(SO_DO_NOTHING));
break;
@@ -155,15 +155,15 @@ void updateConfigIcons(const DirectionConfig& directionCfg,
switch (dirCfg.rightNewer)
{
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right")));
buttonRightNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT));
break;
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left")));
buttonRightNewer->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT));
break;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
buttonRightNewer->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none")));
buttonRightNewer->SetToolTip(getSyncOpDescription(SO_DO_NOTHING));
break;
@@ -171,15 +171,15 @@ void updateConfigIcons(const DirectionConfig& directionCfg,
switch (dirCfg.different)
{
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right")));
buttonDifferent->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT));
break;
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left")));
buttonDifferent->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT));
break;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
buttonDifferent->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_none")));
buttonDifferent->SetToolTip(getSyncOpDescription(SO_DO_NOTHING));
break;
@@ -187,15 +187,15 @@ void updateConfigIcons(const DirectionConfig& directionCfg,
switch (dirCfg.conflict)
{
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_right")));
buttonConflict->SetToolTip(getSyncOpDescription(SO_OVERWRITE_RIGHT));
break;
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"so_update_left")));
buttonConflict->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT));
break;
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
buttonConflict->SetBitmapLabel(mirrorIfRtl(getResourceImage(L"cat_conflict"))); //silent dependency to algorithm.cpp::Redetermine!!!
buttonConflict->SetToolTip(_("Leave as unresolved conflict"));
break;
@@ -495,14 +495,14 @@ void toggleSyncDirection(SyncDirection& current)
{
switch (current)
{
- case SYNC_DIR_RIGHT:
- current = SYNC_DIR_LEFT;
+ case SyncDirection::RIGHT:
+ current = SyncDirection::LEFT;
break;
- case SYNC_DIR_LEFT:
- current = SYNC_DIR_NONE;
+ case SyncDirection::LEFT:
+ current = SyncDirection::NONE;
break;
- case SYNC_DIR_NONE:
- current = SYNC_DIR_RIGHT;
+ case SyncDirection::NONE:
+ current = SyncDirection::RIGHT;
break;
}
}
diff --git a/ui/taskbar.cpp b/ui/taskbar.cpp
index 0e019d04..e6aadc90 100644
--- a/ui/taskbar.cpp
+++ b/ui/taskbar.cpp
@@ -30,7 +30,7 @@ using namespace tbseven;
class Taskbar::Pimpl //throw TaskbarNotAvailable
{
public:
- Pimpl(const wxTopLevelWindow& window) :
+ Pimpl(const wxFrame& window) :
assocWindow(window.GetHWND()),
setStatus_ (getDllName(), funName_setStatus),
setProgress_(getDllName(), funName_setProgress)
@@ -86,7 +86,7 @@ const char FFS_DESKTOP_FILE[] = "freefilesync.desktop";
class Taskbar::Pimpl //throw (TaskbarNotAvailable)
{
public:
- Pimpl(const wxTopLevelWindow& window) :
+ Pimpl(const wxFrame& window) :
tbEntry(unity_launcher_entry_get_for_desktop_id(FFS_DESKTOP_FILE))
//tbEntry(unity_launcher_entry_get_for_app_uri("application://freefilesync.desktop"))
{
@@ -133,7 +133,7 @@ private:
class Taskbar::Pimpl
{
public:
- Pimpl(const wxTopLevelWindow& window) {}
+ Pimpl(const wxFrame& window) {}
~Pimpl() { setDockText(""); }
@@ -161,7 +161,7 @@ private:
class Taskbar::Pimpl
{
public:
- Pimpl(const wxTopLevelWindow& window) { throw TaskbarNotAvailable(); }
+ Pimpl(const wxFrame& window) { throw TaskbarNotAvailable(); }
void setStatus(Status status) {}
void setProgress(double fraction) {}
};
@@ -169,7 +169,7 @@ public:
//########################################################################################################
-Taskbar::Taskbar(const wxTopLevelWindow& window) : pimpl_(new Pimpl(window)) {} //throw TaskbarNotAvailable
+Taskbar::Taskbar(const wxFrame& window) : pimpl_(new Pimpl(window)) {} //throw TaskbarNotAvailable
Taskbar::~Taskbar() {}
void Taskbar::setStatus(Status status) { pimpl_->setStatus(status); }
diff --git a/ui/taskbar.h b/ui/taskbar.h
index ad708794..82e08656 100644
--- a/ui/taskbar.h
+++ b/ui/taskbar.h
@@ -8,7 +8,7 @@
#define TASKBARPROGRESS_H_INCLUDED
#include <memory>
-#include <wx/toplevel.h>
+#include <wx/frame.h>
/*
Windows 7; show progress in windows superbar via ITaskbarList3 Interface: http://msdn.microsoft.com/en-us/library/dd391692(VS.85).aspx
@@ -27,7 +27,7 @@ class TaskbarNotAvailable {};
class Taskbar
{
public:
- Taskbar(const wxTopLevelWindow& window); //throw TaskbarNotAvailable()
+ Taskbar(const wxFrame& window); //throw TaskbarNotAvailable
~Taskbar();
enum Status
diff --git a/ui/tray_icon.cpp b/ui/tray_icon.cpp
index 6a9c640c..3826458f 100644
--- a/ui/tray_icon.cpp
+++ b/ui/tray_icon.cpp
@@ -14,8 +14,6 @@
#include "../lib/resources.h"
-const wxEventType FFS_REQUEST_RESUME_TRAY_EVENT = wxNewEventType();
-
namespace
{
void fillRange(wxImage& img, int pixelFirst, int pixelLast, const wxColor& col) //tolerant input range
@@ -52,90 +50,56 @@ void fillRange(wxImage& img, int pixelFirst, int pixelLast, const wxColor& col)
wxIcon generateProgressIcon(const wxImage& logo, double fraction) //generate icon with progress indicator
{
- if (!logo.IsOk())
+ if (!logo.IsOk() || logo.GetWidth() <= 0 || logo.GetHeight() <= 0)
return wxIcon();
const int pixelCount = logo.GetWidth() * logo.GetHeight();
- const int startFillPixel = std::min(numeric::round(fraction * pixelCount), pixelCount);
+ const int startFillPixel = numeric::confineCpy(numeric::round(fraction * pixelCount), 0, pixelCount);
//minor optimization
static std::pair<int, wxIcon> buffer = std::make_pair(-1, wxNullIcon);
if (buffer.first != startFillPixel)
{
- //progress bar
- if (logo.GetWidth() > 0 &&
- logo.GetHeight() > 0)
- {
- wxImage genImage(logo.Copy()); //workaround wxWidgets' screwed-up design from hell: their copy-construction implements reference-counting WITHOUT copy-on-write!
-
- //gradually make FFS icon brighter while nearing completion
- zen::brighten(genImage, -200 * (1 - fraction));
-
- //fill black border row
- if (startFillPixel <= pixelCount - genImage.GetWidth())
- {
- /*
- --------
- ---bbbbb
- bbbbSyyy S : start yellow remainder
- yyyyyyyy
- */
- int bStart = startFillPixel - genImage.GetWidth();
- if (bStart % genImage.GetWidth() != 0) //add one more black pixel, see ascii-art
- --bStart;
- fillRange(genImage, bStart, startFillPixel, *wxBLACK);
- }
- else if (startFillPixel < pixelCount)
- {
- //special handling for last row
- /*
- --------
- --------
- ---bbbbb
- ---bSyyy S : start yellow remainder
- */
- int bStart = startFillPixel - genImage.GetWidth() - 1;
- int bEnd = (bStart / genImage.GetWidth() + 1) * genImage.GetWidth();
-
- fillRange(genImage, bStart, bEnd, *wxBLACK);
- fillRange(genImage, startFillPixel - 1, startFillPixel, *wxBLACK);
- }
+ wxImage genImage(logo.Copy()); //workaround wxWidgets' screwed-up design from hell: their copy-construction implements reference-counting WITHOUT copy-on-write!
- //fill yellow remainder
- fillRange(genImage, startFillPixel, pixelCount, wxColour(240, 200, 0));
+ //gradually make FFS icon brighter while nearing completion
+ zen::brighten(genImage, -200 * (1 - fraction));
+ //fill black border row
+ if (startFillPixel <= pixelCount - genImage.GetWidth())
+ {
/*
- const int indicatorWidth = genImage.GetWidth() * .4;
- const int indicatorXBegin = std::ceil((genImage.GetWidth() - indicatorWidth) / 2.0);
- const int indicatorYBegin = genImage.GetHeight() - indicatorHeight;
-
- //draw progress indicator: do NOT use wxDC::DrawRectangle! Doesn't respect alpha in Windows, but does in Linux!
- //We need a simple, working solution:
-
- for (int row = indicatorYBegin; row < genImage.GetHeight(); ++row)
- {
- for (int col = indicatorXBegin; col < indicatorXBegin + indicatorWidth; ++col)
- {
- unsigned char* const pixelBegin = data + (row * genImage.GetWidth() + col) * 3;
- pixelBegin[0] = 240; //red
- pixelBegin[1] = 200; //green
- pixelBegin[2] = 0; //blue
- }
- }
-
- if (genImage.HasAlpha())
- {
- unsigned char* const alpha = genImage.GetAlpha();
- //make progress indicator fully opaque:
- for (int row = indicatorYBegin; row < genImage.GetHeight(); ++row)
- ::memset(alpha + row * genImage.GetWidth() + indicatorXBegin, wxIMAGE_ALPHA_OPAQUE, indicatorWidth);
- }
+ --------
+ ---bbbbb
+ bbbbSyyy S : start yellow remainder
+ yyyyyyyy
*/
- buffer.second.CopyFromBitmap(wxBitmap(genImage));
+ int bStart = startFillPixel - genImage.GetWidth();
+ if (bStart % genImage.GetWidth() != 0) //add one more black pixel, see ascii-art
+ --bStart;
+ fillRange(genImage, bStart, startFillPixel, *wxBLACK);
+ }
+ else if (startFillPixel < pixelCount)
+ {
+ //special handling for last row
+ /*
+ --------
+ --------
+ ---bbbbb
+ ---bSyyy S : start yellow remainder
+ */
+ int bStart = startFillPixel - genImage.GetWidth() - 1;
+ int bEnd = (bStart / genImage.GetWidth() + 1) * genImage.GetWidth();
+
+ fillRange(genImage, bStart, bEnd, *wxBLACK);
+ fillRange(genImage, startFillPixel - 1, startFillPixel, *wxBLACK);
}
- else
- buffer.second = wxIcon();
+
+ //fill yellow remainder
+ fillRange(genImage, startFillPixel, pixelCount, wxColour(240, 200, 0));
+
+ buffer.second.CopyFromBitmap(wxBitmap(genImage));
}
return buffer.second;
@@ -145,7 +109,7 @@ wxIcon generateProgressIcon(const wxImage& logo, double fraction) //generate ico
enum Selection
{
- CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of Zero does not work under Mac"
+ CONTEXT_RESTORE = 1, //wxWidgets: "A MenuItem ID of zero does not work under Mac"
CONTEXT_ABOUT = wxID_ABOUT
};
}
@@ -154,95 +118,107 @@ enum Selection
class FfsTrayIcon::TaskBarImpl : public wxTaskBarIcon
{
public:
- TaskBarImpl(FfsTrayIcon& parent) : parent_(&parent) {}
+ TaskBarImpl(const std::function<void()>& onRequestResume) : onRequestResume_(onRequestResume)
+ {
+ Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(TaskBarImpl::OnDoubleClick), nullptr, this); //register double-click
+ }
- void parentHasDied() { parent_ = nullptr; }
+ //virtual ~TaskBarImpl(){}
+
+ void dontCallbackAnymore() { onRequestResume_ = nullptr; }
private:
virtual wxMenu* CreatePopupMenu()
{
- if (!parent_)
+ if (!onRequestResume_)
return nullptr;
wxMenu* contextMenu = new wxMenu;
- contextMenu->Append(CONTEXT_ABOUT, _("&About"));
- contextMenu->AppendSeparator();
contextMenu->Append(CONTEXT_RESTORE, _("&Restore"));
+ contextMenu->AppendSeparator();
+ contextMenu->Append(CONTEXT_ABOUT, _("&About"));
//event handling
- contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FfsTrayIcon::OnContextMenuSelection), nullptr, parent_);
+ contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TaskBarImpl::OnContextMenuSelection), nullptr, this);
return contextMenu; //ownership transferred to caller
}
- FfsTrayIcon* parent_;
+ void OnContextMenuSelection(wxCommandEvent& event)
+ {
+ switch (static_cast<Selection>(event.GetId()))
+ {
+ case CONTEXT_ABOUT:
+ {
+ //ATTENTION: the modal dialog below does NOT disable all GUI input, e.g. user may still double-click on tray icon
+ //which will implicitly destroy the tray icon while still showing the modal dialog
+ SetEvtHandlerEnabled(false);
+ ZEN_ON_SCOPE_EXIT(SetEvtHandlerEnabled(true));
+
+ zen::showAboutDialog(nullptr);
+ }
+ break;
+
+ case CONTEXT_RESTORE:
+ if (onRequestResume_)
+ onRequestResume_();
+ break;
+ }
+ }
+
+ void OnDoubleClick(wxCommandEvent& event)
+ {
+ if (onRequestResume_)
+ onRequestResume_();
+ }
+
+ std::function<void()> onRequestResume_;
};
-FfsTrayIcon::FfsTrayIcon() :
- trayIcon(new TaskBarImpl(*this)),
- fractionLast(1), //show FFS logo by default
+FfsTrayIcon::FfsTrayIcon(const std::function<void()>& onRequestResume) :
+ trayIcon(new TaskBarImpl(onRequestResume)),
+ activeFraction(1), //show FFS logo by default
#if defined ZEN_WIN || defined ZEN_MAC //16x16 seems to be the only size that is shown correctly on OS X
logo(getResourceImage(L"FFS_tray_16x16").ConvertToImage())
#elif defined ZEN_LINUX
logo(getResourceImage(L"FFS_tray_24x24").ConvertToImage())
#endif
{
- trayIcon->SetIcon(generateProgressIcon(logo, fractionLast), L"FreeFileSync");
- trayIcon->Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(FfsTrayIcon::OnDoubleClick), nullptr, this); //register double-click
+ trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), L"FreeFileSync");
}
FfsTrayIcon::~FfsTrayIcon()
{
- trayIcon->RemoveIcon(); //hide icon until final deletion takes place
- trayIcon->Disconnect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(FfsTrayIcon::OnDoubleClick), nullptr, this);
- trayIcon->parentHasDied(); //TaskBarImpl (potentially) has longer lifetime than FfsTrayIcon: avoid callback!
-
- //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking)
- if (!wxPendingDelete.Member(trayIcon))
- wxPendingDelete.Append(trayIcon);
-}
+ trayIcon->dontCallbackAnymore(); //TaskBarImpl has longer lifetime than FfsTrayIcon: avoid callback!
+ /*
+ This is not working correctly on OS X! It seems both wxTaskBarIcon::RemoveIcon() and ~wxTaskBarIcon() are broken and do NOT immediately
+ remove the icon from the system tray! Only some time later in the event loop which called these functions they will be removed.
+ Maybe some system component has still shared ownership? Objective C auto release pools are freed at the end of the current event loop...
+ Anyway, wxWidgets fails to disconnect the wxTaskBarIcon event handlers before calling "[m_statusitem release]"!
-void FfsTrayIcon::setToolTip(const wxString& toolTip)
-{
- toolTipLast = toolTip;
- trayIcon->SetIcon(generateProgressIcon(logo, fractionLast), toolTip); //another wxWidgets design bug: non-orthogonal method!
-}
+ => !!!clicking on the icon after ~wxTaskBarIcon ran crashes the application!!!
+ - if ~wxTaskBarIcon() ran from the SyncProgressDialog::updateGui() event loop (e.g. user manually clicking the icon) => icon removed on return
+ - if ~wxTaskBarIcon() ran from SyncProgressDialog::closeWindowDirectly() => leaves the icon dangling until user closes this dialog and outter event loop runs!
+ */
-void FfsTrayIcon::setProgress(double fraction)
-{
- fractionLast = fraction;
- trayIcon->SetIcon(generateProgressIcon(logo, fraction), toolTipLast);
+ trayIcon->RemoveIcon(); //required on Windows: unlike on OS X, wxPendingDelete does not kick in before main event loop!
+ //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking)
+ wxPendingDelete.Append(trayIcon);
}
-void FfsTrayIcon::OnContextMenuSelection(wxCommandEvent& event)
+void FfsTrayIcon::setToolTip(const wxString& toolTip)
{
- switch (static_cast<Selection>(event.GetId()))
- {
- case CONTEXT_ABOUT:
- {
- //ATTENTION: the modal dialog below does NOT disable all GUI input, e.g. user may still double-click on tray icon
- //which will implicitly destroy the tray icon while still showing the modal dialog
- trayIcon->SetEvtHandlerEnabled(false);
- zen::showAboutDialog(nullptr);
- trayIcon->SetEvtHandlerEnabled(true);
- }
- break;
- case CONTEXT_RESTORE:
- {
- wxCommandEvent dummy(FFS_REQUEST_RESUME_TRAY_EVENT);
- ProcessEvent(dummy);
- }
- }
+ activeToolTip = toolTip;
+ trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip); //another wxWidgets design bug: non-orthogonal method!
}
-void FfsTrayIcon::OnDoubleClick(wxCommandEvent& event)
+void FfsTrayIcon::setProgress(double fraction)
{
- wxCommandEvent dummy(FFS_REQUEST_RESUME_TRAY_EVENT);
- ProcessEvent(dummy);
+ activeFraction = fraction;
+ trayIcon->SetIcon(generateProgressIcon(logo, activeFraction), activeToolTip);
}
-
diff --git a/ui/tray_icon.h b/ui/tray_icon.h
index 866d79d5..24c97eb0 100644
--- a/ui/tray_icon.h
+++ b/ui/tray_icon.h
@@ -4,38 +4,42 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef TRAYICON_H_INCLUDED
-#define TRAYICON_H_INCLUDED
+#ifndef TRAYICON_H_84217830427534285
+#define TRAYICON_H_84217830427534285
-#include <wx/event.h>
+#include <functional>
#include <wx/image.h>
-//show tray icon with progress during lifetime of this instance
-//emits the following wxCommandEvent in case user double-clicks on tray icon or selects corresponding context menu item:
-extern const wxEventType FFS_REQUEST_RESUME_TRAY_EVENT;
+/*
+show tray icon with progress during lifetime of this instance
-class FfsTrayIcon : public wxEvtHandler
+ATTENTION: wxWidgets never assumes that an object indirectly destroys itself while processing an event!
+ this includes wxEvtHandler-derived objects!!!
+ it seems ProcessEvent() works (on Windows), but AddPendingEvent() will crash since it uses "this" after the event processing!
+
+=> don't derive from wxEvtHandler or any other wxWidgets object here!!!!!!
+=> use simple std::function as callback instead => instance may now be safely deleted in callback!
+*/
+
+class FfsTrayIcon
{
public:
- FfsTrayIcon();
+ FfsTrayIcon(const std::function<void()>& onRequestResume); //callback only held during lifetime of this instance
~FfsTrayIcon();
void setToolTip(const wxString& toolTip);
void setProgress(double fraction); //number between [0, 1], for small progress indicator
private:
- FfsTrayIcon(const FfsTrayIcon&);
- FfsTrayIcon& operator=(const FfsTrayIcon&);
-
- void OnContextMenuSelection(wxCommandEvent& event);
- void OnDoubleClick(wxCommandEvent& event);
+ FfsTrayIcon(const FfsTrayIcon&); //=delete
+ FfsTrayIcon& operator=(const FfsTrayIcon&); //=delete
class TaskBarImpl;
- TaskBarImpl* trayIcon; //actual tray icon (don't use inheritance to enable delayed deletion)
+ TaskBarImpl* trayIcon;
- wxString toolTipLast;
- double fractionLast;
+ wxString activeToolTip;
+ double activeFraction;
wxImage logo;
};
-#endif // TRAYICON_H_INCLUDED
+#endif //TRAYICON_H_84217830427534285
diff --git a/ui/tree_view.cpp b/ui/tree_view.cpp
index 10037380..791fa6cc 100644
--- a/ui/tree_view.cpp
+++ b/ui/tree_view.cpp
@@ -46,11 +46,11 @@ void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in
if (fileObj.isActive())
switch (fileObj.getSyncDir())
{
- case SYNC_DIR_LEFT:
+ case SyncDirection::LEFT:
return fileObj.getFileSize<RIGHT_SIDE>();
- case SYNC_DIR_RIGHT:
+ case SyncDirection::RIGHT:
return fileObj.getFileSize<LEFT_SIDE>();
- case SYNC_DIR_NONE:
+ case SyncDirection::NONE:
break;
}
return std::max(fileObj.getFileSize<LEFT_SIDE>(), fileObj.getFileSize<RIGHT_SIDE>());
diff --git a/version/version.h b/version/version.h
index 9bf4aba7..42589d07 100644
--- a/version/version.h
+++ b/version/version.h
@@ -3,7 +3,7 @@
namespace zen
{
- const wchar_t currentVersion[] = L"5.18"; //internal linkage!
+const wchar_t currentVersion[] = L"5.19"; //internal linkage!
}
#endif
diff --git a/wx+/dc.h b/wx+/dc.h
new file mode 100644
index 00000000..d0f5c805
--- /dev/null
+++ b/wx+/dc.h
@@ -0,0 +1,127 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef DC_3813704987123956832143243214
+#define DC_3813704987123956832143243214
+
+#include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER
+
+namespace zen
+{
+/*
+1. wxDCClipper does *not* stack: another fix for yet another poor wxWidgets implementation
+
+class RecursiveDcClipper
+{
+ RecursiveDcClipper(wxDC& dc, const wxRect& r) : dc_(dc)
+};
+
+------------------------------------------------------------------------------------------------
+
+2. wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active: a fix for a poor wxWidgets implementation
+class BufferedPaintDC
+{
+ BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer);
+};
+*/
+
+
+
+
+
+
+
+
+
+
+
+//---------------------- implementation ------------------------
+class RecursiveDcClipper
+{
+public:
+ RecursiveDcClipper(wxDC& dc, const wxRect& r) : dc_(dc)
+ {
+ auto it = refDcToAreaMap().find(&dc);
+ if (it != refDcToAreaMap().end())
+ {
+ oldRect.reset(new wxRect(it->second));
+
+ wxRect tmp = r;
+ tmp.Intersect(*oldRect); //better safe than sorry
+ dc_.SetClippingRegion(tmp); //
+ it->second = tmp;
+ }
+ else
+ {
+ dc_.SetClippingRegion(r);
+ refDcToAreaMap().insert(std::make_pair(&dc_, r));
+ }
+ }
+
+ ~RecursiveDcClipper()
+ {
+ dc_.DestroyClippingRegion();
+ if (oldRect.get() != nullptr)
+ {
+ dc_.SetClippingRegion(*oldRect);
+ refDcToAreaMap()[&dc_] = *oldRect;
+ }
+ else
+ refDcToAreaMap().erase(&dc_);
+ }
+
+private:
+ //associate "active" clipping area with each DC
+ static hash_map<wxDC*, wxRect>& refDcToAreaMap() { static hash_map<wxDC*, wxRect> clippingAreas; return clippingAreas; }
+
+ std::unique_ptr<wxRect> oldRect;
+ wxDC& dc_;
+};
+
+
+#ifndef wxALWAYS_NATIVE_DOUBLE_BUFFER
+#error we need this one!
+#endif
+
+#if wxALWAYS_NATIVE_DOUBLE_BUFFER
+struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : wxPaintDC(&wnd) {} };
+
+#else
+class BufferedPaintDC : public wxMemoryDC
+{
+public:
+ BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : buffer_(buffer), paintDc(&wnd)
+ {
+ const wxSize clientSize = wnd.GetClientSize();
+ if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight()))
+ buffer.reset(new wxBitmap(clientSize.GetWidth(), clientSize.GetHeight()));
+
+ SelectObject(*buffer);
+
+ if (paintDc.IsOk() && paintDc.GetLayoutDirection() == wxLayout_RightToLeft)
+ SetLayoutDirection(wxLayout_RightToLeft);
+ }
+
+ ~BufferedPaintDC()
+ {
+ if (GetLayoutDirection() == wxLayout_RightToLeft)
+ {
+ paintDc.SetLayoutDirection(wxLayout_LeftToRight); //workaround bug in wxDC::Blit()
+ SetLayoutDirection(wxLayout_LeftToRight); //
+ }
+
+ const wxPoint origin = GetDeviceOrigin();
+ paintDc.Blit(0, 0, buffer_->GetWidth(), buffer_->GetHeight(), this, -origin.x, -origin.y);
+ }
+
+private:
+ std::unique_ptr<wxBitmap>& buffer_;
+ wxPaintDC paintDc;
+};
+#endif
+}
+
+#endif //DC_3813704987123956832143243214
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index 0d14ae69..cbedfa53 100644
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -9,8 +9,9 @@
#include <algorithm>
#include <numeric>
#include <zen/basic_math.h>
+#include <zen/scope_guard.h>
#include <wx/settings.h>
-#include "rtl.h"
+#include "dc.h"
using namespace zen;
@@ -31,8 +32,8 @@ double zen::nextNiceNumber(double blockSize) //round to next number which is a c
const double e = std::pow(10, k);
if (numeric::isNull(e))
return 0;
- const double a = blockSize / e; //blockSize = a * 10^k with a in (1, 10)
- assert(1 < a && a < 10);
+ const double a = blockSize / e; //blockSize = a * 10^k with a in [1, 10)
+ assert(1 <= a && a < 10);
//have a look at leading two digits: "nice" numbers start with 1, 2, 2.5 and 5
const double steps[] = { 1, 2, 2.5, 5, 10 };
@@ -78,18 +79,22 @@ public:
ConvertCoord(double valMin, double valMax, size_t screenSize) :
min_(valMin),
scaleToReal(screenSize == 0 ? 0 : (valMax - valMin) / screenSize),
- scaleToScr(numeric::isNull((valMax - valMin)) ? 0 : screenSize / (valMax - valMin)) {}
+ scaleToScr(numeric::isNull((valMax - valMin)) ? 0 : screenSize / (valMax - valMin)),
+ outOfBoundsLow (-1 * scaleToReal + valMin),
+ outOfBoundsHigh((screenSize + 1) * scaleToReal + valMin) { if (outOfBoundsLow > outOfBoundsHigh) std::swap(outOfBoundsLow, outOfBoundsHigh); }
double screenToReal(double screenPos) const //input value: [0, screenSize - 1]
{
- return screenPos * scaleToReal + min_; //come close to valMax, but NEVER reach it!
+ return screenPos * scaleToReal + min_;
}
double realToScreen(double realPos) const //return screen position in pixel (but with double precision!)
{
return (realPos - min_) * scaleToScr;
}
- int realToScreenRound(double realPos) const //useful to find "proper" y-pixel positions
+ int realToScreenRound(double realPos) const //returns -1 and screenSize + 1 if out of bounds!
{
+ //catch large double values: if double is larger than what int can represent => undefined behavior!
+ numeric::confine(realPos , outOfBoundsLow, outOfBoundsHigh);
return numeric::round(realToScreen(realPos));
}
@@ -97,19 +102,22 @@ private:
double min_;
double scaleToReal;
double scaleToScr;
+
+ double outOfBoundsLow;
+ double outOfBoundsHigh;
};
//enlarge value range to display to a multiple of a "useful" block size
void widenRange(double& valMin, double& valMax, //in/out
int& blockCount, //out
- int graphAreaSize, //in pixel
- int optimalBlockSize, //
+ int graphAreaSize, //in pixel
+ int optimalBlockSizePx, //
const LabelFormatter& labelFmt)
{
if (graphAreaSize > 0)
{
- double valRangePerBlock = (valMax - valMin) * optimalBlockSize / graphAreaSize; //proposal
+ double valRangePerBlock = (valMax - valMin) * optimalBlockSizePx / graphAreaSize; //proposal
valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock);
if (!numeric::isNull(valRangePerBlock))
{
@@ -145,7 +153,7 @@ void drawXLabel(wxDC& dc, double xMin, double xMax, int blockCount, const Conver
//draw x axis labels
const wxString label = labelFmt.formatText(valX, valRangePerBlock);
- wxSize labelExtent = dc.GetMultiLineTextExtent(label);
+ const wxSize labelExtent = dc.GetMultiLineTextExtent(label);
dc.DrawText(label, wxPoint(x - labelExtent.GetWidth() / 2, labelArea.y + (labelArea.height - labelExtent.GetHeight()) / 2)); //center
}
}
@@ -173,31 +181,223 @@ void drawYLabel(wxDC& dc, double yMin, double yMax, int blockCount, const Conver
//draw y axis labels
const wxString label = labelFmt.formatText(valY, valRangePerBlock);
- wxSize labelExtent = dc.GetMultiLineTextExtent(label);
+ const wxSize labelExtent = dc.GetMultiLineTextExtent(label);
dc.DrawText(label, wxPoint(labelArea.x + (labelArea.width - labelExtent.GetWidth()) / 2, y - labelExtent.GetHeight() / 2)); //center
}
}
-template <class StdContainter>
-void subsample(StdContainter& cont, size_t factor)
+void drawCornerText(wxDC& dc, const wxRect& graphArea, const wxString& txt, Graph2D::PosCorner pos)
{
- if (factor <= 1) return;
+ if (txt.empty()) return;
+ const int borderX = 5;
+ const int borderY = 2; //it looks like wxDC::GetMultiLineTextExtent() precisely returns width, but too large a height: maybe they consider "text row height"?
- auto itOut = cont.begin();
- for (auto itIn = cont.begin(); cont.end() - itIn >= static_cast<ptrdiff_t>(factor); itIn += factor) //don't even let iterator point out of range!
- *itOut++ = std::accumulate(itIn, itIn + factor, 0.0) / static_cast<double>(factor);
+ wxDCTextColourChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels
+ wxSize txtExtent = dc.GetMultiLineTextExtent(txt);
+ txtExtent.x += 2 * borderX;
+ txtExtent.y += 2 * borderY;
- cont.erase(itOut, cont.end());
+ wxPoint drawPos = graphArea.GetTopLeft();
+ switch (pos)
+ {
+ case Graph2D::CORNER_TOP_LEFT:
+ break;
+ case Graph2D::CORNER_TOP_RIGHT:
+ drawPos.x += graphArea.width - txtExtent.GetWidth();
+ break;
+ case Graph2D::CORNER_BOTTOM_LEFT:
+ drawPos.y += graphArea.height - txtExtent.GetHeight();
+ break;
+ case Graph2D::CORNER_BOTTOM_RIGHT:
+ drawPos.x += graphArea.width - txtExtent.GetWidth();
+ drawPos.y += graphArea.height - txtExtent.GetHeight();
+ break;
+ }
+ dc.DrawText(txt, drawPos + wxPoint(borderX, borderY));
}
+
+
+warn_static("review")
+
+template <class Function, class Function2>
+void cutPoints(std::vector<CurvePoint>& curvePoints, std::vector<char>& oobMarker, Function isInside, Function2 getIntersection)
+{
+ assert(curvePoints.size() == oobMarker.size());
+ if (curvePoints.size() != oobMarker.size() || curvePoints.empty()) return;
+ auto isMarkedOob = [&](size_t index) { return oobMarker[index] != 0; };
+
+ std::vector<CurvePoint> curvePointsTmp;
+ std::vector<char> oobMarkerTmp;
+ auto savePoint = [&](const CurvePoint& pt, bool markedOob) { curvePointsTmp.push_back(pt); oobMarkerTmp.push_back(markedOob); };
+
+ warn_static("perf: avoid these push_backs")
+
+ bool lastPointInside = isInside(curvePoints[0]);
+ if (lastPointInside)
+ savePoint(curvePoints[0], isMarkedOob(0));
+
+ for (auto it = curvePoints.begin() + 1; it != curvePoints.end(); ++it)
+ {
+ const size_t index = it - curvePoints.begin();
+
+ const bool pointInside = isInside(*it);
+ if (pointInside != lastPointInside)
+ {
+ lastPointInside = pointInside;
+
+ const CurvePoint is = getIntersection(*(it - 1), *it); //getIntersection returns *it when delta is zero
+ savePoint(is, !pointInside || isMarkedOob(index - 1));
+ }
+ if (pointInside)
+ savePoint(*it, isMarkedOob(index));
+ }
+ curvePointsTmp.swap(curvePoints);
+ oobMarkerTmp .swap(oobMarker);
+}
+
+
+struct GetIntersectionX
+{
+ GetIntersectionX(double x) : x_(x) {}
+ CurvePoint operator()(const CurvePoint& from, const CurvePoint& to) const
+ {
+ const double deltaX = to.x - from.x;
+ const double deltaY = to.y - from.y;
+ return !numeric::isNull(deltaX) ? CurvePoint(x_, from.y + (x_ - from.x) / deltaX * deltaY) : to;
+ };
+private:
+ double x_;
+};
+
+struct GetIntersectionY
+{
+ GetIntersectionY(double y) : y_(y) {}
+ CurvePoint operator()(const CurvePoint& from, const CurvePoint& to) const
+ {
+ const double deltaX = to.x - from.x;
+ const double deltaY = to.y - from.y;
+ return !numeric::isNull(deltaY) ? CurvePoint(from.x + (y_ - from.y) / deltaY * deltaX, y_) : to;
+ };
+private:
+ double y_;
+};
+
+void cutPointsOutsideX(std::vector<CurvePoint>& curvePoints, std::vector<char>& oobMarker, double minX, double maxX)
+{
+ cutPoints(curvePoints, oobMarker, [&](const CurvePoint& pt) { return pt.x >= minX; }, GetIntersectionX(minX));
+ cutPoints(curvePoints, oobMarker, [&](const CurvePoint& pt) { return pt.x <= maxX; }, GetIntersectionX(maxX));
+}
+
+void cutPointsOutsideY(std::vector<CurvePoint>& curvePoints, std::vector<char>& oobMarker, double minY, double maxY)
+{
+ cutPoints(curvePoints, oobMarker, [&](const CurvePoint& pt) { return pt.y >= minY; }, GetIntersectionY(minY));
+ cutPoints(curvePoints, oobMarker, [&](const CurvePoint& pt) { return pt.y <= maxY; }, GetIntersectionY(maxY));
+}
+}
+
+
+warn_static("review")
+void ContinuousCurveData::getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const
+{
+ if (pixelWidth <= 1) return;
+ const ConvertCoord cvrtX(minX, maxX, pixelWidth - 1); //map [minX, maxX] to [0, pixelWidth - 1]
+
+ std::pair<double, double> rangeX = getRangeX();
+ //catch large double values: if double is larger than what int can represent => undefined behavior!
+ const double xOutOfBoundsLow = cvrtX.screenToReal(-1);
+ const double xOutOfBoundsHigh = cvrtX.screenToReal(pixelWidth);
+ numeric::confine(rangeX.first , xOutOfBoundsLow, xOutOfBoundsHigh); //don't confine to [minX, maxX] which
+ numeric::confine(rangeX.second, xOutOfBoundsLow, xOutOfBoundsHigh); //would prevent empty ranges
+
+ const int posFrom = std::ceil (cvrtX.realToScreen(std::max(rangeX.first, minX))); //do not step outside [minX, maxX]
+ const int posTo = std::floor(cvrtX.realToScreen(std::min(rangeX.second, maxX))); //
+ //conversion from std::floor/std::ceil double return value to int is loss-free for full value range of 32-bit int! tested successfully on MSVC
+
+ for (int i = posFrom; i <= posTo; ++i)
+ {
+ const double x = cvrtX.screenToReal(i);
+ points.push_back(CurvePoint(x, getValue(x)));
+ }
+}
+
+
+void SparseCurveData::getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const
+{
+ if (pixelWidth <= 1) return;
+ const ConvertCoord cvrtX(minX, maxX, pixelWidth - 1); //map [minX, maxX] to [0, pixelWidth - 1]
+ const std::pair<double, double> rangeX = getRangeX();
+
+ auto addPoint = [&](const CurvePoint& pt)
+ {
+ warn_static("verify steps")
+ if (addSteps_ && !points.empty())
+ if (pt.y != points.back().y)
+ points.push_back(CurvePoint(pt.x, points.back().y));
+ points.push_back(pt);
+ };
+
+ const int posFrom = cvrtX.realToScreenRound(std::max(rangeX.first, minX));
+ const int posTo = cvrtX.realToScreenRound(std::min(rangeX.second, maxX));
+
+ for (int i = posFrom; i <= posTo; ++i)
+ {
+ const double x = cvrtX.screenToReal(i);
+ Opt<CurvePoint> ptLe = getLessEq(x);
+ Opt<CurvePoint> ptGe = getGreaterEq(x);
+ //both non-existent and invalid return values are mapped to out of expected range: => check on posLe/posGe NOT ptLe/ptGE in the following!
+ const int posLe = ptLe ? cvrtX.realToScreenRound(ptLe->x) : i + 1;
+ const int posGe = ptGe ? cvrtX.realToScreenRound(ptGe->x) : i - 1;
+ assert(!ptLe || posLe <= i); //check for invalid return values
+ assert(!ptGe || posGe >= i); //
+
+ if (posGe == i) //test if point would be mapped to pixel x-position i
+ {
+ if (posLe == i) //
+ addPoint(x - ptLe->x < ptGe->x - x ? *ptLe : *ptGe);
+ else
+ addPoint(*ptGe);
+ }
+ else
+ {
+ if (posLe == i)
+ addPoint(*ptLe);
+ else //no point for x-position i
+ {
+ if (i == posFrom && posGe > i)
+ {
+ if (posLe < i)
+ addPoint(*ptLe); //use first point outside display area!
+ else if (posGe > posTo) //curve starts outside the draw range!
+ break;
+ }
+ }
+
+ if (posGe < i)
+ break;
+
+ if (posGe > posTo) //last point outside the display area!
+ {
+ if (i == posTo && posLe == i) //no need for outside point if last position was already set above
+ break;
+
+ addPoint(*ptGe);
+ break;
+ }
+ if (posGe > i) //skip sparse area
+ i = posGe - 1;
+ }
+ }
}
+
Graph2D::Graph2D(wxWindow* parent,
wxWindowID winid,
const wxPoint& pos,
const wxSize& size,
long style,
- const wxString& name) : wxPanel(parent, winid, pos, size, style, name)
+ const wxString& name) : wxPanel(parent, winid, pos, size, style, name),
+ labelFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial")
{
Connect(wxEVT_PAINT, wxPaintEventHandler(Graph2D::onPaintEvent), nullptr, this);
Connect(wxEVT_SIZE, wxSizeEventHandler (Graph2D::onSizeEvent ), nullptr, this);
@@ -272,14 +472,14 @@ void Graph2D::OnMouseCaptureLost(wxMouseCaptureLostEvent& event)
}
-void Graph2D::setData(const std::shared_ptr<GraphData>& data, const CurveAttributes& ca)
+void Graph2D::setCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca)
{
curves_.clear();
- addData(data, ca);
+ addCurve(data, ca);
}
-void Graph2D::addData(const std::shared_ptr<GraphData>& data, const CurveAttributes& ca)
+void Graph2D::addCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca)
{
CurveAttributes newAttr = ca;
if (newAttr.autoColor)
@@ -288,22 +488,13 @@ void Graph2D::addData(const std::shared_ptr<GraphData>& data, const CurveAttribu
Refresh();
}
-namespace //putting this into function scope makes MSVC crash...
-{
-struct CurveSamples
-{
- CurveSamples() : offsetX(0) {}
- std::vector<double> yValues; //actual y-values at each screen pixel position
- int offsetX; //x-value offset in pixels
-};
-}
void Graph2D::render(wxDC& dc) const
{
using namespace numeric;
//set label font right at the start so that it is considered by wxDC::GetTextExtent below!
- dc.SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial"));
+ dc.SetFont(labelFont);
const wxRect clientRect = GetClientRect(); //DON'T use wxDC::GetSize()! DC may be larger than visible area!
{
@@ -365,27 +556,25 @@ void Graph2D::render(wxDC& dc) const
//set label areas respecting graph area border!
const wxRect xLabelArea(graphArea.x, xLabelPosY, graphArea.width, attr.xLabelHeight);
const wxRect yLabelArea(yLabelPosX, graphArea.y, attr.yLabelWidth, graphArea.height);
-
const wxPoint graphAreaOrigin = graphArea.GetTopLeft();
//detect x value range
double minX = attr.minXauto ? std::numeric_limits<double>::infinity() : attr.minX; //automatic: ensure values are initialized by first curve
double maxX = attr.maxXauto ? -std::numeric_limits<double>::infinity() : attr.maxX; //
- if (!curves_.empty())
- for (auto it = curves_.begin(); it != curves_.end(); ++it)
- if (it->first.get())
- {
- const GraphData& graph = *it->first;
- assert(graph.getXBegin() <= graph.getXEnd() + 1.0e-9);
- //GCC fucks up badly when comparing two *binary identical* doubles and finds "begin > end" with diff of 1e-18
-
- if (attr.minXauto)
- minX = std::min(minX, graph.getXBegin());
- if (attr.maxXauto)
- maxX = std::max(maxX, graph.getXEnd());
- }
+ for (auto it = curves_.begin(); it != curves_.end(); ++it)
+ if (const CurveData* curve = it->first.get())
+ {
+ const std::pair<double, double> rangeX = curve->getRangeX();
+ assert(rangeX.first <= rangeX.second + 1.0e-9);
+ //GCC fucks up badly when comparing two *binary identical* doubles and finds "begin > end" with diff of 1e-18
+
+ if (attr.minXauto)
+ minX = std::min(minX, rangeX.first);
+ if (attr.maxXauto)
+ maxX = std::max(maxX, rangeX.second);
+ }
- if (minX < maxX && maxX - minX < std::numeric_limits<double>::infinity()) //valid x-range
+ if (minX <= maxX && maxX - minX < std::numeric_limits<double>::infinity()) //valid x-range
{
int blockCountX = 0;
//enlarge minX, maxX to a multiple of a "useful" block size
@@ -396,39 +585,37 @@ void Graph2D::render(wxDC& dc) const
dc.GetTextExtent(L"100000000000000").GetWidth(),
*attr.labelFmtX);
- //detect y value range
- std::vector<CurveSamples> yValuesList(curves_.size());
+ //get raw values + detect y value range
double minY = attr.minYauto ? std::numeric_limits<double>::infinity() : attr.minY; //automatic: ensure values are initialized by first curve
double maxY = attr.maxYauto ? -std::numeric_limits<double>::infinity() : attr.maxY; //
- {
- const int AVG_FACTOR = 2; //some averaging of edgy input data to smoothen behavior on window resize
- const ConvertCoord cvrtX(minX, maxX, graphArea.width * AVG_FACTOR);
- for (auto it = curves_.begin(); it != curves_.end(); ++it)
- if (const GraphData* graph = it->first.get())
- {
- CurveSamples& samples = yValuesList[it - curves_.begin()];
- {
- const int posFirst = std::ceil(cvrtX.realToScreen(std::max(graph->getXBegin(), minX))); //do not step outside [xBegin, xEnd) range => 2 x ceil!
- const int posLast = std::ceil(cvrtX.realToScreen(std::min(graph->getXEnd (), maxX))); //
- //conversion from std::ceil double return valute to int is loss-free for full value range of 32-bit int! tested successfully on MSVC
+ std::vector<std::vector<CurvePoint>> curvePoints(curves_.size());
+ std::vector<std::vector<char>> oobMarker (curves_.size()); //effectively a std::vector<bool> marking points that start a out of bounds line
- for (int i = posFirst; i < posLast; ++i)
- samples.yValues.push_back(graph->getValue(cvrtX.screenToReal(i)));
+ for (auto it = curves_.begin(); it != curves_.end(); ++it)
+ if (const CurveData* curve = it->first.get())
+ {
+ const size_t index = it - curves_.begin();
+ std::vector<CurvePoint>& points = curvePoints[index];
+ auto& marker = oobMarker[index];
- subsample(samples.yValues, AVG_FACTOR);
- samples.offsetX = posFirst / AVG_FACTOR;
- }
- if (!samples.yValues.empty())
- {
- if (attr.minYauto)
- minY = std::min(minY, *std::min_element(samples.yValues.begin(), samples.yValues.end()));
- if (attr.maxYauto)
- maxY = std::max(maxY, *std::max_element(samples.yValues.begin(), samples.yValues.end()));
- }
+ curve->getPoints(minX, maxX, graphArea.width, points);
+
+ //cut points outside visible x-range now in order to calculate height of visible points only!
+ marker.resize(points.size()); //default value: false
+ cutPointsOutsideX(points, marker, minX, maxX);
+
+ if ((attr.minYauto || attr.maxYauto) && !points.empty())
+ {
+ auto itPair = std::minmax_element(points.begin(), points.end(), [](const CurvePoint& lhs, const CurvePoint& rhs) { return lhs.y < rhs.y; });
+ if (attr.minYauto)
+ minY = std::min(minY, itPair.first->y);
+ if (attr.maxYauto)
+ maxY = std::max(maxY, itPair.second->y);
}
- }
- if (minY < maxY) //valid y-range
+ }
+
+ if (minY <= maxY) //valid y-range
{
int blockCountY = 0;
//enlarge minY, maxY to a multiple of a "useful" block size
@@ -439,44 +626,55 @@ void Graph2D::render(wxDC& dc) const
3 * dc.GetTextExtent(L"1").GetHeight(),
*attr.labelFmtY);
- const ConvertCoord cvrtX(minX, maxX, graphArea.width); //map [minX, maxX) to [0, graphWidth)
- const ConvertCoord cvrtY(maxY, minY, graphArea.height <= 0 ? 0 : graphArea.height - 1); //map [minY, maxY] to [graphHeight - 1, 0]
+ if (graphArea.width <= 1 || graphArea.height <= 1) return;
+ const ConvertCoord cvrtX(minX, maxX, graphArea.width - 1); //map [minX, maxX] to [0, pixelWidth - 1]
+ const ConvertCoord cvrtY(maxY, minY, graphArea.height - 1); //map [minY, maxY] to [pixelHeight - 1, 0]
//calculate curve coordinates on graph area
- auto getCurvePoints = [&](size_t index, std::vector<wxPoint>& points)
- {
- if (index < yValuesList.size())
- {
- CurveSamples& samples = yValuesList[index];
+ std::vector<std::vector<wxPoint>> drawPoints(curves_.size());
- for (auto it = samples.yValues.begin(); it != samples.yValues.end(); ++it)
- points.push_back(wxPoint(samples.offsetX + (it - samples.yValues.begin()),
- cvrtY.realToScreenRound(*it)) + graphAreaOrigin);
- }
- };
+ for (size_t index = 0; index < curves_.size(); ++index)
+ {
+ //cut points outside visible y-range before calculating pixels:
+ //1. realToScreenRound() deforms out-of-range values!
+ //2. pixels that are grossly out of range may become a severe performance problem when drawing on the DC (Windows)
+ cutPointsOutsideY(curvePoints[index], oobMarker[index], minY, maxY);
+
+ auto& points = drawPoints[index];
+ for (const auto& pt : curvePoints[index])
+ points.push_back(wxPoint(cvrtX.realToScreenRound(pt.x),
+ cvrtY.realToScreenRound(pt.y)) + graphAreaOrigin);
+ }
//update active mouse selection
if (activeSel.get() &&
graphArea.width > 0 && graphArea.height > 0)
{
- wxPoint startPos = activeSel->getStartPos() - graphAreaOrigin; //make relative to graphArea
- wxPoint currentPos = activeSel->refCurrentPos() - graphAreaOrigin;
-
- //normalize positions: a mouse selection is symmetric and *not* an half-open range!
- confine(startPos .x, 0, graphArea.width - 1);
- confine(currentPos.x, 0, graphArea.width - 1);
- confine(startPos .y, 0, graphArea.height - 1);
- confine(currentPos.y, 0, graphArea.height - 1);
-
- auto& from = activeSel->refSelection().from;
- auto& to = activeSel->refSelection().to;
+ auto widen = [](double* low, double* high)
+ {
+ if (*low > *high)
+ std::swap(low, high);
+ *low -= 0.5;
+ *high += 0.5;
+ };
- //save current selection as double coordinates
- from.x = cvrtX.screenToReal(startPos .x + (startPos.x <= currentPos.x ? 0 : 1)); // use full pixel range for selection!
- to .x = cvrtX.screenToReal(currentPos.x + (startPos.x <= currentPos.x ? 1 : 0));
+ const wxPoint screenStart = activeSel->getStartPos() - graphAreaOrigin; //make relative to graphArea
+ const wxPoint screenCurrent = activeSel->refCurrentPos() - graphAreaOrigin;
- from.y = cvrtY.screenToReal(startPos .y + (startPos.y <= currentPos.y ? 0 : 1));
- to .y = cvrtY.screenToReal(currentPos.y + (startPos.y <= currentPos.y ? 1 : 0));
+ //normalize positions: a mouse selection is symmetric and *not* an half-open range!
+ double screenFromX = confineCpy(screenStart .x, 0, graphArea.width - 1);
+ double screenFromY = confineCpy(screenStart .y, 0, graphArea.height - 1);
+ double screenToX = confineCpy(screenCurrent.x, 0, graphArea.width - 1);
+ double screenToY = confineCpy(screenCurrent.y, 0, graphArea.height - 1);
+ widen(&screenFromX, &screenToX); //use full pixel range for selection!
+ widen(&screenFromY, &screenToY);
+
+ //save current selection as "double" coordinates
+ activeSel->refSelection().from = CurvePoint(cvrtX.screenToReal(screenFromX),
+ cvrtY.screenToReal(screenFromY));
+
+ activeSel->refSelection().to = CurvePoint(cvrtX.screenToReal(screenToX),
+ cvrtY.screenToReal(screenToY));
}
//#################### begin drawing ####################
@@ -484,8 +682,7 @@ void Graph2D::render(wxDC& dc) const
for (auto it = curves_.begin(); it != curves_.end(); ++it)
if (it->second.drawCurveArea)
{
- std::vector<wxPoint> points;
- getCurvePoints(it - curves_.begin(), points);
+ std::vector<wxPoint> points = drawPoints[it - curves_.begin()];
if (!points.empty())
{
points.push_back(wxPoint(points.back ().x, graphArea.GetBottom())); //add lower right and left corners
@@ -502,36 +699,39 @@ void Graph2D::render(wxDC& dc) const
if (activeSel)
allSelections.push_back(activeSel->refSelection());
{
- //alpha channel (not yet) supported on wxMSW, so draw selection before curves
+ //alpha channel not supported on wxMSW, so draw selection before curves
wxDCBrushChanger dummy(dc, wxColor(168, 202, 236)); //light blue
wxDCPenChanger dummy2(dc, wxColor(51, 153, 255)); //dark blue
- for (auto it = allSelections.begin(); it != allSelections.end(); ++it)
+ auto shrink = [](double* low, double* high)
{
- //harmonize with active mouse selection above!
- wxPoint pixelFrom(cvrtX.realToScreenRound(it->from.x),
- cvrtY.realToScreenRound(it->from.y));
- wxPoint pixelTo(cvrtX.realToScreenRound(it->to.x),
- cvrtY.realToScreenRound(it->to.y));
- //convert half-open to inclusive ranges for use with wxDC::DrawRectangle
- if (pixelFrom.x != pixelTo.x) //no matter how small the selection, always draw at least one pixel!
- {
- pixelFrom.x -= pixelFrom.x < pixelTo.x ? 0 : 1;
- pixelTo .x -= pixelFrom.x < pixelTo.x ? 1 : 0;
- }
- if (pixelFrom.y != pixelTo.y)
- {
- pixelFrom.y -= pixelFrom.y < pixelTo.y ? 0 : 1;
- pixelTo .y -= pixelFrom.y < pixelTo.y ? 1 : 0;
- }
- confine(pixelFrom.x, 0, graphArea.width - 1);
- confine(pixelTo .x, 0, graphArea.width - 1);
- confine(pixelFrom.y, 0, graphArea.height - 1);
- confine(pixelTo .y, 0, graphArea.height - 1);
-
- pixelFrom += graphAreaOrigin;
- pixelTo += graphAreaOrigin;
+ if (*low > *high)
+ std::swap(low, high);
+ *low += 0.5;
+ *high -= 0.5;
+ if (*low > *high)
+ *low = *high = (*low + *high) / 2;
+ };
+ for (auto it = allSelections.begin(); it != allSelections.end(); ++it)
+ {
+ //harmonize with active mouse selection above
+ double screenFromX = cvrtX.realToScreen(it->from.x);
+ double screenFromY = cvrtY.realToScreen(it->from.y);
+ double screenToX = cvrtX.realToScreen(it->to.x);
+ double screenToY = cvrtY.realToScreen(it->to.y);
+ shrink(&screenFromX, &screenToX);
+ shrink(&screenFromY, &screenToY);
+
+ confine(screenFromX, 0.0, graphArea.width - 1.0);
+ confine(screenFromY, 0.0, graphArea.height - 1.0);
+ confine(screenToX, 0.0, graphArea.width - 1.0);
+ confine(screenToY, 0.0, graphArea.height - 1.0);
+
+ const wxPoint pixelFrom = wxPoint(numeric::round(screenFromX),
+ numeric::round(screenFromY)) + graphAreaOrigin;
+ const wxPoint pixelTo = wxPoint(numeric::round(screenToX),
+ numeric::round(screenToY)) + graphAreaOrigin;
switch (attr.mouseSelMode)
{
case SELECT_NONE:
@@ -554,17 +754,42 @@ void Graph2D::render(wxDC& dc) const
drawYLabel(dc, minY, maxY, blockCountY, cvrtY, graphArea, yLabelArea, *attr.labelFmtY);
//4. finally draw curves
- for (auto it = curves_.begin(); it != curves_.end(); ++it)
{
- std::vector<wxPoint> points;
- getCurvePoints(it - curves_.begin(), points);
- if (!points.empty())
+ dc.SetClippingRegion(graphArea); //prevent thick curves from drawing slightly outside
+ ZEN_ON_SCOPE_EXIT(dc.DestroyClippingRegion());
+
+ for (auto it = curves_.begin(); it != curves_.end(); ++it)
{
wxDCPenChanger dummy(dc, wxPen(it->second.color, it->second.lineWidth));
- dc.DrawLines(static_cast<int>(points.size()), &points[0]);
- dc.DrawPoint(points.back()); //wxDC::DrawLines() doesn't draw last pixel
+
+ const size_t index = it - curves_.begin();
+ std::vector<wxPoint>& points = drawPoints[index]; //alas wxDC::DrawLines() is not const-correct!!!
+ auto& marker = oobMarker [index];
+ assert(points.size() == marker.size());
+ warn_static("review")
+
+ //draw all parts of the curve except for the out-of-bounds ranges
+ size_t pointsIndexFirst = 0;
+ while (pointsIndexFirst < points.size())
+ {
+ size_t pointsIndexLast = std::find(marker.begin() + pointsIndexFirst, marker.end(), true) - marker.begin();
+ if (pointsIndexLast < points.size()) ++ pointsIndexLast;
+
+ const int pointCount = static_cast<int>(pointsIndexLast - pointsIndexFirst);
+ if (pointCount > 0)
+ {
+ if (pointCount >= 2) //on OS X wxWidgets has a nasty assert on this
+ dc.DrawLines(pointCount, &points[pointsIndexFirst]);
+ dc.DrawPoint(points[pointsIndexLast - 1]); //wxDC::DrawLines() doesn't draw last pixel
+ }
+ pointsIndexFirst = std::find(marker.begin() + pointsIndexLast, marker.end(), false) - marker.begin();
+ }
}
}
+
+ //5. draw corner texts
+ for (auto it = attr.cornerTexts.begin(); it != attr.cornerTexts.end(); ++it)
+ drawCornerText(dc, graphArea, it->second, it->first);
}
}
}
diff --git a/wx+/graph.h b/wx+/graph.h
index 8f816b08..fe008a38 100644
--- a/wx+/graph.h
+++ b/wx+/graph.h
@@ -7,13 +7,15 @@
#ifndef WX_PLOT_HEADER_2344252459
#define WX_PLOT_HEADER_2344252459
+#include <map>
#include <vector>
#include <memory>
#include <wx/panel.h>
-#include <wx/dcbuffer.h>
+//#include <wx/dcbuffer.h>
#include <zen/string_tools.h>
+#include <zen/optional.h>
-//simple 2D graph as wxPanel specialization
+//elegant 2D graph as wxPanel specialization
namespace zen
{
@@ -21,62 +23,90 @@ namespace zen
Example:
//init graph (optional)
m_panelGraph->setAttributes(Graph2D::MainAttributes().
- setLabelX(Graph2D::POSLX_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()).
- setLabelY(Graph2D::POSLY_RIGHT, 60, std::make_shared<LabelFormatterBytes>()));
+ setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()).
+ setLabelY(Graph2D::Y_LABEL_RIGHT, 60, std::make_shared<LabelFormatterBytes>()));
//set graph data
- std::shared_ptr<GraphData> graphDataBytes = ...
- m_panelGraph->setData(graphDataBytes, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0)));
+ std::shared_ptr<CurveData> curveDataBytes = ...
+ m_panelGraph->setCurve(curveDataBytes, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0)));
*/
-//------------------------------------------------------------------------------------------------------------
-struct GraphData
+struct CurvePoint
{
- virtual ~GraphData() {}
- virtual double getValue (double x) const = 0;
- virtual double getXBegin() const = 0;
- virtual double getXEnd () const = 0; //upper bound for x, getValue() is NOT evaluated at this position! Similar to std::vector::end()
+ CurvePoint() : x(0), y(0) {}
+ CurvePoint(double xVal, double yVal) : x(xVal), y(yVal) {}
+ double x;
+ double y;
};
+inline bool operator==(const CurvePoint& lhs, const CurvePoint& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; }
+inline bool operator!=(const CurvePoint& lhs, const CurvePoint& rhs) { return !(lhs == rhs); }
+struct CurveData
+{
+ virtual ~CurveData() {}
-//reference data implementation
-class RangeData : public GraphData
+ virtual std::pair<double, double> getRangeX() const = 0;
+ virtual void getPoints(double minX, double maxX, int pixelWidth,
+ std::vector<CurvePoint>& points) const = 0; //points outside the draw area are automatically trimmed!
+};
+
+//special curve types:
+struct ContinuousCurveData : public CurveData
{
-public:
- std::vector<double>& refData() { return data; }
+ virtual double getValue(double x) const = 0;
private:
- virtual double getValue(double x) const
- {
- const size_t pos = static_cast<size_t>(x);
- return pos < data.size() ? data[pos] : 0;
- }
- virtual double getXBegin() const { return 0; }
- virtual double getXEnd() const { return data.size(); } //example: two-element range is accessible within [0, 2)
+ virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const final;
+};
- std::vector<double> data;
+struct SparseCurveData : public CurveData
+{
+ SparseCurveData(bool addSteps = false) : addSteps_(addSteps) {} //addSteps: add points to get a staircase effect or connect points via a direct line
+
+ virtual Opt<CurvePoint> getLessEq (double x) const = 0;
+ virtual Opt<CurvePoint> getGreaterEq(double x) const = 0;
+
+private:
+ virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const final;
+ bool addSteps_;
};
-/*
-//reference data implementation
-class VectorData : public GraphData
+struct ArrayCurveData : public SparseCurveData
{
-public:
- operator std::vector<double>& () { return data; }
+ virtual double getValue(size_t pos) const = 0;
+ virtual size_t getSize() const = 0;
private:
- virtual double getValue(double x) const
+ virtual std::pair<double, double> getRangeX() const final { const size_t sz = getSize(); return std::make_pair(0.0, sz == 0 ? 0.0 : sz - 1.0); }
+
+ virtual Opt<CurvePoint> getLessEq(double x) const final
{
- const size_t pos = static_cast<size_t>(x);
- return pos < data.size() ? data[pos] : 0;
+ const size_t sz = getSize();
+ const size_t pos = std::min<ptrdiff_t>(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty!
+ if (pos < sz)
+ return CurvePoint(pos, getValue(pos));
+ return NoValue();
}
- virtual double getXBegin() const { return 0; }
- virtual double getXEnd() const { return data.size(); } //example: two-element range is accessible within [0, 2)
- std::vector<double> data;
+ virtual Opt<CurvePoint> getGreaterEq(double x) const final
+ {
+ const size_t pos = std::max<ptrdiff_t>(std::ceil(x), 0); //[!] use std::max with signed type!
+ if (pos < getSize())
+ return CurvePoint(pos, getValue(pos));
+ return NoValue();
+ }
+};
+
+struct VectorCurveData : public ArrayCurveData
+{
+ std::vector<double>& refData() { return data; }
+private:
+ virtual double getValue(size_t pos) const final { return pos < data.size() ? data[pos] : 0; }
+ virtual size_t getSize() const final { return data.size(); }
+ std::vector<double> data;
};
-*/
//------------------------------------------------------------------------------------------------------------
+
struct LabelFormatter
{
virtual ~LabelFormatter() {}
@@ -106,16 +136,8 @@ extern const wxEventType wxEVT_GRAPH_SELECTION;
struct SelectionBlock
{
- struct Point
- {
- Point() : x(0), y(0) {}
- Point(double xVal, double yVal) : x(xVal), y(yVal) {}
- double x;
- double y;
- };
-
- Point from;
- Point to;
+ CurvePoint from;
+ CurvePoint to;
};
class GraphSelectEvent : public wxCommandEvent
@@ -135,8 +157,8 @@ typedef void (wxEvtHandler::*GraphSelectEventFunction)(GraphSelectEvent&);
#define GraphSelectEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GraphSelectEventFunction, &func)
-
//------------------------------------------------------------------------------------------------------------
+
class Graph2D : public wxPanel
{
public:
@@ -168,8 +190,8 @@ public:
int lineWidth;
};
- void setData(const std::shared_ptr<GraphData>& data, const CurveAttributes& ca = CurveAttributes());
- void addData(const std::shared_ptr<GraphData>& data, const CurveAttributes& ca = CurveAttributes());
+ void setCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca = CurveAttributes());
+ void addCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca = CurveAttributes());
enum PosLabelY
{
@@ -185,6 +207,14 @@ public:
X_LABEL_NONE
};
+ enum PosCorner
+ {
+ CORNER_TOP_LEFT,
+ CORNER_TOP_RIGHT,
+ CORNER_BOTTOM_LEFT,
+ CORNER_BOTTOM_RIGHT,
+ };
+
enum SelMode
{
SELECT_NONE,
@@ -238,6 +268,8 @@ public:
return *this;
}
+ MainAttributes& setCornerText(const wxString& txt, PosCorner pos) { cornerTexts[pos] = txt; return *this; }
+
MainAttributes& setSelectionMode(SelMode mode) { mouseSelMode = mode; return *this; }
private:
@@ -261,6 +293,8 @@ public:
int yLabelWidth;
std::shared_ptr<LabelFormatter> labelFmtY;
+ std::map<PosCorner, wxString> cornerTexts;
+
SelMode mouseSelMode;
};
void setAttributes(const MainAttributes& newAttr) { attr = newAttr; Refresh(); }
@@ -311,8 +345,9 @@ private:
std::unique_ptr<wxBitmap> doubleBuffer;
- typedef std::vector<std::pair<std::shared_ptr<GraphData>, CurveAttributes>> GraphList;
- GraphList curves_;
+ typedef std::vector<std::pair<std::shared_ptr<CurveData>, CurveAttributes>> CurveList;
+ CurveList curves_;
+ wxFont labelFont; //perf!!! generating the font is *very* expensive! don't do this repeatedly in Graph2D::render()!
};
}
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 46c0c406..c8d418ee 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -17,8 +17,9 @@
#include <zen/scope_guard.h>
#include <zen/utf.h>
#include <zen/format_unit.h>
-#include "image_tools.h"
-#include "rtl.h"
+//#include "image_tools.h"
+//#include "rtl.h"
+#include "dc.h"
#ifdef ZEN_LINUX
#include <gtk/gtk.h>
@@ -63,50 +64,6 @@ const wxColor COLOR_LABEL_GRADIENT_TO_FOCUS = COLOR_LABEL_GRADIENT_TO;
wxColor getColorMainWinBackground() { return wxListBox::GetClassDefaultAttributes().colBg; } //cannot be initialized statically on wxGTK!
const wxColor colorGridLine = wxColour(192, 192, 192); //light grey
-
-//------------------------------------------------------------
-
-//another fix for yet another poor wxWidgets implementation (wxDCClipper does *not* stack)
-hash_map<wxDC*, wxRect> clippingAreas; //associate "active" clipping area with each DC
-
-class DcClipper
-{
-public:
- DcClipper(wxDC& dc, const wxRect& r) : dc_(dc)
- {
- auto it = clippingAreas.find(&dc);
- if (it != clippingAreas.end())
- {
- oldRect.reset(new wxRect(it->second));
-
- wxRect tmp = r;
- tmp.Intersect(*oldRect); //better safe than sorry
- dc_.SetClippingRegion(tmp); //
- it->second = tmp;
- }
- else
- {
- dc_.SetClippingRegion(r);
- clippingAreas.insert(std::make_pair(&dc_, r));
- }
- }
-
- ~DcClipper()
- {
- dc_.DestroyClippingRegion();
- if (oldRect.get() != nullptr)
- {
- dc_.SetClippingRegion(*oldRect);
- clippingAreas[&dc_] = *oldRect;
- }
- else
- clippingAreas.erase(&dc_);
- }
-
-private:
- std::unique_ptr<wxRect> oldRect;
- wxDC& dc_;
-};
}
//----------------------------------------------------------------------------------------------------------------
@@ -208,7 +165,7 @@ wxString getTruncatedText(const wxString& text, Function textFits)
void drawTextLabelFitting(wxDC& dc, const wxString& text, const wxRect& rect, int alignment)
{
- DcClipper clip(dc, rect); //wxDC::DrawLabel doesn't care about width, WTF?
+ RecursiveDcClipper clip(dc, rect); //wxDC::DrawLabel doesn't care about width, WTF?
/*
performance notes:
@@ -555,7 +512,7 @@ private:
wxRect textRect = rect;
textRect.Deflate(1);
{
- DcClipper clip(dc, textRect); //wxDC::DrawLabel doesn't care about with, WTF?
+ RecursiveDcClipper clip(dc, textRect); //wxDC::DrawLabel doesn't care about with, WTF?
dc.DrawLabel(formatRow(row), textRect, wxALIGN_CENTRE);
}
@@ -691,7 +648,7 @@ private:
highlight ? col == highlight->first && compPos == highlight->second :
false;
- DcClipper clip(dc, rect);
+ RecursiveDcClipper clip(dc, rect);
dataView->renderColumnLabel(refParent(), dc, rect, colType, isHighlighted);
//draw move target location
@@ -978,7 +935,7 @@ private:
{
//draw background lines
{
- DcClipper dummy2(dc, rect); //solve issues with drawBackground() painting in area outside of rect (which is not also refreshed by renderCell()) -> keep small scope!
+ RecursiveDcClipper dummy2(dc, rect); //solve issues with drawBackground() painting in area outside of rect (which is not also refreshed by renderCell()) -> keep small scope!
for (int row = rowFirst; row < rowLast; ++row)
drawBackground(*prov, dc, wxRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(compWidth, rowHeight)), row, compPos);
}
@@ -995,7 +952,7 @@ private:
for (int row = rowFirst; row < rowLast; ++row)
{
const wxRect& cellRect = wxRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, width, rowHeight);
- DcClipper clip(dc, cellRect);
+ RecursiveDcClipper clip(dc, cellRect);
prov->renderCell(refParent(), dc, cellRect, row, iterCol->type_);
}
cellAreaTL.x += width;
diff --git a/wx+/no_flicker.h b/wx+/no_flicker.h
index ea9efb4d..fd64628f 100644
--- a/wx+/no_flicker.h
+++ b/wx+/no_flicker.h
@@ -15,20 +15,27 @@ namespace zen
inline
void setText(wxTextCtrl& control, const wxString& newText, bool* additionalLayoutChange = nullptr)
{
+ const wxString& label = control.GetValue(); //perf: don't call twice!
if (additionalLayoutChange && !*additionalLayoutChange) //never revert from true to false!
- *additionalLayoutChange = control.GetValue().length() != newText.length(); //avoid screen flicker: update layout only when necessary
+ *additionalLayoutChange = label.length() != newText.length(); //avoid screen flicker: update layout only when necessary
- if (control.GetValue() != newText)
+ if (label != newText)
control.ChangeValue(newText);
}
inline
-void setText(wxStaticText& control, const wxString& newText, bool* additionalLayoutChange = nullptr)
+void setText(wxStaticText& control, wxString newText, bool* additionalLayoutChange = nullptr)
{
+#ifdef ZEN_WIN
+ //wxStaticText handles ampersands incorrectly: https://sourceforge.net/p/freefilesync/bugs/279/
+ replace(newText, L'&', L"&&");
+#endif
+
+ const wxString& label = control.GetLabel(); //perf: don't call twice!
if (additionalLayoutChange && !*additionalLayoutChange)
- *additionalLayoutChange = control.GetLabel().length() != newText.length(); //avoid screen flicker: update layout only when necessary
+ *additionalLayoutChange = label.length() != newText.length(); //avoid screen flicker: update layout only when necessary
- if (control.GetLabel() != newText)
+ if (label != newText)
control.SetLabel(newText);
}
}
diff --git a/wx+/rtl.h b/wx+/rtl.h
index 84009a86..4caf19ea 100644
--- a/wx+/rtl.h
+++ b/wx+/rtl.h
@@ -13,7 +13,6 @@
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/app.h>
-#include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER
namespace zen
{
@@ -38,17 +37,6 @@ void drawIconRtlNoMirror(wxDC& dc, //wxDC::DrawIcon DOES mirror by default
wxBitmap mirrorIfRtl(const wxBitmap& bmp);
-
-/*
-class BufferedPaintDC
-{
-public:
- BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer);
-};
-*/
-//a fix for a poor wxWidgets implementation (wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active)
-
-
//manual text flow correction: http://www.w3.org/International/articles/inline-bidi-markup/
@@ -127,48 +115,6 @@ wxBitmap mirrorIfRtl(const wxBitmap& bmp)
else
return bmp;
}
-
-
-#ifndef wxALWAYS_NATIVE_DOUBLE_BUFFER
-#error we need this one!
-#endif
-
-#if wxALWAYS_NATIVE_DOUBLE_BUFFER
-struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : wxPaintDC(&wnd) {} };
-
-#else
-class BufferedPaintDC : public wxMemoryDC
-{
-public:
- BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : buffer_(buffer), paintDc(&wnd)
- {
- const wxSize clientSize = wnd.GetClientSize();
- if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight()))
- buffer.reset(new wxBitmap(clientSize.GetWidth(), clientSize.GetHeight()));
-
- SelectObject(*buffer);
-
- if (paintDc.IsOk() && paintDc.GetLayoutDirection() == wxLayout_RightToLeft)
- SetLayoutDirection(wxLayout_RightToLeft);
- }
-
- ~BufferedPaintDC()
- {
- if (GetLayoutDirection() == wxLayout_RightToLeft)
- {
- paintDc.SetLayoutDirection(wxLayout_LeftToRight); //workaround bug in wxDC::Blit()
- SetLayoutDirection(wxLayout_LeftToRight); //
- }
-
- const wxPoint origin = GetDeviceOrigin();
- paintDc.Blit(0, 0, buffer_->GetWidth(), buffer_->GetHeight(), this, -origin.x, -origin.y);
- }
-
-private:
- std::unique_ptr<wxBitmap>& buffer_;
- wxPaintDC paintDc;
-};
-#endif
}
#endif //RTL_H_0183487180058718273432148
diff --git a/wx+/shell_execute.h b/wx+/shell_execute.h
index 8965186e..1d67aa21 100644
--- a/wx+/shell_execute.h
+++ b/wx+/shell_execute.h
@@ -42,12 +42,15 @@ void shellExecute(const Zstring& command, ExecutionType type = EXEC_TYPE_ASYNC)
{
#ifdef ZEN_WIN
//parse commandline
+ Zstring commandTmp = command;
+ trim(commandTmp, true, false); //CommandLineToArgvW() does not like leading spaces
+
std::vector<std::wstring> argv;
int argc = 0;
- if (LPWSTR* tmp = ::CommandLineToArgvW(command.c_str(), &argc))
+ if (LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc))
{
+ ZEN_ON_SCOPE_EXIT(::LocalFree(tmp));
std::copy(tmp, tmp + argc, std::back_inserter(argv));
- ::LocalFree(tmp);
}
std::wstring filename;
@@ -74,7 +77,7 @@ void shellExecute(const Zstring& command, ExecutionType type = EXEC_TYPE_ASYNC)
if (!::ShellExecuteEx(&execInfo)) //__inout LPSHELLEXECUTEINFO lpExecInfo
{
wxString cmdFmt = L"File: " + filename + L"\nArg: " + arguments;
- wxMessageBox(_("Invalid command line:") + L"\n" + cmdFmt + L"\n\n" + formatSystemError(L"ShellExecuteEx", getLastError()));
+ wxMessageBox(_("Invalid command line:") + L"\n" + cmdFmt + L"\n\n" + formatSystemError(L"ShellExecuteEx", getLastError()), /*L"FreeFileSync - " + */_("Error"), wxOK | wxICON_ERROR);
return;
}
@@ -99,7 +102,7 @@ void shellExecute(const Zstring& command, ExecutionType type = EXEC_TYPE_ASYNC)
//Posix::system - execute a shell command
int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect...
if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)"
- wxMessageBox(_("Invalid command line:") + L"\n" + utfCvrtTo<wxString>(command));
+ wxMessageBox(_("Invalid command line:") + L"\n" + utfCvrtTo<wxString>(command), /*L"FreeFileSync - " +*/ _("Error"), wxOK | wxICON_ERROR);
}
else
async([=] { int rv = ::system(command.c_str()); (void)rv; });
diff --git a/wx+/string_conv.h b/wx+/string_conv.h
index b919a3ee..e9150223 100644
--- a/wx+/string_conv.h
+++ b/wx+/string_conv.h
@@ -23,7 +23,6 @@ inline std::vector<Zstring> toZ(const std::vector<wxString>& strList)
std::transform(strList.begin(), strList.end(), std::back_inserter(tmp), [](const wxString& str) { return toZ(str); });
return tmp;
}
-
}
#endif // STRINGCONV_H_INCLUDED
diff --git a/zen/FindFilePlus/FindFilePlus.vcxproj b/zen/FindFilePlus/FindFilePlus.vcxproj
index d7dfe219..73b7f70e 100644
--- a/zen/FindFilePlus/FindFilePlus.vcxproj
+++ b/zen/FindFilePlus/FindFilePlus.vcxproj
@@ -172,7 +172,7 @@
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
- <GenerateDebugInformation>false</GenerateDebugInformation>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
@@ -209,7 +209,7 @@
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
- <GenerateDebugInformation>false</GenerateDebugInformation>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp
index fdabc46f..ea58005e 100644
--- a/zen/FindFilePlus/find_file_plus.cpp
+++ b/zen/FindFilePlus/find_file_plus.cpp
@@ -113,10 +113,10 @@ public:
FileSearcher(const wchar_t* dirname); //throw NtFileError
~FileSearcher();
- void readDir(FileInformation& output); //throw NtFileError
+ bool readDir(FileInformation& output); //throw NtFileError; returns false if "no more files"
private:
- template <class QueryPolicy> void readDirImpl(FileInformation& output); //throw NtFileError
+ template <class QueryPolicy> bool readDirImpl(FileInformation& output); //throw NtFileError
UNICODE_STRING dirnameNt; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()!
HANDLE hDir;
@@ -271,11 +271,11 @@ struct DirQueryFileId
inline
-void FileSearcher::readDir(FileInformation& output) { readDirImpl<DirQueryFileId>(output); } //throw NtFileError
+bool FileSearcher::readDir(FileInformation& output) { return readDirImpl<DirQueryFileId>(output); } //throw NtFileError
template <class QueryPolicy>
-void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError
+bool FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError; returns false if "no more files"
{
//although FILE_ID_FULL_DIR_INFORMATION should suffice for our purposes, there are problems on Windows XP for certain directories, e.g. "\\Vboxsvr\build"
//making NtQueryDirectoryFile() return with STATUS_INVALID_PARAMETER while other directories, e.g. "C:\" work fine for some reason
@@ -337,25 +337,30 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError
STATUS_NO_SUCH_FILE is abused by some citrix shares instead of "STATUS_INVALID_PARAMETER" so we treat it as such!
=> since the directory is "truly empty" a fallback won't hurt
*/
- if (rv == STATUS_INVALID_LEVEL ||
- rv == STATUS_NOT_SUPPORTED ||
+ if (rv == STATUS_NO_MORE_FILES) //perf: don't throw an exception in this common case! => 8% perf boost for FFS comparison phase!
+ return false;
+
+ if (rv == STATUS_NOT_SUPPORTED ||
+ rv == STATUS_INVALID_LEVEL ||
+ rv == STATUS_NO_SUCH_FILE || //[!]
rv == STATUS_UNEXPECTED_NETWORK_ERROR ||
rv == STATUS_INVALID_PARAMETER ||
rv == STATUS_INVALID_NETWORK_RESPONSE ||
rv == STATUS_INVALID_INFO_CLASS ||
+ //rv == STATUS_NOT_IMPLEMENTED || -> first confirm that these codes
+ //rv == STATUS_INVALID_DEVICE_REQUEST || -> are in fact used!
rv == STATUS_UNSUCCESSFUL ||
- rv == STATUS_ACCESS_VIOLATION ||
- rv == STATUS_NO_SUCH_FILE) //[!]
+ rv == STATUS_ACCESS_VIOLATION)
rv = STATUS_NOT_SUPPORTED;
throw NtFileError(rv); //throws STATUS_NO_MORE_FILES when finished
}
- //for (NTSTATUS i = 0xC0000000L; i != -1; ++i)
- //{
- // if (rtlNtStatusToDosError(i) == 59) //ERROR_UNEXP_NET_ERR
- // __asm int 3;
- //}
+ // for (NTSTATUS i = 0xC0000000L; i != -1; ++i)
+ // {
+ // if (rtlNtStatusToDosError(i) == 59) //ERROR_UNEXP_NET_ERR
+ // __debugbreak(); //__asm int 3;
+ // }
if (status.Information == 0) //except for the first call to call ::NtQueryDirectoryFile():
throw NtFileError(STATUS_BUFFER_OVERFLOW); //if buffer size is too small, return value is STATUS_SUCCESS and Information == 0 -> we don't expect this!
@@ -399,6 +404,7 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw NtFileError
static_assert(sizeof(output.lastWriteTime) == sizeof(dirInfo.LastWriteTime), "dang!");
static_assert(sizeof(output.fileSize) == sizeof(dirInfo.EndOfFile), "dang!");
static_assert(sizeof(output.fileAttributes) == sizeof(dirInfo.FileAttributes), "dang!");
+ return true;
}
@@ -425,7 +431,11 @@ bool findplus::readDir(FindHandle hnd, FileInformation& output)
{
try
{
- hnd->readDir(output); //throw NtFileError
+ if (!hnd->readDir(output)) //throw NtFileError
+ {
+ setWin32Error(rtlNtStatusToDosError(STATUS_NO_MORE_FILES));
+ return false;
+ }
return true;
}
catch (const NtFileError& e)
diff --git a/zen/basic_math.h b/zen/basic_math.h
index f01421c7..05d892ee 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -36,11 +36,6 @@ void confine(T& val, const T& minVal, const T& maxVal); //make sure minVal <= va
template <class T>
T confineCpy(const T& val, const T& minVal, const T& maxVal);
-template <class InputIterator>
-std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last);
-template <class InputIterator, class Compare>
-std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare comp);
-
template <class T, class InputIterator> //precondition: range must be sorted!
auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type;
@@ -160,6 +155,8 @@ void confine(T& val, const T& minVal, const T& maxVal) //name trim, clamp?
}
+/*
+part of C++11 now!
template <class InputIterator, class Compare> inline
std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare compLess)
{
@@ -200,7 +197,7 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input
{
return minMaxElement(first, last, std::less<typename std::iterator_traits<InputIterator>::value_type>());
}
-
+*/
template <class T, class InputIterator> inline
auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index a800fb80..e53b63e2 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -96,7 +96,7 @@ public:
//context of main thread
- void getChanges(std::vector<DirWatcher::Entry>& output) //throw FileError, ErrorNotExisting
+ void getChanges(std::vector<DirWatcher::Entry>& output) //throw FileError
{
boost::lock_guard<boost::mutex> dummy(lockAccess);
@@ -105,9 +105,6 @@ public:
{
const std::wstring msg = copyStringTo<std::wstring>(errorInfo->msg);
const std::wstring descr = copyStringTo<std::wstring>(errorInfo->descr);
-
- if (errorCodeForNotExisting(errorInfo->errorCode))
- throw ErrorNotExisting(msg, descr);
throw FileError(msg, descr);
}
@@ -163,9 +160,6 @@ public:
const DWORD lastError = ::GetLastError(); //copy before making other system calls!
const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory));
const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError);
-
- if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(errorMsg, errorDescr);
throw FileError(errorMsg, errorDescr);
}
@@ -199,7 +193,6 @@ public:
const DWORD lastError = ::GetLastError(); //copy before making other system calls!
return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirnamePf)), formatSystemError(L"CreateEvent", lastError), lastError);
}
-
ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent));
DWORD bytesReturned = 0; //should not be needed for async calls, still pass it to help broken drivers
@@ -465,9 +458,6 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError
const ErrorCode lastError = getLastError(); //copy before making other system calls!
const std::wstring errorMsg = replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir));
const std::wstring errorDescr = formatSystemError(L"inotify_add_watch", lastError);
-
- if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(errorMsg, errorDescr);
throw FileError(errorMsg, errorDescr);
}
diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h
index b0df48bd..eaee5aab 100644
--- a/zen/dir_watcher.h
+++ b/zen/dir_watcher.h
@@ -27,12 +27,12 @@ namespace zen
Renaming of top watched directory handled incorrectly: Not notified(!) + additional changes in subfolders
now do report FILE_ACTION_MODIFIED for directory (check that should prevent this fails!)
- Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are possible
+ Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are detected
*/
class DirWatcher
{
public:
- DirWatcher(const Zstring& directory); //throw FileError, ErrorNotExisting
+ DirWatcher(const Zstring& directory); //throw FileError
~DirWatcher();
enum ActionType
diff --git a/zen/file_error.h b/zen/file_error.h
index 71619834..db8b371d 100644
--- a/zen/file_error.h
+++ b/zen/file_error.h
@@ -30,7 +30,6 @@ private:
#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} };
-DEFINE_NEW_FILE_ERROR(ErrorNotExisting);
DEFINE_NEW_FILE_ERROR(ErrorTargetExisting);
DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing);
DEFINE_NEW_FILE_ERROR(ErrorFileLocked);
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index 398e88e8..103a39f7 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -45,67 +45,111 @@
using namespace zen;
+warn_static("remove after test")
-bool zen::fileExists(const Zstring& filename)
+namespace
+{
+void writeSysErrorIfNeeded(std::wstring* sysErrorMsg, const wchar_t* functionName, ErrorCode lastError)
+{
+ if (sysErrorMsg)
+ {
+ //skip uninteresting error codes:
+#ifdef ZEN_WIN
+ if (lastError == ERROR_FILE_NOT_FOUND ||
+ lastError == ERROR_PATH_NOT_FOUND) return;
+ //lastError == ERROR_BAD_NETPATH || //e.g. for a path like: \\192.168.1.1\test
+ //lastError == ERROR_NETNAME_DELETED;
+
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ if (lastError == ENOENT) return;
+#endif
+ *sysErrorMsg = formatSystemError(functionName, lastError);
+ }
+}
+}
+
+
+bool zen::fileExists(const Zstring& filename, std::wstring* sysErrorMsg)
{
//symbolic links (broken or not) are also treated as existing files!
#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"GetFileAttributes";
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
- return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
+ if (attr != INVALID_FILE_ATTRIBUTES)
+ return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"lstat";
struct ::stat fileInfo = {};
- return ::lstat(filename.c_str(), &fileInfo) == 0 &&
- (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)); //in Linux a symbolic link is neither file nor directory
+ if (::lstat(filename.c_str(), &fileInfo) == 0)
+ return S_ISREG(fileInfo.st_mode) || S_ISLNK(fileInfo.st_mode); //in Linux a symbolic link is neither file nor directory
#endif
+ writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
+ return false;
}
-bool zen::dirExists(const Zstring& dirname)
+bool zen::dirExists(const Zstring& dirname, std::wstring* sysErrorMsg)
{
//symbolic links (broken or not) are also treated as existing directories!
#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"GetFileAttributes";
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
- return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
+ if (attr != INVALID_FILE_ATTRIBUTES)
+ return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"lstat";
struct ::stat dirInfo = {};
- return ::lstat(dirname.c_str(), &dirInfo) == 0 &&
- (S_ISLNK(dirInfo.st_mode) || S_ISDIR(dirInfo.st_mode)); //in Linux a symbolic link is neither file nor directory
+ if (::lstat(dirname.c_str(), &dirInfo) == 0)
+ return S_ISDIR(dirInfo.st_mode) || S_ISLNK(dirInfo.st_mode); //in Linux a symbolic link is neither file nor directory
#endif
+ writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
+ return false;
}
-bool zen::symlinkExists(const Zstring& linkname)
+bool zen::symlinkExists(const Zstring& linkname, std::wstring* sysErrorMsg)
{
#ifdef ZEN_WIN
- WIN32_FIND_DATA fileInfo = {};
+ const wchar_t functionName[] = L"FindFirstFile";
+ WIN32_FIND_DATA linkInfo = {};
+ const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &linkInfo);
+ if (searchHandle != INVALID_HANDLE_VALUE)
{
- const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &fileInfo);
- if (searchHandle == INVALID_HANDLE_VALUE)
- return false;
::FindClose(searchHandle);
+ return isSymlink(linkInfo);
}
- return isSymlink(fileInfo);
#elif defined ZEN_LINUX || defined ZEN_MAC
- struct ::stat fileInfo = {};
- return ::lstat(linkname.c_str(), &fileInfo) == 0 &&
- S_ISLNK(fileInfo.st_mode); //symbolic link
+ const wchar_t functionName[] = L"lstat";
+ struct ::stat linkInfo = {};
+ if (::lstat(linkname.c_str(), &linkInfo) == 0)
+ return S_ISLNK(linkInfo.st_mode);
#endif
+ writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
+ return false;
}
-bool zen::somethingExists(const Zstring& objname)
+bool zen::somethingExists(const Zstring& objname, std::wstring* sysErrorMsg)
{
#ifdef ZEN_WIN
+ const wchar_t functionName[] = L"GetFileAttributes";
const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
- return attr != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys"
+ if (attr != INVALID_FILE_ATTRIBUTES)
+ return true;
+ if (::GetLastError() == ERROR_SHARING_VIOLATION) //"C:\pagefile.sys"
+ return true;
#elif defined ZEN_LINUX || defined ZEN_MAC
+ const wchar_t functionName[] = L"lstat";
struct ::stat fileInfo = {};
- return ::lstat(objname.c_str(), &fileInfo) == 0;
+ if (::lstat(objname.c_str(), &fileInfo) == 0)
+ return true;
#endif
+ writeSysErrorIfNeeded(sysErrorMsg, functionName, getLastError());
+ return false;
}
@@ -275,9 +319,6 @@ bool zen::removeFile(const Zstring& filename) //throw FileError
#endif
{
ErrorCode lastError = getLastError();
- if (errorCodeForNotExisting(lastError)) //no error situation if file is not existing! manual deletion relies on it!
- return false;
-
#ifdef ZEN_WIN
if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only
{
@@ -288,7 +329,6 @@ bool zen::removeFile(const Zstring& filename) //throw FileError
lastError = ::GetLastError();
}
#endif
- //after "lastError" evaluation it *may* be redundant to check existence again, but better be safe than sorry:
if (!somethingExists(filename)) //warning: changes global error code!!
return false; //neither file nor any other object (e.g. broken symlink) with that name existing
@@ -2119,7 +2159,7 @@ void copyFileLinuxMac(const Zstring& sourceFile,
FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
{
//open sourceFile for reading
- FileInputUnbuffered fileIn(sourceFile); //throw FileError, ErrorNotExisting
+ FileInputUnbuffered fileIn(sourceFile); //throw FileError
struct ::stat sourceInfo = {};
if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source
diff --git a/zen/file_handling.h b/zen/file_handling.h
index f677358b..3cb8a68c 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -18,10 +18,11 @@ struct CallbackRemoveDir;
struct CallbackCopyFile;
-bool fileExists (const Zstring& filename); //noexcept; check whether file *or* (file) symlink exists
-bool dirExists (const Zstring& dirname); //noexcept; check whether directory *or* (dir) symlink exists
-bool symlinkExists (const Zstring& linkname); //noexcept; check whether a symbolic link exists
-bool somethingExists(const Zstring& objname); //noexcept; check whether any object with this name exists
+bool fileExists (const Zstring& filename, std::wstring* sysErrorMsg = nullptr); //noexcept; check whether file *or* (file) symlink exists
+bool dirExists (const Zstring& dirname , std::wstring* sysErrorMsg = nullptr); //noexcept; check whether directory *or* (dir) symlink exists
+bool symlinkExists (const Zstring& linkname, std::wstring* sysErrorMsg = nullptr); //noexcept; check whether a symbolic link exists
+bool somethingExists(const Zstring& objname , std::wstring* sysErrorMsg = nullptr); //noexcept; check whether any object with this name exists
+//sysErrorMsg: optional in + optional out! written only for non-expected errors other than ERROR_FILE_NOT_FOUND/ENOENT, ect...
enum SymLinkType
{
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index e0f8c12e..45cbd028 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -77,8 +77,7 @@ void checkForUnsupportedType(const Zstring& filename) //throw FileError
FileInput::FileInput(FileHandle handle, const Zstring& filename) : FileInputBase(filename), fileHandle(handle) {}
-FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting
- FileInputBase(filename)
+FileInput::FileInput(const Zstring& filename) : FileInputBase(filename) //throw FileError
{
#ifdef ZEN_WIN
const wchar_t functionName[] = L"CreateFile";
@@ -104,8 +103,8 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExist
while FILE_FLAG_RANDOM_ACCESS offers best performance for
- same physical disk (HDD <-> HDD)
- Problem: bad XP implementation of prefetch makes flag FILE_FLAG_SEQUENTIAL_SCAN effectively load two files at once from one drive
- swapping every 64 kB (or similar). File access times explode!
+ Problem: bad XP implementation of prefetch makes flag FILE_FLAG_SEQUENTIAL_SCAN effectively load two files at the same time
+ from one drive, swapping every 64 kB (or similar). File access times explode!
=> For XP it is critical to use FILE_FLAG_RANDOM_ACCESS (to disable prefetch) if reading two files on same disk and
FILE_FLAG_SEQUENTIAL_SCAN when reading from different disk (e.g. massive performance improvement compared to random access for DVD <-> HDD!)
=> there is no compromise that satisfies all cases! (on XP)
@@ -122,9 +121,7 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExist
#endif
{
const ErrorCode lastError = getLastError(); //copy before making other system calls!
- const std::wstring errorMsg = errorCodeForNotExisting(lastError) ?
- replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) :
- replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename));
+ const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename));
std::wstring errorDescr = formatSystemError(functionName, lastError);
#ifdef ZEN_WIN
@@ -136,10 +133,6 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExist
errorDescr = _("The file is locked by another process:") + L"\n" + procList;
}
#endif
-
- if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(errorMsg, errorDescr);
-
throw FileError(errorMsg, errorDescr);
}
}
@@ -319,7 +312,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
#if defined ZEN_LINUX || defined ZEN_MAC
//Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-5.0.tar.gz
-FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError, ErrorNotExisting
+FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError
{
checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
@@ -327,15 +320,8 @@ FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBas
if (fdFile == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle
{
const ErrorCode lastError = getLastError(); //copy before making other system calls!
-
- const std::wstring errorMsg = errorCodeForNotExisting(lastError) ?
- replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) :
- replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename));
+ const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtFileName(filename));
const std::wstring errorDescr = formatSystemError(L"open", lastError);
-
- if (errorCodeForNotExisting(lastError))
- throw ErrorNotExisting(errorMsg, errorDescr);
-
throw FileError(errorMsg, errorDescr);
}
}
diff --git a/zen/file_io.h b/zen/file_io.h
index 93267a39..0c7506a1 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -39,7 +39,7 @@ typedef FILE* FileHandle;
class FileInput : public FileInputBase
{
public:
- FileInput(const Zstring& filename); //throw FileError, ErrorNotExisting
+ FileInput(const Zstring& filename); //throw FileError
FileInput(FileHandle handle, const Zstring& filename); //takes ownership!
~FileInput();
@@ -68,7 +68,7 @@ private:
class FileInputUnbuffered : public FileInputBase
{
public:
- FileInputUnbuffered(const Zstring& filename); //throw FileError, ErrorNotExisting
+ FileInputUnbuffered(const Zstring& filename); //throw FileError
~FileInputUnbuffered();
//considering safe-read.c it seems buffer size should be a multiple of 8192
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 9e5975d9..4f16e3e1 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -24,47 +24,45 @@
using namespace zen;
+std::wstring zen::formatThreeDigitPrecision(double value)
+{
+ //print at least three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111
+ if (numeric::abs(value) < 9.995) //9.999 must not be formatted as "10.00"
+ return printNumber<std::wstring>(L"%.2f", value);
+ if (numeric::abs(value) < 99.95) //99.99 must not be formatted as "100.0"
+ return printNumber<std::wstring>(L"%.1f", value);
+ return numberTo<std::wstring>(numeric::round(value));
+}
+
+
std::wstring zen::filesizeToShortString(Int64 size)
{
//if (size < 0) return _("Error"); -> really?
if (numeric::abs(size) <= 999)
- return replaceCpy(_P("1 Byte", "%x Bytes", to<int>(size)), L"%x", numberTo<std::wstring>(size));
-
- auto formatUnitSize = [](double sizeInUnit, const std::wstring& unitTxt) -> std::wstring
- {
- //print just three significant digits: 0,01 | 0,11 | 1,11 | 11,1 | 111
- const size_t fullunits = static_cast<size_t>(numeric::abs(sizeInUnit));
- const int precisionDigits = fullunits < 10 ? 2 : fullunits < 100 ? 1 : 0; //sprintf requires "int"
-
- wchar_t buffer[50];
-#ifdef __MINGW32__
- int charsWritten = ::_snwprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); //MinGW does not comply to the C standard here
-#else
- int charsWritten = std::swprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit);
-#endif
- return charsWritten > 0 ? replaceCpy(unitTxt, L"%x", std::wstring(buffer, charsWritten)) : _("Error");
- };
+ return replaceCpy(_P("1 byte", "%x bytes", to<int>(size)), L"%x", numberTo<std::wstring>(size));
double sizeInUnit = to<double>(size);
+ auto formatUnit = [&](const std::wstring& unitTxt) { return replaceCpy(unitTxt, L"%x", formatThreeDigitPrecision(sizeInUnit)); };
+
sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) <= 999)
- return formatUnitSize(sizeInUnit, _("%x KB"));
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x KB"));
sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) <= 999)
- return formatUnitSize(sizeInUnit, _("%x MB"));
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x MB"));
sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) <= 999)
- return formatUnitSize(sizeInUnit, _("%x GB"));
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x GB"));
sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) <= 999)
- return formatUnitSize(sizeInUnit, _("%x TB"));
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x TB"));
sizeInUnit /= 1024;
- return formatUnitSize(sizeInUnit, _("%x PB"));
+ return formatUnit(_("%x PB"));
}
@@ -148,7 +146,7 @@ std::wstring zen::remainingTimeToString(double timeInSec)
std::wstring zen::fractionToString(double fraction)
{
- return printNumber<std::wstring>(L"%3.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
+ return printNumber<std::wstring>(L"%.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
}
diff --git a/zen/format_unit.h b/zen/format_unit.h
index 86477999..e3e2d107 100644
--- a/zen/format_unit.h
+++ b/zen/format_unit.h
@@ -16,19 +16,12 @@ namespace zen
std::wstring filesizeToShortString(Int64 filesize);
std::wstring remainingTimeToString(double timeInSec);
std::wstring fractionToString(double fraction); //within [0, 1]
-
-template <class NumberType>
-std::wstring toGuiString(NumberType number); //format integer number including thousands separator
-
std::wstring utcToLocalTimeString(Int64 utcTime); //like Windows Explorer would...
+std::wstring formatThreeDigitPrecision(double value); //= *at least* three digits
-
-
-
-
-
-
+template <class NumberType>
+std::wstring toGuiString(NumberType number); //format integer number including thousands separator
diff --git a/zen/i18n.h b/zen/i18n.h
index 66e5fa53..b7d10d3c 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -68,6 +68,7 @@ std::wstring translate(const std::wstring& text)
inline
std::wstring translate(const std::wstring& singular, const std::wstring& plural, int n)
{
+ if (n < 0) n = -n;
return getTranslator() ? getTranslator()->translate(singular, plural, n) : n == 1 ? singular : plural;
}
diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h
index db1fbdca..cdff09fa 100644
--- a/zen/long_path_prefix.h
+++ b/zen/long_path_prefix.h
@@ -57,6 +57,11 @@ Zstring applyLongPathPrefixImpl(const Zstring& path)
assert(!path.empty()); //nicely check almost all WinAPI accesses!
assert(!zen::isWhiteSpace(path[0]));
+ //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#naming_conventions))
+ /*
+ - special names like ".NUL" create all kinds of trouble (e.g. CreateDirectory() reports success, but does nothing)
+ unless prefix is supplied => accept as limitation
+ */
if (path.length() >= maxPath || //maximum allowed path length without prefix is (MAX_PATH - 1)
endsWith(path, L' ') || //by default all Win32 APIs trim trailing spaces and period, unless long path prefix is supplied!
endsWith(path, L'.')) //note: adding long path prefix might screw up relative paths "." and ".."!
diff --git a/zen/optional.h b/zen/optional.h
index a6a53103..6e54408a 100644
--- a/zen/optional.h
+++ b/zen/optional.h
@@ -45,6 +45,7 @@ public:
if (tmp.valid)
value = tmp.value;
valid = tmp.valid;
+ return *this;
}
////rvalue optimization: only basic exception safety:
diff --git a/zen/serialize.h b/zen/serialize.h
index 415bd430..64c07213 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -56,7 +56,7 @@ private:
//----------------------------------------------------------------------
//functions based on binary container abstraction
template <class BinContainer> void saveBinStream(const Zstring& filename, const BinContainer& cont); //throw FileError
-template <class BinContainer> BinContainer loadBinStream(const Zstring& filename); //throw FileError, ErrorNotExisting
+template <class BinContainer> BinContainer loadBinStream(const Zstring& filename); //throw FileError
/*
@@ -157,11 +157,11 @@ void saveBinStream(const Zstring& filename, const BinContainer& cont) //throw Fi
template <class BinContainer> inline
-BinContainer loadBinStream(const Zstring& filename) //throw FileError, ErrorNotExisting
+BinContainer loadBinStream(const Zstring& filename) //throw FileError
{
assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further)
- FileInput fileIn(filename); //throw FileError, ErrorNotExisting
+ FileInput fileIn(filename); //throw FileError
BinContainer contOut;
const size_t blockSize = 128 * 1024;
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index c93f2d61..bf47bb1c 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -183,6 +183,9 @@ template <class K, class V> class hash_map : public std::map<K, V> {};
#else
template <class T> class hash_set : public std::unordered_set<T> {};
template <class K, class V> class hash_map : public std::unordered_map<K, V> {};
+//C++11:
+//template <class T> using hash_set = std::unordered_set<T>;
+//template <class K, class V> using hash_map = std::unordered_map<K, V>;
#endif
//as long as variadic templates are not available in MSVC
diff --git a/zen/string_base.h b/zen/string_base.h
index 591ed62b..31a09e63 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -21,7 +21,7 @@ namespace zen
Allocator Policy:
-----------------
void* allocate(size_t size) //throw std::bad_alloc
- void deallocate(void* ptr)
+ void deallocate(void* ptr) //must handle deallocate(nullptr)!
size_t calcCapacity(size_t length)
*/
class AllocatorOptimalSpeed //exponential growth + min size
@@ -30,8 +30,9 @@ public:
//::operator new/ ::operator delete show same performance characterisics like malloc()/free()!
static void* allocate(size_t size) { return ::operator new(size); } //throw std::bad_alloc
static void deallocate(void* ptr) { ::operator delete(ptr); }
- static size_t calcCapacity(size_t length) { return std::max<size_t>(std::max<size_t>(16, length), length + length / 2); } //size_t might overflow!
- //any growth rate should not exceed golden ratio: 1.618033989
+ static size_t calcCapacity(size_t length) { return std::max<size_t>(16, std::max(length + length / 2, length)); }
+ //- size_t might overflow! => better catch here than return a too small size covering up the real error: a way too large length!
+ //- any growth rate should not exceed golden ratio: 1.618033989
};
@@ -52,7 +53,7 @@ template <typename Char, //Character Type
Char* create(size_t size)
Char* create(size_t size, size_t minCapacity)
Char* clone(Char* ptr)
- void destroy(Char* ptr)
+ void destroy(Char* ptr) //must handle destroy(nullptr)!
bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
size_t length(const Char* ptr)
void setLength(Char* ptr, size_t newLength)
@@ -87,7 +88,7 @@ protected:
return newData;
}
- static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); }
+ static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); } //should support destroy(nullptr)!
//this needs to be checked before writing to "ptr"
static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; }
@@ -141,6 +142,7 @@ protected:
static void destroy(Char* ptr)
{
+ if (!ptr) return; //support destroy(nullptr)
assert(descr(ptr)->refCount > 0);
if (--descr(ptr)->refCount == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation!
{
@@ -251,7 +253,7 @@ public:
private:
Zbase(int); //
- Zbase& operator=(int); //detect usage errors
+ Zbase& operator=(int); //detect usage errors by creating an intentional ambiguity with "Char"
Zbase& operator+=(int); //
void push_back(int); //
@@ -294,15 +296,6 @@ template <class Char, template <class, class> class SP, class AP> inline Zbase<C
-
-
-
-
-
-
-
-
-
//################################# implementation ########################################
template <class Char, template <class, class> class SP, class AP> inline
Zbase<Char, SP, AP>::Zbase()
@@ -361,16 +354,9 @@ Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source)
template <class Char, template <class, class> class SP, class AP> inline
Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp)
{
- if (this->canWrite(tmp.rawStr, 0)) //perf: following optimization saves about 4%
- {
- //do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory!
- //instead create a dummy string and swap:
- rawStr = this->create(0); //no perf issue! see comment in default constructor
- rawStr[0] = 0;
- swap(tmp);
- }
- else //shared representation: yet another "add ref" won't hurt
- rawStr = this->clone(tmp.rawStr);
+ rawStr = tmp.rawStr;
+ tmp.rawStr = nullptr; //usually nullptr would violate the class invarants, but it is good enough for the destructor!
+ //caveat: do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory!
}
@@ -388,7 +374,7 @@ Zbase<Char, SP, AP>::Zbase(const S& other, typename S::value_type)
template <class Char, template <class, class> class SP, class AP> inline
Zbase<Char, SP, AP>::~Zbase()
{
- this->destroy(rawStr);
+ this->destroy(rawStr); //rawStr may be nullptr; see move constructor!
}
@@ -499,25 +485,25 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const
template <class Char, template <class, class> class SP, class AP> inline
void Zbase<Char, SP, AP>::resize(size_t newSize, Char fillChar)
{
+ const size_t oldSize = length();
if (this->canWrite(rawStr, newSize))
{
- if (length() < newSize)
- std::fill(rawStr + length(), rawStr + newSize, fillChar);
+ if (oldSize < newSize)
+ std::fill(rawStr + oldSize, rawStr + newSize, fillChar);
rawStr[newSize] = 0;
- this->setLength(rawStr, newSize); //keep after call to length()
+ this->setLength(rawStr, newSize);
}
else
{
Char* newStr = this->create(newSize);
- newStr[newSize] = 0;
-
- if (length() < newSize)
+ if (oldSize < newSize)
{
- std::copy(rawStr, rawStr + length(), newStr);
- std::fill(newStr + length(), newStr + newSize, fillChar);
+ std::copy(rawStr, rawStr + oldSize, newStr);
+ std::fill(newStr + oldSize, newStr + newSize, fillChar);
}
else
std::copy(rawStr, rawStr + newSize, newStr);
+ newStr[newSize] = 0;
this->destroy(rawStr);
rawStr = newStr;
@@ -614,7 +600,7 @@ void Zbase<Char, SP, AP>::clear()
{
if (this->canWrite(rawStr, 0))
{
- rawStr[0] = 0; //keep allocated memory
+ rawStr[0] = 0; //keep allocated memory
this->setLength(rawStr, 0); //
}
else
@@ -636,8 +622,9 @@ void Zbase<Char, SP, AP>::reserve(size_t minCapacity) //make unshared and check
if (!this->canWrite(rawStr, minCapacity))
{
//allocate a new string
- Char* newStr = this->create(length(), std::max(minCapacity, length())); //reserve() must NEVER shrink the string: logical const!
- std::copy(rawStr, rawStr + length() + 1, newStr); //include 0-termination
+ const size_t len = length();
+ Char* newStr = this->create(len, std::max(len, minCapacity)); //reserve() must NEVER shrink the string: logical const!
+ std::copy(rawStr, rawStr + len + 1, newStr); //include 0-termination
this->destroy(rawStr);
rawStr = newStr;
diff --git a/zen/sys_error.h b/zen/sys_error.h
index bbac2eaa..cea2f5f9 100644
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -28,14 +28,14 @@ namespace zen
typedef DWORD ErrorCode;
#elif defined ZEN_LINUX || defined ZEN_MAC
typedef int ErrorCode;
+#else
+#error define a platform!
#endif
ErrorCode getLastError();
std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError);
-bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" aliases
-
//A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"!
class SysError
@@ -105,20 +105,6 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE
return output;
}
-
-
-inline
-bool errorCodeForNotExisting(ErrorCode lastError)
-{
-#ifdef ZEN_WIN
- return lastError == ERROR_FILE_NOT_FOUND ||
- lastError == ERROR_PATH_NOT_FOUND ||
- lastError == ERROR_BAD_NETPATH ||
- lastError == ERROR_NETNAME_DELETED;
-#elif defined ZEN_LINUX || defined ZEN_MAC
- return lastError == ENOENT;
-#endif
-}
}
#endif //LAST_ERROR_H_3284791347018951324534
diff --git a/zen/thread.h b/zen/thread.h
index 638d9474..76596513 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -128,7 +128,7 @@ public:
void reportFinished(std::unique_ptr<T>&& result)
{
{
- boost::unique_lock<boost::mutex> dummy(lockResult);
+ boost::lock_guard<boost::mutex> dummy(lockResult);
++jobsFinished;
if (!result_)
result_ = std::move(result);
@@ -149,7 +149,6 @@ public:
std::unique_ptr<T> getResult(size_t jobsTotal)
{
boost::unique_lock<boost::mutex> dummy(lockResult);
-
while (!jobDone(jobsTotal))
conditionJobDone.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point!
diff --git a/zen/type_tools.h b/zen/type_tools.h
index e5ce29bd..76a12a5a 100644
--- a/zen/type_tools.h
+++ b/zen/type_tools.h
@@ -29,10 +29,10 @@ template <class T, class U>
struct SelectIf<false, T, U> : ResultType<U> {};
//------------------------------------------------------
template <class T, class U>
-struct IsSameType : StaticBool<false> {};
+struct IsSameType : FalseType {};
template <class T>
-struct IsSameType<T, T> : StaticBool<true> {};
+struct IsSameType<T, T> : TrueType {};
//------------------------------------------------------
template <bool, class T = void>
diff --git a/zen/type_traits.h b/zen/type_traits.h
index 4f71f961..a90b9793 100644
--- a/zen/type_traits.h
+++ b/zen/type_traits.h
@@ -21,6 +21,9 @@ struct StaticInt
template <bool b>
struct StaticBool : StaticInt<b> {};
+typedef StaticBool<true> TrueType;
+typedef StaticBool<false> FalseType;
+
template <class EnumType, EnumType val>
struct StaticEnum
{
@@ -45,7 +48,7 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat
//remaining non-arithmetic types: bool, char, wchar_t
//optional: specialize new types like:
-//template <> struct IsUnsignedInt<UInt64> : StaticBool<true> {};
+//template <> struct IsUnsignedInt<UInt64> : TrueType {};
//################# Class Members ########################
@@ -81,10 +84,10 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat
//################ implementation ######################
-#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : StaticBool<true> {};
+#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : TrueType {};
template <class T>
-struct IsUnsignedInt : StaticBool<false> {};
+struct IsUnsignedInt : FalseType {};
ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned char);
ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned short int);
@@ -94,7 +97,7 @@ ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long long int); //new with C++11 -
//------------------------------------------------------
template <class T>
-struct IsSignedInt : StaticBool<false> {};
+struct IsSignedInt : FalseType {};
ZEN_SPECIALIZE_TRAIT(IsSignedInt, signed char);
ZEN_SPECIALIZE_TRAIT(IsSignedInt, short int);
@@ -104,7 +107,7 @@ ZEN_SPECIALIZE_TRAIT(IsSignedInt, long long int); //new with C++11 - same type a
//------------------------------------------------------
template <class T>
-struct IsFloat : StaticBool<false> {};
+struct IsFloat : FalseType {};
ZEN_SPECIALIZE_TRAIT(IsFloat, float);
ZEN_SPECIALIZE_TRAIT(IsFloat, double);
@@ -143,7 +146,7 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
}; \
\
template<class T> \
- struct HasMemberImpl_##NAME<false, T> : StaticBool<false> {}; \
+ struct HasMemberImpl_##NAME<false, T> : FalseType {}; \
\
template<typename T> \
struct HasMember_##NAME : StaticBool<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {};
diff --git a/zen/win.h b/zen/win.h
index 45487a09..40a157f5 100644
--- a/zen/win.h
+++ b/zen/win.h
@@ -23,7 +23,6 @@
#endif
#include <windows.h>
-
#endif
//------------------------------------------------------
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index a469ade2..1cc037e8 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -33,19 +33,15 @@ public:
void insert(const void* ptr, size_t size)
{
boost::lock_guard<boost::mutex> dummy(lockActStrings);
- if (activeStrings.find(ptr) != activeStrings.end())
+ if (!activeStrings.insert(std::make_pair(ptr, size)).second)
reportProblem("Fatal Error: New memory points into occupied space: " + rawMemToString(ptr, size));
-
- activeStrings[ptr] = size;
}
void remove(const void* ptr)
{
boost::lock_guard<boost::mutex> dummy(lockActStrings);
- if (activeStrings.find(ptr) == activeStrings.end())
+ if (activeStrings.erase(ptr) != 1)
reportProblem("Fatal Error: No memory available for deallocation at this location!");
-
- activeStrings.erase(ptr);
}
static LeakChecker& instance() { static LeakChecker inst; return inst; }
diff --git a/zen/zstring.h b/zen/zstring.h
index 80c267e3..f103faf7 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -28,11 +28,11 @@ class AllocatorFreeStoreChecked
public:
static void* allocate(size_t size) //throw std::bad_alloc
{
- void* newMem = ::operator new(size);
+ void* ptr = zen::AllocatorOptimalSpeed::allocate(size);
#ifndef NDEBUG
- z_impl::leakCheckerInsert(newMem, size); //test Zbase for memory leaks
+ z_impl::leakCheckerInsert(ptr, size); //test Zbase for memory leaks
#endif
- return newMem;
+ return ptr;
}
static void deallocate(void* ptr)
@@ -40,10 +40,10 @@ public:
#ifndef NDEBUG
z_impl::leakCheckerRemove(ptr); //check for memory leaks
#endif
- ::operator delete(ptr);
+ zen::AllocatorOptimalSpeed::deallocate(ptr);
}
- static size_t calcCapacity(size_t length) { return std::max<size_t>(16, length + length / 2); } //exponential growth + min size
+ static size_t calcCapacity(size_t length) { return zen::AllocatorOptimalSpeed::calcCapacity(length); }
};
bgstack15