summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:21:59 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:21:59 +0200
commitd4af25c52a28b93484ffb55e0a8027bc4ce7856f (patch)
tree853d57468d6b370711e7a5dd2c3dc7d5bac81b10 /ui
parent5.8 (diff)
downloadFreeFileSync-d4af25c52a28b93484ffb55e0a8027bc4ce7856f.tar.gz
FreeFileSync-d4af25c52a28b93484ffb55e0a8027bc4ce7856f.tar.bz2
FreeFileSync-d4af25c52a28b93484ffb55e0a8027bc4ce7856f.zip
5.9
Diffstat (limited to 'ui')
-rw-r--r--ui/IFileDialog_Vista/ifile_dialog.cpp18
-rw-r--r--ui/IFileDialog_Vista/ifile_dialog.h12
-rw-r--r--ui/batch_config.cpp1
-rw-r--r--ui/batch_status_handler.cpp13
-rw-r--r--ui/check_version.cpp177
-rw-r--r--ui/custom_grid.cpp63
-rw-r--r--ui/dir_name.cpp9
-rw-r--r--ui/folder_history_box.cpp14
-rw-r--r--ui/gui_generated.cpp29
-rw-r--r--ui/gui_generated.h20
-rw-r--r--ui/gui_status_handler.cpp12
-rw-r--r--ui/main_dlg.cpp188
-rw-r--r--ui/main_dlg.h15
-rw-r--r--ui/progress_indicator.cpp63
-rw-r--r--ui/progress_indicator.h1
-rw-r--r--ui/search.cpp1
-rw-r--r--ui/sync_cfg.cpp2
17 files changed, 472 insertions, 166 deletions
diff --git a/ui/IFileDialog_Vista/ifile_dialog.cpp b/ui/IFileDialog_Vista/ifile_dialog.cpp
index 8c3e694c..565dfa1a 100644
--- a/ui/IFileDialog_Vista/ifile_dialog.cpp
+++ b/ui/IFileDialog_Vista/ifile_dialog.cpp
@@ -19,6 +19,7 @@ namespace
{
bool showFolderPickerImpl(HWND ownerWindow, //throw ComError; return "false" if cancelled by user
const wchar_t* defaultFolder, //optional!
+ const GUID* persistenceGuid, //
std::wstring& selectedFolder)
{
ComPtr<IFileDialog> fileDlg;
@@ -27,6 +28,9 @@ bool showFolderPickerImpl(HWND ownerWindow, //throw ComError; return "false" if
CLSCTX_ALL,
IID_PPV_ARGS(fileDlg.init())));
+ if (persistenceGuid)
+ ZEN_CHECK_COM(fileDlg->SetClientGuid(*persistenceGuid));
+
FILEOPENDIALOGOPTIONS dlgOptions = 0;
ZEN_CHECK_COM(fileDlg->GetOptions(&dlgOptions)); //throw ComError
ZEN_CHECK_COM(fileDlg->SetOptions(dlgOptions | FOS_PICKFOLDERS | FOS_NOVALIDATE | FOS_FORCEFILESYSTEM));
@@ -57,7 +61,7 @@ bool showFolderPickerImpl(HWND ownerWindow, //throw ComError; return "false" if
return true;
}
-const wchar_t* allocString(const std::wstring& msg) //ownership passed
+wchar_t* allocString(const std::wstring& msg) //ownership passed
{
auto tmp = new wchar_t [msg.size() + 1]; //std::bad_alloc ?
::wmemcpy(tmp, msg.c_str(), msg.size() + 1); //include 0-termination
@@ -69,9 +73,10 @@ const wchar_t* allocString(const std::wstring& msg) //ownership passed
void ifile::showFolderPicker(void* ownerWindow,
const wchar_t* defaultFolder,
- const wchar_t*& selectedFolder,
+ const GuidProxy* guid,
+ wchar_t*& selectedFolder,
bool& cancelled,
- const wchar_t*& errorMsg)
+ wchar_t*& errorMsg)
{
selectedFolder = nullptr;
cancelled = false;
@@ -79,8 +84,13 @@ void ifile::showFolderPicker(void* ownerWindow,
try
{
+ static_assert(sizeof(GuidProxy) == sizeof(GUID), "");
+ GUID winGuid = {};
+ if (guid)
+ ::memcpy(&winGuid, guid, sizeof(GUID));
+
std::wstring folderPath;
- if (showFolderPickerImpl(static_cast<HWND>(ownerWindow), defaultFolder, folderPath)) //throw ComError
+ if (showFolderPickerImpl(static_cast<HWND>(ownerWindow), defaultFolder, guid ? &winGuid : nullptr, folderPath)) //throw ComError
selectedFolder = allocString(folderPath);
else
cancelled = true;
diff --git a/ui/IFileDialog_Vista/ifile_dialog.h b/ui/IFileDialog_Vista/ifile_dialog.h
index d0099dda..5b4dc532 100644
--- a/ui/IFileDialog_Vista/ifile_dialog.h
+++ b/ui/IFileDialog_Vista/ifile_dialog.h
@@ -25,12 +25,15 @@ namespace ifile
//COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
//Requires Windows Vista and later
+typedef char GuidProxy[16]; //= Windows 128-bit GUID; we don't want to include "Guiddef.h" here!
+
DLL_FUNCTION_DECLARATION
void showFolderPicker(void* ownerWindow, //in; ==HWND
const wchar_t* defaultFolder, //in, optional!
- const wchar_t*& selectedFolder, //out: call freeString() after use!
+ const GuidProxy* guid, //set nullptr by default: Windows stores dialog state (position, x, y coordinates, ect.) associated with the process executable name => use other GUID when needed
+ wchar_t*& selectedFolder, //out: call freeString() after use!
bool& cancelled, //out
- const wchar_t*& errorMsg); //out, optional: call freeString() after use!
+ wchar_t*& errorMsg); //out, optional: call freeString() after use!
DLL_FUNCTION_DECLARATION
void freeString(const wchar_t* str);
@@ -40,9 +43,10 @@ void freeString(const wchar_t* str);
----------*/
typedef bool (*FunType_showFolderPicker)(void* ownerWindow,
const wchar_t* defaultFolder,
- const wchar_t*& selectedFolder,
+ const GuidProxy* guid,
+ wchar_t*& selectedFolder,
bool& cancelled,
- const wchar_t*& errorMsg);
+ wchar_t*& errorMsg);
typedef void (*FunType_freeString)(const wchar_t* str);
/*--------------
diff --git a/ui/batch_config.cpp b/ui/batch_config.cpp
index f07dbc07..d5aa7bc2 100644
--- a/ui/batch_config.cpp
+++ b/ui/batch_config.cpp
@@ -14,6 +14,7 @@
#include <wx+/mouse_move_dlg.h>
#include <wx+/context_menu.h>
#include <zen/file_handling.h>
+#include "../ui/exec_finished_box.h"
#include "../lib/help_provider.h"
#include "folder_pair.h"
#include "msg_popup.h"
diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp
index 594ffb7a..b33b0d80 100644
--- a/ui/batch_status_handler.cpp
+++ b/ui/batch_status_handler.cpp
@@ -135,7 +135,8 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
BatchStatusHandler::~BatchStatusHandler()
{
- const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log
+ const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log
+ const int totalWarnings = errorLog.getItemCount(TYPE_WARNING);
//finalize error log
std::wstring finalStatus;
@@ -149,6 +150,12 @@ BatchStatusHandler::~BatchStatusHandler()
{
raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_ERRORS);
finalStatus = _("Synchronization completed with errors!");
+ errorLog.logMsg(finalStatus, TYPE_ERROR);
+ }
+ else if (totalWarnings > 0)
+ {
+ raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_WARNINGS);
+ finalStatus = _("Synchronization completed with warnings!");
errorLog.logMsg(finalStatus, TYPE_WARNING);
}
else
@@ -190,7 +197,7 @@ BatchStatusHandler::~BatchStatusHandler()
switchBatchToGui_.execute(); //open FreeFileSync GUI
}
catch (...) {}
- syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is main window => program will quit directly
+ syncStatusFrame.closeWindowDirectly(); //syncStatusFrame is not main window anymore
}
else
{
@@ -220,6 +227,8 @@ BatchStatusHandler::~BatchStatusHandler()
syncStatusFrame.processHasFinished(SyncStatus::RESULT_ABORTED, errorLog); //enable okay and close events
else if (totalErrors > 0)
syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_ERROR, errorLog);
+ else if (totalWarnings > 0)
+ syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_WARNINGS, errorLog);
else
syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_SUCCESS, errorLog);
}
diff --git a/ui/check_version.cpp b/ui/check_version.cpp
index 5bf67b90..c9d2049d 100644
--- a/ui/check_version.cpp
+++ b/ui/check_version.cpp
@@ -16,50 +16,179 @@
#include "msg_popup.h"
#include "../version/version.h"
#include "../lib/ffs_paths.h"
+#include <zen/scope_guard.h>
+
+#ifdef FFS_WIN
+#include <wininet.h>
+#endif
using namespace zen;
namespace
{
+#ifdef FFS_WIN
+class InternetConnectionError {};
+
+class WinInetAccess //using IE proxy settings! :)
+{
+public:
+ WinInetAccess(const wchar_t* url) //throw InternetConnectionError (if url cannot be reached; no need to also call readBytes())
+ {
+ //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection!
+
+ hInternet = ::InternetOpen(L"FreeFileSync", //_In_ LPCTSTR lpszAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
+ nullptr, //_In_ LPCTSTR lpszProxyName,
+ nullptr, //_In_ LPCTSTR lpszProxyBypass,
+ 0); //_In_ DWORD dwFlags
+ if (!hInternet)
+ throw InternetConnectionError();
+ zen::ScopeGuard guardInternet = zen::makeGuard([&] { ::InternetCloseHandle(hInternet); });
+
+ hRequest = ::InternetOpenUrl(hInternet, //_In_ HINTERNET hInternet,
+ url, //_In_ LPCTSTR lpszUrl,
+ nullptr, //_In_ LPCTSTR lpszHeaders,
+ 0, //_In_ DWORD dwHeadersLength,
+ INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_UI, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ if (!hRequest) //won't fail due to unreachable url here! There is no substitute for HTTP_QUERY_STATUS_CODE!!!
+ throw InternetConnectionError();
+ zen::ScopeGuard guardRequest = zen::makeGuard([&] { ::InternetCloseHandle(hRequest); });
+
+ DWORD statusCode = 0;
+ DWORD bufferLength = sizeof(statusCode);
+ if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
+ HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
+ &statusCode, //_Inout_ LPVOID lpvBuffer,
+ &bufferLength, //_Inout_ LPDWORD lpdwBufferLength,
+ nullptr)) //_Inout_ LPDWORD lpdwIndex
+ throw InternetConnectionError();
+
+ if (statusCode != HTTP_STATUS_OK)
+ throw InternetConnectionError(); //e.g. 404 - HTTP_STATUS_NOT_FOUND
+
+ guardRequest.dismiss();
+ guardInternet.dismiss();
+ }
+
+ ~WinInetAccess()
+ {
+ ::InternetCloseHandle(hRequest);
+ ::InternetCloseHandle(hInternet);
+ }
+
+ template <class OutputIterator>
+ OutputIterator readBytes(OutputIterator result) //throw InternetConnectionError
+ {
+ //internet says "HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers...
+ const DWORD bufferSize = 64 * 1024;
+ std::vector<char> buffer(bufferSize);
+ for (;;)
+ {
+ DWORD bytesRead = 0;
+ if (!::InternetReadFile(hRequest, //_In_ HINTERNET hFile,
+ &buffer[0], //_Out_ LPVOID lpBuffer,
+ bufferSize, //_In_ DWORD dwNumberOfBytesToRead,
+ &bytesRead)) //_Out_ LPDWORD lpdwNumberOfBytesRead
+ throw InternetConnectionError();
+ if (bytesRead == 0)
+ return result;
+
+ result = std::copy(buffer.begin(), buffer.begin() + bytesRead, result);
+ }
+ }
+
+private:
+ HINTERNET hInternet;
+ HINTERNET hRequest;
+};
+
+
+inline
+bool canAccessUrl(const wchar_t* url) //throw ()
+{
+ try
+ {
+ (void)WinInetAccess(url); //throw InternetConnectionError
+ return true;
+ }
+ catch (const InternetConnectionError&)
+ {
+ return false;
+ }
+}
+
+
+template <class OutputIterator> inline
+OutputIterator readBytesUrl(const wchar_t* url, OutputIterator result) //throw InternetConnectionError
+{
+ return WinInetAccess(url).readBytes(result); //throw InternetConnectionError
+}
+#endif
+
+
enum GetVerResult
{
GET_VER_SUCCESS,
- GET_VER_NO_CONNECTION, //no internet connection?
+ GET_VER_NO_CONNECTION, //no internet connection or just Sourceforge down?
GET_VER_PAGE_NOT_FOUND //version file seems to have moved! => trigger an update!
};
GetVerResult getOnlineVersion(wxString& version) //empty string on error;
{
- wxWindowDisabler dummy;
+#ifdef FFS_WIN
+ //internet access supporting proxy connections
+ std::vector<char> output;
+ try
+ {
+ readBytesUrl(L"http://freefilesync.sourceforge.net/latest_version.txt", std::back_inserter(output)); //throw InternetConnectionError
+ }
+ catch (const InternetConnectionError&)
+ {
+ return canAccessUrl(L"http://sourceforge.net/") ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION;
+ }
- wxHTTP webAccess;
- webAccess.SetHeader(L"content-type", L"text/html; charset=utf-8");
- webAccess.SetTimeout(5); //5 seconds of timeout instead of 10 minutes(WTF are they thinking???)...
+ output.push_back('\0');
+ version = utfCvrtTo<wxString>(&output[0]);
+ return GET_VER_SUCCESS;
- if (webAccess.Connect(L"freefilesync.sourceforge.net")) //only the server, no pages here yet...
- {
- //wxApp::IsMainLoopRunning(); // should return true
+#else
+ wxWindowDisabler dummy;
- std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(L"/latest_version.txt"));
- //must be deleted BEFORE webAccess is closed
+ auto getStringFromUrl = [](const wxString& server, const wxString& page, int timeout, wxString* output) -> bool //true on successful connection
+ {
+ wxHTTP webAccess;
+ webAccess.SetHeader(L"content-type", L"text/html; charset=utf-8");
+ webAccess.SetTimeout(timeout); //default: 10 minutes(WTF are they thinking???)...
- if (httpStream && webAccess.GetError() == wxPROTO_NOERR)
+ if (webAccess.Connect(server)) //will *not* fail for non-reachable url here!
{
- wxString tmp;
- wxStringOutputStream outStream(&tmp);
- httpStream->Read(outStream);
- version = tmp;
- return GET_VER_SUCCESS;
+ //wxApp::IsMainLoopRunning(); // should return true
+
+ std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page));
+ //must be deleted BEFORE webAccess is closed
+
+ if (httpStream && webAccess.GetError() == wxPROTO_NOERR)
+ {
+ if (output)
+ {
+ output->clear();
+ wxStringOutputStream outStream(output);
+ httpStream->Read(outStream);
+ }
+ return true;
+ }
}
- else
- return GET_VER_PAGE_NOT_FOUND;
- }
- else //check if sourceforge in general is reachable
- {
- webAccess.SetTimeout(1);
- return webAccess.Connect(L"sourceforge.net") ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION;
- }
+ return false;
+ };
+
+ if (getStringFromUrl(L"freefilesync.sourceforge.net", L"/latest_version.txt", 5, &version))
+ return GET_VER_SUCCESS;
+
+ const bool canConnectToSf = getStringFromUrl(L"sourceforge.net", L"/", 1, nullptr);
+ return canConnectToSf ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION;
+#endif
}
diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp
index 13964d9e..e7152905 100644
--- a/ui/custom_grid.cpp
+++ b/ui/custom_grid.cpp
@@ -224,7 +224,7 @@ protected:
else
{
//alternate background color to improve readability (while lacking cell borders)
- if (getRowDisplayType(row) == DISP_TYPE_NORMAL && row % 2 == 0)
+ if (getRowDisplayType(row) == DISP_TYPE_NORMAL && row % 2 == 1)
{
//accessibility, support high-contrast schemes => work with user-defined background color!
const auto backCol = getBackGroundColor(row);
@@ -1192,7 +1192,7 @@ public:
GridDataLeft& provLeft,
GridDataMiddle& provMiddle,
GridDataRight& provRight) :
- gridL_(gridL), gridC_(gridC), gridR_(gridR),
+ gridL_(gridL), gridC_(gridC), gridR_(gridR), scrollMaster(nullptr),
provLeft_(provLeft), provMiddle_(provMiddle), provRight_(provRight)
{
gridL_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnL), nullptr, this);
@@ -1209,29 +1209,31 @@ public:
gridC_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onCenterSelectEnd ), nullptr, this);
//clear selection of other grid when selecting on
- gridL_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionL), nullptr, this);
- gridR_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionR), nullptr, this);
+ gridL_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionL), nullptr, this);
+ gridR_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionR), nullptr, this);
//parallel grid scrolling: do NOT use DoPrepareDC() to align grids! GDI resource leak! Use regular paint event instead:
- gridL_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridL), NULL, this);
- gridC_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridC), NULL, this);
- gridR_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridR), NULL, this);
-
- gridL_.Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxEventHandler(GridEventManager::onGridAccessL), NULL, this);
- gridL_.Connect(wxEVT_SCROLLWIN_PAGEUP, wxEventHandler(GridEventManager::onGridAccessL), NULL, this);
- gridL_.Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxEventHandler(GridEventManager::onGridAccessL), NULL, this);
- gridL_.Connect(wxEVT_SCROLLWIN_TOP, wxEventHandler(GridEventManager::onGridAccessL), NULL, this);
- gridL_.Connect(wxEVT_SCROLLWIN_BOTTOM, wxEventHandler(GridEventManager::onGridAccessL), NULL, this);
- gridL_.Connect(wxEVT_SCROLLWIN_LINEUP, wxEventHandler(GridEventManager::onGridAccessL), NULL, this);
- gridL_.Connect(wxEVT_SCROLLWIN_LINEDOWN, wxEventHandler(GridEventManager::onGridAccessL), NULL, this);
-
- gridR_.Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxEventHandler(GridEventManager::onGridAccessR), NULL, this);
- gridR_.Connect(wxEVT_SCROLLWIN_PAGEUP, wxEventHandler(GridEventManager::onGridAccessR), NULL, this);
- gridR_.Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxEventHandler(GridEventManager::onGridAccessR), NULL, this);
- gridR_.Connect(wxEVT_SCROLLWIN_TOP, wxEventHandler(GridEventManager::onGridAccessR), NULL, this);
- gridR_.Connect(wxEVT_SCROLLWIN_BOTTOM, wxEventHandler(GridEventManager::onGridAccessR), NULL, this);
- gridR_.Connect(wxEVT_SCROLLWIN_LINEUP, wxEventHandler(GridEventManager::onGridAccessR), NULL, this);
- gridR_.Connect(wxEVT_SCROLLWIN_LINEDOWN, wxEventHandler(GridEventManager::onGridAccessR), NULL, this);
+ gridL_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridL), nullptr, this);
+ gridC_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridC), nullptr, this);
+ gridR_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridR), nullptr, this);
+
+ auto connectGridAccess = [&](Grid& grid, wxObjectEventFunction func)
+ {
+ grid.Connect(wxEVT_SCROLLWIN_TOP, func, nullptr, this);
+ grid.Connect(wxEVT_SCROLLWIN_BOTTOM, func, nullptr, this);
+ grid.Connect(wxEVT_SCROLLWIN_LINEUP, func, nullptr, this);
+ grid.Connect(wxEVT_SCROLLWIN_LINEDOWN, func, nullptr, this);
+ grid.Connect(wxEVT_SCROLLWIN_PAGEUP, func, nullptr, this);
+ grid.Connect(wxEVT_SCROLLWIN_PAGEDOWN, func, nullptr, this);
+ grid.Connect(wxEVT_SCROLLWIN_THUMBTRACK, func, nullptr, this);
+
+ grid.getMainWin().Connect(wxEVT_SET_FOCUS, func, nullptr, this);
+ //on wxEVT_KILL_FOCUS, there's no need to reset "scrollMaster"
+
+ };
+ connectGridAccess(gridL_, wxEventHandler(GridEventManager::onGridAccessL));
+ connectGridAccess(gridC_, wxEventHandler(GridEventManager::onGridAccessC));
+ connectGridAccess(gridR_, wxEventHandler(GridEventManager::onGridAccessR));
Connect(EVENT_ALIGN_SCROLLBARS, wxEventHandler(GridEventManager::onAlignScrollBars), NULL, this);
}
@@ -1350,8 +1352,9 @@ private:
trg.setColumnConfig(cfgTrg);
}
- void onGridAccessL(wxEvent& event) { gridL_.SetFocus(); event.Skip(); }
- void onGridAccessR(wxEvent& event) { gridR_.SetFocus(); event.Skip(); }
+ void onGridAccessL(wxEvent& event) { scrollMaster = &gridL_; event.Skip(); }
+ void onGridAccessC(wxEvent& event) { scrollMaster = &gridC_; event.Skip(); }
+ void onGridAccessR(wxEvent& event) { scrollMaster = &gridR_; event.Skip(); }
void onPaintGridL(wxEvent& event) { onPaintGrid(gridL_); event.Skip(); }
void onPaintGridC(wxEvent& event) { onPaintGrid(gridC_); event.Skip(); }
@@ -1367,9 +1370,9 @@ private:
Grid* follow2 = nullptr;
auto setGrids = [&](const Grid& l, Grid& f1, Grid& f2) { lead = &l; follow1 = &f1; follow2 = &f2; };
- if (wxWindow::FindFocus() == &gridC_.getMainWin())
+ if (&gridC_ == scrollMaster)
setGrids(gridC_, gridL_, gridR_);
- else if (wxWindow::FindFocus() == &gridR_.getMainWin())
+ else if (&gridR_ == scrollMaster)
setGrids(gridR_, gridL_, gridC_);
else //default: left panel
setGrids(gridL_, gridC_, gridR_);
@@ -1384,7 +1387,7 @@ private:
int yOld = 0;
target.GetViewStart(nullptr, &yOld);
if (yOld != y)
- target.Scroll(-1, y);
+ target.Scroll(-1, y); //empirical test Windows/Ubuntu: this call does NOT trigger a wxEVT_SCROLLWIN event, which would incorrectly set "scrollMaster" to "&target"!
};
int y = 0;
lead->GetViewStart(nullptr, &y);
@@ -1423,6 +1426,10 @@ private:
Grid& gridL_;
Grid& gridC_;
Grid& gridR_;
+
+ const Grid* scrollMaster; //for address check only; this needn't be the grid having focus!
+ //e.g. mouse wheel events should set window under cursor as scrollMaster, but *not* change focus
+
GridDataLeft& provLeft_;
GridDataMiddle& provMiddle_;
GridDataRight& provRight_;
diff --git a/ui/dir_name.cpp b/ui/dir_name.cpp
index 406903e6..7d4f7a31 100644
--- a/ui/dir_name.cpp
+++ b/ui/dir_name.cpp
@@ -185,14 +185,19 @@ void DirectoryName<NameControl>::OnSelectDir(wxCommandEvent& event)
const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString);
if (showFolderPicker && freeString)
{
- const wchar_t* selectedFolder = nullptr;
- const wchar_t* errorMsg = nullptr;
+ wchar_t* selectedFolder = nullptr;
+ wchar_t* errorMsg = nullptr;
bool cancelled = false;
ZEN_ON_SCOPE_EXIT(freeString(selectedFolder));
ZEN_ON_SCOPE_EXIT(freeString(errorMsg));
+ const GuidProxy guid = { '\x0', '\x4a', '\xf9', '\x31', '\xb4', '\x92', '\x40', '\xa0',
+ '\x8d', '\xc2', '\xc', '\xa5', '\xef', '\x59', '\x6e', '\x3b'
+ }; //some random GUID => have Windows save IFileDialog state separately from other file/dir pickers!
+
showFolderPicker(static_cast<HWND>(selectButton_.GetHWND()), //in; ==HWND
defaultDirname.empty() ? nullptr : defaultDirname.c_str(), //in, optional!
+ &guid,
selectedFolder, //out: call freeString() after use!
cancelled, //out
errorMsg); //out, optional: call freeString() after use!
diff --git a/ui/folder_history_box.cpp b/ui/folder_history_box.cpp
index 2832afed..cefc03c8 100644
--- a/ui/folder_history_box.cpp
+++ b/ui/folder_history_box.cpp
@@ -36,10 +36,16 @@ FolderHistoryBox::FolderHistoryBox(wxWindow* parent,
/*##*/ SetMinSize(wxSize(150, -1)); //## workaround yet another wxWidgets bug: default minimum size is much too large for a wxComboBox
//#####################################
- Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(FolderHistoryBox::OnKeyEvent ), nullptr, this);
- Connect(wxEVT_LEFT_DOWN, wxEventHandler(FolderHistoryBox::OnUpdateList), nullptr, this);
+ Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(FolderHistoryBox::OnKeyEvent ), nullptr, this);
Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(FolderHistoryBox::OnSelection ), nullptr, this);
- Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(FolderHistoryBox::OnMouseWheel), nullptr, this);
+ Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(FolderHistoryBox::OnMouseWheel), nullptr, this);
+#ifdef FFS_WIN //on Win, this event only fires, when clicking on the small down arrow, NOT when clicking on the text field
+ //thanks to wxWidgets' non-portability it's exactly the converse on Linux!
+ Connect(wxEVT_LEFT_DOWN, wxEventHandler(FolderHistoryBox::OnUpdateList), nullptr, this);
+#elif defined FFS_LINUX //update on each text change: maybe a little too often, but we have no choice as long as we don't have an event right before showing the drop-down list
+ Connect(wxEVT_COMMAND_TEXT_UPDATED, wxEventHandler(FolderHistoryBox::OnUpdateList), nullptr, this);
+#endif
+
#if wxCHECK_VERSION(2, 9, 1)
Connect(wxEVT_COMMAND_COMBOBOX_DROPDOWN, wxCommandEventHandler(FolderHistoryBox::OnShowDropDown), nullptr, this);
@@ -73,10 +79,8 @@ void FolderHistoryBox::setValueAndUpdateList(const wxString& dirname)
std::list<Zstring> dirList;
//add some aliases to allow user changing to volume name and back, if possible
-#ifdef FFS_WIN
std::vector<Zstring> aliases = getDirectoryAliases(toZ(dirname));
dirList.insert(dirList.end(), aliases.begin(), aliases.end());
-#endif
if (sharedHistory_.get())
{
diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp
index 2e99377a..d132d748 100644
--- a/ui/gui_generated.cpp
+++ b/ui/gui_generated.cpp
@@ -1,10 +1,19 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Apr 10 2012)
+// C++ code generated with wxFormBuilder (version Oct 8 2012)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
+#include "../wx+/button.h"
+#include "../wx+/graph.h"
+#include "../wx+/grid.h"
+#include "../wx+/toggle_button.h"
+#include "exec_finished_box.h"
+#include "folder_history_box.h"
+#include "triple_splitter.h"
+#include "wx_form_build_hide_warnings.h"
+
#include "gui_generated.h"
///////////////////////////////////////////////////////////////////////////
@@ -259,6 +268,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
wxBoxSizer* bSizer1771;
bSizer1771 = new wxBoxSizer( wxVERTICAL );
+
+ bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 );
+
m_bpButtonSwapSides = new wxBitmapButton( m_panelTopMiddle, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW );
m_bpButtonSwapSides->SetToolTip( _("Swap sides") );
@@ -280,6 +292,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer1771->Add( bSizer160, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
+ bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 );
+
+
m_panelTopMiddle->SetSizer( bSizer1771 );
m_panelTopMiddle->Layout();
bSizer1771->Fit( m_panelTopMiddle );
@@ -851,6 +866,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this );
m_listBoxHistory->Connect( wxEVT_CHAR, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this );
m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this );
+ m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this );
m_bpButtonFilter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this );
m_checkBoxShowExcluded->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnShowExcluded ), NULL, this );
m_bpButtonSyncCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateLeft ), NULL, this );
@@ -897,6 +913,7 @@ MainDialogGenerated::~MainDialogGenerated()
m_bpButtonSave->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this );
m_listBoxHistory->Disconnect( wxEVT_CHAR, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this );
m_listBoxHistory->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this );
+ m_listBoxHistory->Disconnect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this );
m_bpButtonFilter->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigureFilter ), NULL, this );
m_checkBoxShowExcluded->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnShowExcluded ), NULL, this );
m_bpButtonSyncCreateLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateLeft ), NULL, this );
@@ -1015,6 +1032,7 @@ CompareStatusGenerated::CompareStatusGenerated( wxWindow* parent, wxWindowID id,
bSizer182 = new wxBoxSizer( wxVERTICAL );
m_textCtrlStatus = new wxTextCtrl( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, wxTE_READONLY );
+ m_textCtrlStatus->SetMaxLength( 0 );
m_textCtrlStatus->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
bSizer182->Add( m_textCtrlStatus, 0, wxEXPAND, 5 );
@@ -2266,6 +2284,7 @@ SyncStatusDlgGenerated::SyncStatusDlgGenerated( wxWindow* parent, wxWindowID id,
bSizer1811 = new wxBoxSizer( wxVERTICAL );
m_textCtrlStatus = new wxTextCtrl( m_panelProgress, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), wxTE_READONLY|wxNO_BORDER );
+ m_textCtrlStatus->SetMaxLength( 0 );
m_textCtrlStatus->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
bSizer1811->Add( m_textCtrlStatus, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT, 5 );
@@ -2508,6 +2527,7 @@ LogControlGenerated::LogControlGenerated( wxWindow* parent, wxWindowID id, const
bSizer153->Add( m_staticline13, 0, wxEXPAND, 5 );
m_textCtrlInfo = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER );
+ m_textCtrlInfo->SetMaxLength( 0 );
bSizer153->Add( m_textCtrlInfo, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 );
@@ -2747,7 +2767,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer170->Add( 0, 0, 1, wxEXPAND, 5 );
- m_hyperlink1 = new wxHyperlinkCtrl( this, wxID_ANY, _("Homepage"), wxT("http://sourceforge.net/projects/freefilesync/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink1 = new wxHyperlinkCtrl( this, wxID_ANY, _("Homepage"), wxT("http://freefilesync.sourceforge.net/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink1->SetFont( wxFont( 10, 70, 90, 92, true, wxEmptyString ) );
m_hyperlink1->SetToolTip( _("http://sourceforge.net/projects/freefilesync/") );
@@ -2855,6 +2875,7 @@ MessageDlgGenerated::MessageDlgGenerated( wxWindow* parent, wxWindowID id, const
bSizer26->Add( m_bitmapMsgType, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_textCtrlMessage = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 400,130 ), wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER );
+ m_textCtrlMessage->SetMaxLength( 0 );
bSizer26->Add( m_textCtrlMessage, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
@@ -2963,6 +2984,7 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer24->Add( m_staticline91, 0, wxEXPAND|wxBOTTOM, 5 );
m_textCtrlFileList = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 550,200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER );
+ m_textCtrlFileList->SetMaxLength( 0 );
bSizer24->Add( m_textCtrlFileList, 1, wxEXPAND|wxLEFT, 5 );
m_staticline9 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
@@ -3107,6 +3129,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w
sbSizer8->Add( m_bitmapInclude, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 );
m_textCtrlInclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE );
+ m_textCtrlInclude->SetMaxLength( 0 );
sbSizer8->Add( m_textCtrlInclude, 1, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 );
@@ -3119,6 +3142,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w
sbSizer26->Add( m_bitmapExclude, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_textCtrlExclude = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE );
+ m_textCtrlExclude->SetMaxLength( 0 );
sbSizer26->Add( m_textCtrlExclude, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 );
@@ -3693,6 +3717,7 @@ SearchDialogGenerated::SearchDialogGenerated( wxWindow* parent, wxWindowID id, c
bSizer162->Add( m_staticText101, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_textCtrlSearchTxt = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), 0 );
+ m_textCtrlSearchTxt->SetMaxLength( 0 );
bSizer162->Add( m_textCtrlSearchTxt, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
diff --git a/ui/gui_generated.h b/ui/gui_generated.h
index fc5291e3..14e30f75 100644
--- a/ui/gui_generated.h
+++ b/ui/gui_generated.h
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Apr 10 2012)
+// C++ code generated with wxFormBuilder (version Oct 8 2012)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@@ -11,14 +11,15 @@
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
-#include "wx_form_build_hide_warnings.h"
-#include "../wx+/button.h"
-#include "folder_history_box.h"
-#include "../wx+/grid.h"
-#include "triple_splitter.h"
-#include "../wx+/toggle_button.h"
-#include "exec_finished_box.h"
-#include "../wx+/graph.h"
+class ExecFinishedBox;
+class FolderHistoryBox;
+class ToggleButton;
+class wxStaticText;
+namespace zen{ class BitmapButton; }
+namespace zen{ class Graph2D; }
+namespace zen{ class Grid; }
+namespace zen{ class TripleSplitter; }
+
#include <wx/string.h>
#include <wx/bitmap.h>
#include <wx/image.h>
@@ -192,6 +193,7 @@ class MainDialogGenerated : public wxFrame
virtual void OnSwapSides( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCfgHistoryKeyEvent( wxKeyEvent& event ) { event.Skip(); }
virtual void OnLoadFromHistory( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnLoadFromHistoryDoubleClick( wxCommandEvent& event ) { event.Skip(); }
virtual void OnConfigureFilter( wxCommandEvent& event ) { event.Skip(); }
virtual void OnShowExcluded( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSyncCreateLeft( wxCommandEvent& event ) { event.Skip(); }
diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp
index 63e394ff..120eab39 100644
--- a/ui/gui_status_handler.cpp
+++ b/ui/gui_status_handler.cpp
@@ -194,7 +194,8 @@ SyncStatusHandler::SyncStatusHandler(MainDialog* parentDlg,
SyncStatusHandler::~SyncStatusHandler()
{
- const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log
+ const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log
+ const int totalWarnings = errorLog.getItemCount(TYPE_WARNING);
//finalize error log
//finalize error log
@@ -207,7 +208,12 @@ SyncStatusHandler::~SyncStatusHandler()
else if (totalErrors > 0)
{
finalStatus = _("Synchronization completed with errors!");
- errorLog.logMsg(finalStatus, TYPE_WARNING);
+ errorLog.logMsg(finalStatus, TYPE_ERROR);
+ }
+ else if (totalWarnings > 0)
+ {
+ finalStatus = _("Synchronization completed with warnings!");
+ errorLog.logMsg(finalStatus, TYPE_WARNING); //give status code same warning priority as display category!
}
else
{
@@ -250,6 +256,8 @@ SyncStatusHandler::~SyncStatusHandler()
syncStatusFrame.processHasFinished(SyncStatus::RESULT_ABORTED, errorLog); //enable okay and close events
else if (totalErrors > 0)
syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_ERROR, errorLog);
+ else if (totalWarnings > 0)
+ syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_WARNINGS, errorLog);
else
syncStatusFrame.processHasFinished(SyncStatus::RESULT_FINISHED_WITH_SUCCESS, errorLog);
}
diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp
index 2d52d45e..0004d2fc 100644
--- a/ui/main_dlg.cpp
+++ b/ui/main_dlg.cpp
@@ -55,6 +55,7 @@
#include <wx+/no_flicker.h>
#include <wx+/grid.h>
#include "../lib/error_log.h"
+#include "triple_splitter.h"
using namespace zen;
using namespace std::rel_ops;
@@ -309,23 +310,23 @@ void setMenuItemImage(wxMenuItem*& menuItem, const wxBitmap& bmp)
if (wxMenu* menu = menuItem->GetMenu())
{
- int pos = menu->GetMenuItems().IndexOf(menuItem);
- if (pos != wxNOT_FOUND)
- {
- /*
- menu->Remove(item); ->this simple sequence crashes on Kubuntu x64, wxWidgets 2.9.2
- menu->Insert(index, item);
- */
- const bool enabled = menuItem->IsEnabled();
- wxMenuItem* newItem = new wxMenuItem(menu, menuItem->GetId(), menuItem->GetItemLabel());
- newItem->SetBitmap(bmp);
-
- menu->Destroy(menuItem); //actual workaround
- menuItem = menu->Insert(pos, newItem); //don't forget to update input item pointer!
-
- if (!enabled)
- menuItem->Enable(false); //do not enable BEFORE appending item! wxWidgets screws up for yet another crappy reason
- }
+ int pos = menu->GetMenuItems().IndexOf(menuItem);
+ if (pos != wxNOT_FOUND)
+ {
+ /*
+ menu->Remove(item); ->this simple sequence crashes on Kubuntu x64, wxWidgets 2.9.2
+ menu->Insert(index, item);
+ */
+ const bool enabled = menuItem->IsEnabled();
+ wxMenuItem* newItem = new wxMenuItem(menu, menuItem->GetId(), menuItem->GetItemLabel());
+ newItem->SetBitmap(bmp);
+
+ menu->Destroy(menuItem); //actual workaround
+ menuItem = menu->Insert(pos, newItem); //don't forget to update input item pointer!
+
+ if (!enabled)
+ menuItem->Enable(false); //do not enable BEFORE appending item! wxWidgets screws up for yet another crappy reason
+ }
}
}
@@ -578,7 +579,7 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
//init handling of first folder pair
firstFolderPair.reset(new DirectoryPairFirst(*this));
- initViewFilterButtons();
+ //initViewFilterButtons();
//init grid settings
gridview::init(*m_gridMainL, *m_gridMainC, *m_gridMainR, gridDataView);
@@ -783,6 +784,7 @@ void MainDialog::onQueryEndSession()
catch (const xmlAccess::FfsXmlError&) {}
}
+
void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings)
{
globalCfg = globalSettings;
@@ -930,7 +932,7 @@ void MainDialog::setSyncDirManually(const std::vector<FileSystemObject*>& select
}
-void MainDialog::setManualFilter(const std::vector<FileSystemObject*>& selection, bool setIncluded)
+void MainDialog::setFilterManually(const std::vector<FileSystemObject*>& selection, bool setIncluded)
{
//if hidefiltered is active, there should be no filtered elements on screen => current element was filtered out
assert(currentCfg.showFilteredElements || !setIncluded);
@@ -940,7 +942,7 @@ void MainDialog::setManualFilter(const std::vector<FileSystemObject*>& selection
std::for_each(selection.begin(), selection.end(),
[&](FileSystemObject* fsObj) { zen::setActiveStatus(setIncluded, *fsObj); }); //works recursively for directories
- updateGuiAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts
+ updateGuiDelayedIf(!currentCfg.showFilteredElements); //show update GUI before removing rows
}
}
@@ -1170,7 +1172,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
if (!selectionLeft.empty() || !selectionRight.empty())
{
wxWindow* oldFocus = wxWindow::FindFocus();
- ZEN_ON_SCOPE_EXIT( if (oldFocus) oldFocus->SetFocus(); )
+ ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();)
if (zen::showDeleteDialog(this,
selectionLeft,
@@ -1382,7 +1384,8 @@ void MainDialog::disableAllElements(bool enableAbort)
//show abort button
m_buttonAbort->Enable();
m_buttonAbort->Show();
- if (m_buttonAbort->IsShownOnScreen()) m_buttonAbort->SetFocus();
+ if (m_buttonAbort->IsShownOnScreen())
+ m_buttonAbort->SetFocus();
m_buttonCompare->Disable();
m_buttonCompare->Hide();
m_panelTopButtons->Layout();
@@ -1536,9 +1539,9 @@ void MainDialog::onTreeButtonEvent(wxKeyEvent& event)
case WXK_SPACE:
case WXK_NUMPAD_SPACE:
{
- const auto& selection = getTreeSelection();
+ const std::vector<FileSystemObject*>& selection = getTreeSelection();
if (!selection.empty())
- setManualFilter(selection, !selection[0]->isActive());
+ setFilterManually(selection, !selection[0]->isActive());
}
return;
@@ -1622,9 +1625,9 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide)
case WXK_SPACE:
case WXK_NUMPAD_SPACE:
{
- const auto& selection = getGridSelection();
+ const std::vector<FileSystemObject*>& selection = getGridSelection();
if (!selection.empty())
- setManualFilter(selection, !selection[0]->isActive());
+ setFilterManually(selection, !selection[0]->isActive());
}
return;
@@ -1718,14 +1721,16 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
if (!isPartOf(focus, m_gridMainL ) && //
!isPartOf(focus, m_gridMainC ) && //don't propagate keyboard commands if grid is already in focus
!isPartOf(focus, m_gridMainR ) && //
+ !isPartOf(focus, m_gridNavi ) &&
!isPartOf(focus, m_listBoxHistory) && //don't propagate if selecting config
!isPartOf(focus, m_directoryLeft) && //don't propagate if changing directory field
!isPartOf(focus, m_directoryRight) &&
- !isPartOf(focus, m_gridNavi ) &&
!isPartOf(focus, m_scrolledWindowFolderPairs))
- if (wxEvtHandler* evtHandler = m_gridMainL->GetEventHandler())
+ if (wxEvtHandler* evtHandler = m_gridMainL->getMainWin().GetEventHandler())
{
m_gridMainL->SetFocus();
+
+ event.SetEventType(wxEVT_KEY_DOWN); //the grid event handler doesn't expect wxEVT_CHAR_HOOK!
evtHandler->ProcessEvent(event); //propagating event catched at wxTheApp to child leads to recursion, but we prevented it...
event.Skip(false); //definitively handled now!
return;
@@ -1827,9 +1832,9 @@ void MainDialog::onNaviGridContext(GridClickEvent& event)
if (!selection.empty())
{
if (selection[0]->isActive())
- menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, false); }, &GlobalResources::getImage(L"checkboxFalse"));
+ menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &GlobalResources::getImage(L"checkboxFalse"));
else
- menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, true); }, &GlobalResources::getImage(L"checkboxTrue"));
+ menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &GlobalResources::getImage(L"checkboxTrue"));
}
else
menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false);
@@ -1871,7 +1876,7 @@ void MainDialog::onMainGridContextC(GridClickEvent& event)
menu.addItem(_("Exclude all"), [&]
{
zen::setActiveStatus(false, folderCmp);
- updateGuiAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true
+ updateGuiDelayedIf(!currentCfg.showFilteredElements); //show update GUI before removing rows
}, nullptr, gridDataView->rowsTotal() > 0);
menu.popup(*this);
@@ -1919,9 +1924,9 @@ void MainDialog::onMainGridContextRim(bool leftSide)
if (!selection.empty())
{
if (selection[0]->isActive())
- menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, false); }, &GlobalResources::getImage(L"checkboxFalse"));
+ menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &GlobalResources::getImage(L"checkboxFalse"));
else
- menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, true); }, &GlobalResources::getImage(L"checkboxTrue"));
+ menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &GlobalResources::getImage(L"checkboxTrue"));
}
else
menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false);
@@ -2163,7 +2168,8 @@ void MainDialog::onGridLabelContext(Grid& grid, ColumnTypeRim type, const std::v
if (showSelectTimespanDlg(this, manualTimeSpanFrom, manualTimeSpanTo) == ReturnSmallDlg::BUTTON_OKAY)
{
applyTimeSpanFilter(folderCmp, manualTimeSpanFrom, manualTimeSpanTo); //overwrite current active/inactive settings
- updateGuiAfterFilterChange(400);
+ //updateGuiDelayedIf(!currentCfg.showFilteredElements); //show update GUI before removing rows
+ updateGui();
}
};
menu.addItem(_("Select time span..."), selectTimeSpan);
@@ -2376,7 +2382,7 @@ void MainDialog::updateUnsavedCfgStatus()
setImage(*m_bpButtonSave, allowSave ? GlobalResources::getImage(L"save") : makeBrightGrey(GlobalResources::getImage(L"save")));
m_bpButtonSave->Enable(allowSave);
- m_menuItemSave->Enable(allowSave); //bitmap is automatically greyscaled on Win7 (introducing a crappy looking shift), but not on XP
+ m_menuItemSave->Enable(allowSave); //bitmap is automatically greyscaled on Win7 (introducing a crappy looking shift), but not on XP
//set main dialog title
wxString title;
@@ -2551,31 +2557,50 @@ void MainDialog::OnLoadFromHistory(wxCommandEvent& event)
});
if (!filenames.empty())
- {
loadConfiguration(filenames);
- //in case user cancelled saving old config, selection is wrong: so reapply it!
- addFileToCfgHistory(activeConfigFiles);
- }
+ //user changed m_listBoxHistory selection so it's this method's responsibility to synchronize with activeConfigFiles
+ //- if user cancelled saving old config
+ //- there's an error loading new config
+ //- filenames is empty and user tried to unselect the current config
+ addFileToCfgHistory(activeConfigFiles);
}
-void MainDialog::loadConfiguration(const wxString& filename)
+void MainDialog::OnLoadFromHistoryDoubleClick(wxCommandEvent& event)
{
+ wxArrayInt selections;
+ m_listBoxHistory->GetSelections(selections);
+
std::vector<wxString> filenames;
- filenames.push_back(filename);
+ std::for_each(selections.begin(), selections.end(),
+ [&](int pos)
+ {
+ if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(pos)))
+ filenames.push_back(histData->cfgFile_);
+ });
- loadConfiguration(filenames);
+ if (!filenames.empty())
+ if (loadConfiguration(filenames))
+ {
+ //simulate button click on "compare"
+ wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED);
+ if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler())
+ evtHandler->ProcessEvent(dummy2); //synchronous call
+ }
+
+ //synchronize m_listBoxHistory and activeConfigFiles, see OnLoadFromHistory()
+ addFileToCfgHistory(activeConfigFiles);
}
-void MainDialog::loadConfiguration(const std::vector<wxString>& filenames)
+bool MainDialog::loadConfiguration(const std::vector<wxString>& filenames)
{
if (filenames.empty())
- return;
+ return true;
if (!saveOldConfig())
- return;
+ return false; //cancelled by user
//load XML
xmlAccess::XmlGuiConfig newGuiCfg; //structure to receive gui settings, already defaulted!!
@@ -2586,6 +2611,7 @@ void MainDialog::loadConfiguration(const std::vector<wxString>& filenames)
setConfig(newGuiCfg, filenames);
//flashStatusInformation(_("Configuration loaded!")); -> irrelvant!?
+ return true;
}
catch (const xmlAccess::FfsXmlError& error)
{
@@ -2597,6 +2623,7 @@ void MainDialog::loadConfiguration(const std::vector<wxString>& filenames)
}
else
wxMessageBox(error.toString(), _("Error"), wxOK | wxICON_ERROR, this);
+ return false;
}
}
@@ -2670,7 +2697,7 @@ void MainDialog::onCheckRows(CheckRowsEvent& event)
selectedRows.insert(i);
std::vector<FileSystemObject*> objects = gridDataView->getAllFileRef(selectedRows);
- setManualFilter(objects, event.setIncluded_);
+ setFilterManually(objects, event.setIncluded_);
}
}
@@ -2720,7 +2747,7 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::
//evaluate new settings...
//(re-)set view filter buttons
- initViewFilterButtons();
+ initViewFilterButtons(newGuiCfg.mainCfg);
updateFilterButtons();
@@ -2803,18 +2830,18 @@ const wxString& MainDialog::lastRunConfigName()
}
-void MainDialog::updateGuiAfterFilterChange(int delay)
+void MainDialog::updateGuiDelayedIf(bool condition)
{
- //signal UI that grids need to be refreshed on next Update()
+ const int delay = 400;
- if (!currentCfg.showFilteredElements)
+ if (condition)
{
gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR);
m_gridMainL->Update();
m_gridMainC->Update();
m_gridMainR->Update();
- wxMilliSleep(delay); //some delay to show user the rows he has filtered out before they are removed
+ wxMilliSleep(delay); //some delay to show the changed GUI before removing rows from sight
}
updateGui();
@@ -2982,7 +3009,7 @@ wxBitmap buttonReleased(const std::string& name)
}
-void MainDialog::initViewFilterButtons()
+void MainDialog::initViewFilterButtons(const MainConfiguration& mainCfg)
{
//compare result buttons
m_bpButtonLeftOnly->init(buttonPressed("leftOnly"),
@@ -3061,7 +3088,6 @@ void MainDialog::initViewFilterButtons()
m_bpButtonRightOnly-> setActive(true);
m_bpButtonLeftNewer-> setActive(true);
m_bpButtonRightNewer->setActive(true);
- m_bpButtonEqual-> setActive(false);
m_bpButtonDifferent-> setActive(true);
m_bpButtonConflict-> setActive(true);
@@ -3072,7 +3098,46 @@ void MainDialog::initViewFilterButtons()
m_bpButtonSyncDeleteRight-> setActive(true);
m_bpButtonSyncDirOverwLeft-> setActive(true);
m_bpButtonSyncDirOverwRight->setActive(true);
- m_bpButtonSyncDirNone-> setActive(true);
+
+ m_bpButtonEqual->setActive(false);
+
+ //special case "m_bpButtonSyncDirNone": set always active, unless sync direction "none" is part of the rule set:
+ //e.g. for a "custom" config or "update" variant. Otherwise rows with sync direction "none" can only occur on grid if the user manually
+ //sets them, in which case these rows should not be hidden immediately, so m_bpButtonSyncDirNone must be active
+ const std::vector<FolderPairCfg>& cmpCfg = extractCompareCfg(mainCfg);
+ const bool syncDirNonePartOfConfig = std::any_of(cmpCfg.begin(), cmpCfg.end(),
+ [&](const FolderPairCfg& fpCfg) -> bool
+ {
+ //attention: following is quite an amount of implicit/redundant knowledge here... let's hope our model is fundamental enough to not change any time soon!
+
+ if (fpCfg.directionCfg.var == DirectionConfig::AUTOMATIC)
+ return false;
+
+ const DirectionSet dirSet = extractDirections(fpCfg.directionCfg);
+
+ switch (fpCfg.compareVar)
+ {
+ case CMP_BY_TIME_SIZE:
+ return dirSet.exLeftSideOnly == SYNC_DIR_NONE ||
+ dirSet.exRightSideOnly == SYNC_DIR_NONE ||
+ dirSet.leftNewer == SYNC_DIR_NONE ||
+ dirSet.rightNewer == SYNC_DIR_NONE;
+ //dirSet.different == SYNC_DIR_NONE ||
+ //dirSet.conflict == SYNC_DIR_NONE;
+
+ case CMP_BY_CONTENT:
+ return dirSet.exLeftSideOnly == SYNC_DIR_NONE ||
+ dirSet.exRightSideOnly == SYNC_DIR_NONE ||
+ //dirSet.leftNewer == SYNC_DIR_NONE ||
+ //dirSet.rightNewer == SYNC_DIR_NONE ||
+ dirSet.different == SYNC_DIR_NONE;
+ //dirSet.conflict == SYNC_DIR_NONE;
+ }
+ assert(false);
+ return false;
+ });
+
+ m_bpButtonSyncDirNone->setActive(!syncDirNonePartOfConfig);
}
@@ -3105,7 +3170,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
wxBusyCursor dummy; //show hourglass cursor
wxWindow* oldFocus = wxWindow::FindFocus();
- ZEN_ON_SCOPE_EXIT( if (oldFocus) oldFocus->SetFocus(); ); //e.g. keep focus on main grid after pressing F5
+ ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();) //e.g. keep focus on main grid after pressing F5
int scrollPosX = 0;
int scrollPosY = 0;
@@ -3169,7 +3234,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
//add to folder history after successful comparison only
folderHistoryLeft ->addItem(toZ(m_directoryLeft->GetValue()));
folderHistoryRight->addItem(toZ(m_directoryRight->GetValue()));
-
+
//prepare status information
if (allElementsEqual(folderCmp))
flashStatusInformation(_("All folders are in sync!"));
@@ -3378,11 +3443,6 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
syncProcessCfg,
folderCmp,
statusHandler);
-
- //play (optional) sound notification after sync has completed (GUI and batch mode)
- const wxString soundFile = toWx(zen::getResourceDir()) + L"Sync_Complete.wav";
- if (fileExists(toZ(soundFile)))
- wxSound::Play(soundFile, wxSOUND_ASYNC);
}
catch (GuiAbortProcess&)
{
@@ -3674,7 +3734,9 @@ void MainDialog::updateGridViewData()
void MainDialog::applyFilterConfig()
{
applyFiltering(folderCmp, getConfig().mainCfg);
- updateGuiAfterFilterChange(400);
+
+ updateGui();
+ //updateGuiDelayedIf(!currentCfg.showFilteredElements); //show update GUI before removing rows
}
@@ -3771,7 +3833,7 @@ void MainDialog::updateGuiForFolderPair()
m_bpButtonLocalFilter->Show(showLocalCfgFirstPair);
setImage(*m_bpButtonSwapSides, GlobalResources::getImage(showLocalCfgFirstPair ? L"swapSlim" : L"swap"));
m_panelTopMiddle->Layout(); //both required to update button size for calculations below!!!
- m_panelDirectoryPairs->Layout(); // -> updates size of stretched m_panelTopLeft!
+ m_panelDirectoryPairs->Layout(); // -> updates size of stretched m_panelTopLeft!
int addPairMinimalHeight = 0;
int addPairOptimalHeight = 0;
diff --git a/ui/main_dlg.h b/ui/main_dlg.h
index 47630670..f5f07624 100644
--- a/ui/main_dlg.h
+++ b/ui/main_dlg.h
@@ -18,6 +18,7 @@
#include "custom_grid.h"
#include "tree_view.h"
#include <wx+/file_drop.h>
+#include "folder_history_box.h"
//class FolderHistory;
class DirectoryPair;
@@ -77,8 +78,7 @@ private:
void setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings); //messes with Maximize(), window sizes, so call just once!
xmlAccess::XmlGlobalSettings getGlobalCfgBeforeExit(); //destructive "get" thanks to "Iconize(false), Maximize(false)"
- void loadConfiguration(const wxString& filename);
- void loadConfiguration(const std::vector<wxString>& filenames);
+ bool loadConfiguration(const std::vector<wxString>& filenames); //return true if loaded successfully
bool trySaveConfig(const wxString* fileName); //return true if saved successfully
bool saveOldConfig(); //return false on user abort
@@ -89,10 +89,10 @@ private:
//used when saving configuration
std::vector<wxString> activeConfigFiles; //name of currently loaded config file (may be more than 1)
- void initViewFilterButtons();
+ void initViewFilterButtons(const zen::MainConfiguration& mainCfg);
void updateFilterButtons();
- void addFileToCfgHistory(const std::vector<wxString>& filenames);
+ void addFileToCfgHistory(const std::vector<wxString>& filenames); //= update/insert + apply selection
void addFolderPair(const std::vector<zen::FolderPairEnh>& newPairs, bool addFront = false);
void removeAddFolderPair(size_t pos);
@@ -102,6 +102,8 @@ private:
//main method for putting gridDataView on UI: updates data respecting current view settings
void updateGui(); //kitchen-sink update
+ void updateGuiDelayedIf(bool condition); // 400 ms delay
+
void updateGridViewData(); //
void updateStatistics(); // more fine-grained updaters
void updateUnsavedCfgStatus(); //
@@ -111,7 +113,7 @@ private:
std::vector<zen::FileSystemObject*> getTreeSelection() const;
void setSyncDirManually(const std::vector<zen::FileSystemObject*>& selection, zen::SyncDirection direction);
- void setManualFilter(const std::vector<zen::FileSystemObject*>& selection, bool setIncluded);
+ void setFilterManually(const std::vector<zen::FileSystemObject*>& selection, bool setIncluded);
void copySelectionToClipboard();
void deleteSelectedFiles(const std::vector<zen::FileSystemObject*>& selectionLeft,
const std::vector<zen::FileSystemObject*>& selectionRight);
@@ -194,6 +196,7 @@ private:
void OnConfigSaveAs (wxCommandEvent& event);
void OnConfigLoad (wxCommandEvent& event);
void OnLoadFromHistory(wxCommandEvent& event);
+ void OnLoadFromHistoryDoubleClick(wxCommandEvent& event);
void OnCfgHistoryKeyEvent(wxKeyEvent& event);
void OnRegularUpdateCheck(wxIdleEvent& event);
@@ -212,8 +215,6 @@ private:
void OnStartSync (wxCommandEvent& event);
void OnClose (wxCloseEvent& event);
- void updateGuiAfterFilterChange(int delay);
-
void excludeExtension(const Zstring& extension);
void excludeShortname(const zen::FileSystemObject& fsObj);
void excludeItems(const std::vector<zen::FileSystemObject*>& selection);
diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp
index 096d7413..ebbc9edd 100644
--- a/ui/progress_indicator.cpp
+++ b/ui/progress_indicator.cpp
@@ -9,6 +9,7 @@
#include <wx/imaglist.h>
#include <wx/stopwatch.h>
#include <wx/wupdlock.h>
+#include <wx/sound.h>
#include <zen/basic_math.h>
#include <zen/format_unit.h>
#include <wx+/mouse_move_dlg.h>
@@ -16,7 +17,9 @@
#include <wx+/image_tools.h>
#include <wx+/graph.h>
#include <wx+/no_flicker.h>
+#include <zen/file_handling.h>
#include "gui_generated.h"
+#include "../lib/ffs_paths.h"
#include "../lib/resources.h"
#include "../lib/perf_check.h"
#include "tray_icon.h"
@@ -361,20 +364,19 @@ private:
includedTypes |= TYPE_INFO;
//fast replacement for wxString modelling exponential growth
- typedef Zbase<wchar_t> zxString;
- zxString logText;
+ MsgString logText;
const auto& entries = log_.getEntries();
for (auto iter = entries.begin(); iter != entries.end(); ++iter)
if (iter->type & includedTypes)
{
- logText += copyStringTo<zxString>(formatMessage(*iter));
+ logText += formatMessage(*iter);
logText += L'\n';
}
if (logText.empty()) //if no messages match selected view filter, at least show final status message
if (!entries.empty())
- logText = copyStringTo<zxString>(formatMessage(entries.back()));
+ logText = formatMessage(entries.back());
wxWindowUpdateLocker dummy(m_textCtrlInfo);
m_textCtrlInfo->ChangeValue(copyStringTo<wxString>(logText));
@@ -810,6 +812,7 @@ std::wstring getDialogStatusText(const Statistics* syncStat, bool paused, SyncSt
case SyncStatus::RESULT_ABORTED:
return _("Aborted");
case SyncStatus::RESULT_FINISHED_WITH_ERROR:
+ case SyncStatus::RESULT_FINISHED_WITH_WARNINGS:
case SyncStatus::RESULT_FINISHED_WITH_SUCCESS:
return _("Completed");
}
@@ -1007,16 +1010,14 @@ std::wstring SyncStatus::SyncStatusImpl::getExecWhenFinishedCommand() const
void SyncStatus::SyncStatusImpl::updateDialogStatus() //depends on "syncStat_, paused_, finalResult"
{
- m_staticTextStatus->SetLabel(getDialogStatusText(syncStat_, paused_, finalResult));
+ const wxString dlgStatusTxt = getDialogStatusText(syncStat_, paused_, finalResult);
+ m_staticTextStatus->SetLabel(dlgStatusTxt);
+
+ //status bitmap
if (syncStat_) //sync running
{
if (paused_)
- m_buttonPause->SetLabel(_("Continue"));
- else
- m_buttonPause->SetLabel(_("Pause"));
-
- if (paused_)
m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusPause"));
else
switch (syncStat_->currentPhase())
@@ -1036,20 +1037,30 @@ void SyncStatus::SyncStatusImpl::updateDialogStatus() //depends on "syncStat_, p
m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusSyncing"));
break;
}
+
+ m_bitmapStatus->SetToolTip(dlgStatusTxt);
}
else //sync finished
switch (finalResult)
{
case RESULT_ABORTED:
- m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusError"));
+ m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusAborted"));
+ m_bitmapStatus->SetToolTip(_("Synchronization aborted!"));
break;
case RESULT_FINISHED_WITH_ERROR:
- m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusWarning"));
+ m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusFinishedErrors"));
+ m_bitmapStatus->SetToolTip(_("Synchronization completed with errors!"));
+ break;
+
+ case RESULT_FINISHED_WITH_WARNINGS:
+ m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusFinishedWarnings"));
+ m_bitmapStatus->SetToolTip(_("Synchronization completed with warnings!"));
break;
case RESULT_FINISHED_WITH_SUCCESS:
- m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusSuccess"));
+ m_bitmapStatus->SetBitmap(GlobalResources::getImage(L"statusFinishedSuccess"));
+ m_bitmapStatus->SetToolTip(_("Synchronization completed successfully!"));
break;
}
@@ -1082,12 +1093,22 @@ void SyncStatus::SyncStatusImpl::updateDialogStatus() //depends on "syncStat_, p
taskbar_->setStatus(Taskbar::STATUS_ERROR);
break;
+ case RESULT_FINISHED_WITH_WARNINGS:
case RESULT_FINISHED_WITH_SUCCESS:
taskbar_->setStatus(Taskbar::STATUS_NORMAL);
break;
}
}
+ //pause button
+ if (syncStat_) //sync running
+ {
+ if (paused_)
+ m_buttonPause->SetLabel(_("Continue"));
+ else
+ m_buttonPause->SetLabel(_("Pause"));
+ }
+
m_panelHeader->Layout();
Layout();
}
@@ -1228,6 +1249,22 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncResult resultId, const E
m_panelFooter->Layout();
Layout();
+ //play (optional) sound notification after sync has completed -> only play when waiting on results dialog, seems to be pointless otherwise!
+ switch (finalResult)
+ {
+ case SyncStatus::RESULT_ABORTED:
+ break;
+ case SyncStatus::RESULT_FINISHED_WITH_ERROR:
+ case SyncStatus::RESULT_FINISHED_WITH_WARNINGS:
+ case SyncStatus::RESULT_FINISHED_WITH_SUCCESS:
+ {
+ const Zstring soundFile = getResourceDir() + Zstr("Sync_Complete.wav");
+ if (fileExists(soundFile))
+ wxSound::Play(utfCvrtTo<wxString>(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as a service!
+ }
+ break;
+ }
+
//Raise(); -> don't! user may be watching a movie in the meantime ;)
}
diff --git a/ui/progress_indicator.h b/ui/progress_indicator.h
index 5628694f..b3d7ff2f 100644
--- a/ui/progress_indicator.h
+++ b/ui/progress_indicator.h
@@ -61,6 +61,7 @@ public:
{
RESULT_ABORTED,
RESULT_FINISHED_WITH_ERROR,
+ RESULT_FINISHED_WITH_WARNINGS,
RESULT_FINISHED_WITH_SUCCESS
};
//essential to call one of these two methods in StatusUpdater derived class destructor at the LATEST(!)
diff --git a/ui/search.cpp b/ui/search.cpp
index fd3a11c5..be384f74 100644
--- a/ui/search.cpp
+++ b/ui/search.cpp
@@ -9,6 +9,7 @@
#include <wx/msgdlg.h>
#include <wx/utils.h>
#include <utility>
+#include <zen/string_tools.h>
#include <wx+/mouse_move_dlg.h>
using namespace zen;
diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp
index d159503d..ef9d2a68 100644
--- a/ui/sync_cfg.cpp
+++ b/ui/sync_cfg.cpp
@@ -181,7 +181,7 @@ void updateConfigIcons(const DirectionConfig& directionCfg,
buttonConflict->SetToolTip(getSyncOpDescription(SO_OVERWRITE_LEFT));
break;
case SYNC_DIR_NONE:
- buttonConflict->SetBitmapLabel(mirrorIfRtl(GlobalResources::getImage(L"conflict")));
+ buttonConflict->SetBitmapLabel(mirrorIfRtl(GlobalResources::getImage(L"conflict"))); //silent dependency to algorithm.cpp::Redetermine!!!
buttonConflict->SetToolTip(_("Leave as unresolved conflict"));
break;
}
bgstack15