summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:22:18 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:22:18 +0200
commitbcc5cc28c6dc5178e8f4fd0cc521034ae5def388 (patch)
treebacc60d27b435d32172f97643576c5e4e953177d /ui
parent5.9 (diff)
downloadFreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.tar.gz
FreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.tar.bz2
FreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.zip
5.10
Diffstat (limited to 'ui')
-rw-r--r--ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj13
-rw-r--r--ui/Taskbar_Seven/Taskbar_Seven.vcxproj15
-rw-r--r--ui/batch_config.cpp2
-rw-r--r--ui/batch_status_handler.cpp31
-rw-r--r--ui/batch_status_handler.h2
-rw-r--r--ui/column_attr.h5
-rw-r--r--ui/custom_grid.cpp80
-rw-r--r--ui/custom_grid.h12
-rw-r--r--ui/dir_name.cpp2
-rw-r--r--ui/gui_generated.cpp110
-rw-r--r--ui/gui_generated.h10
-rw-r--r--ui/gui_status_handler.cpp17
-rw-r--r--ui/gui_status_handler.h2
-rw-r--r--ui/main_dlg.cpp297
-rw-r--r--ui/main_dlg.h2
-rw-r--r--ui/progress_indicator.cpp411
-rw-r--r--ui/small_dlgs.cpp6
-rw-r--r--ui/tree_view.cpp15
18 files changed, 720 insertions, 312 deletions
diff --git a/ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj b/ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj
index f3dfcd23..1b84eade 100644
--- a/ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj
+++ b/ui/IFileDialog_Vista/IFileDialog_Vista.vcxproj
@@ -19,6 +19,7 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="..\..\zen\debug_memory_leaks.cpp" />
<ClCompile Include="dll_main.cpp" />
<ClCompile Include="ifile_dialog.cpp" />
</ItemGroup>
@@ -117,6 +118,7 @@
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -150,7 +152,9 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
- <AdditionalLibraryDirectories></AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -171,6 +175,7 @@
<DisableSpecificWarnings>4100</DisableSpecificWarnings>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -185,6 +190,7 @@
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -208,6 +214,7 @@
<DisableSpecificWarnings>4100</DisableSpecificWarnings>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -221,7 +228,9 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
- <AdditionalLibraryDirectories></AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/ui/Taskbar_Seven/Taskbar_Seven.vcxproj b/ui/Taskbar_Seven/Taskbar_Seven.vcxproj
index 37786768..a979a1a2 100644
--- a/ui/Taskbar_Seven/Taskbar_Seven.vcxproj
+++ b/ui/Taskbar_Seven/Taskbar_Seven.vcxproj
@@ -96,8 +96,9 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100,4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -109,6 +110,7 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -129,8 +131,9 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100,4996</DisableSpecificWarnings>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
+ <SmallerTypeCheck>true</SmallerTypeCheck>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
@@ -142,6 +145,7 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -159,7 +163,7 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100,4996</DisableSpecificWarnings>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
</ClCompile>
@@ -175,6 +179,7 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX86</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -195,7 +200,7 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <DisableSpecificWarnings>4100</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100,4996</DisableSpecificWarnings>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
</ClCompile>
@@ -211,9 +216,11 @@
</ProfileGuidedDatabase>
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
<TargetMachine>MachineX64</TargetMachine>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClCompile Include="..\..\zen\debug_memory_leaks.cpp" />
<ClCompile Include="dll_main.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
diff --git a/ui/batch_config.cpp b/ui/batch_config.cpp
index d5aa7bc2..0463bd80 100644
--- a/ui/batch_config.cpp
+++ b/ui/batch_config.cpp
@@ -570,7 +570,7 @@ void BatchDialog::loadBatchFile(const std::vector<wxString>& filenames)
const wxString activeFile = filenames.size() == 1 ? filenames[0] : wxString();
if (activeFile.empty())
- SetTitle(_("Create a batch job"));
+ SetTitle(_("Save as batch job"));
else
SetTitle(activeFile);
diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp
index b33b0d80..5a32e545 100644
--- a/ui/batch_status_handler.cpp
+++ b/ui/batch_status_handler.cpp
@@ -38,7 +38,7 @@ private:
virtual std::shared_ptr<TraverseCallback>
onDir (const Zchar* shortName, const Zstring& fullName) { return nullptr; } //DON'T traverse into subdirs
virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) { return LINK_SKIP; }
- virtual HandleError onError (const std::wstring& msg) { return ON_ERROR_IGNORE; } //errors are not really critical in this context
+ virtual HandleError onError (const std::wstring& msg) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context
const Zstring prefix_;
std::vector<Zstring>& logfiles_;
@@ -100,6 +100,7 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
const TimeComp& timeStamp,
const Zstring& logfileDirectory, //may be empty
int logfilesCountLimit,
+ size_t lastSyncsLogFileSizeMax,
const xmlAccess::OnError handleError,
const SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode
FfsReturnCode& returnCode,
@@ -108,6 +109,7 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
switchBatchToGui_(switchBatchToGui),
showFinalResults(showProgress), //=> exit immediately or wait when finished
switchToGuiRequested(false),
+ lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax),
handleError_(handleError),
returnCode_(returnCode),
syncStatusFrame(*this, *this, nullptr, showProgress, jobName, execWhenFinished, execFinishedHistory),
@@ -129,7 +131,7 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
totalTime.Start(); //measure total time
//if (logFile)
- // ::wxSetEnv(L"logfile", utfCvrtTo<wxString>(logFile->getFilename())); -> requires a command line interpreter to take advantage of
+ // ::wxSetEnv(L"logfile", utfCvrtTo<wxString>(logFile->getFilename()));
}
@@ -168,24 +170,35 @@ BatchStatusHandler::~BatchStatusHandler()
errorLog.logMsg(finalStatus, TYPE_INFO);
}
- const Utf8String logStream = generateLogStream(errorLog, jobName_, finalStatus,
- getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING),
- getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), totalTime.Time() / 1000);
+ const SummaryInfo summary =
+ {
+ jobName_,
+ finalStatus,
+ getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING),
+ getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING),
+ totalTime.Time() / 1000
+ };
+
//print the results list: logfile
if (logFile.get())
{
+ //saving log file below may take a *long* time, so report (without logging)
try
{
- if (!logStream.empty())
- logFile->write(&*logStream.begin(), logStream.size()); //throw FileError
+ reportStatus(replaceCpy(_("Saving log file %x"), L"%x", fmtFileName(logFile->getFilename()))); //throw?
+ forceUiRefresh(); //
+ }
+ catch (...) {}
+ try
+ {
+ saveLogToFile(summary, errorLog, *logFile); //throw FileError
}
catch (FileError&) {}
-
logFile.reset(); //close file now: user may do something with it in "on completion"
}
try
{
- saveToLastSyncsLog(logStream); //throw FileError
+ saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError
}
catch (FileError&) {}
diff --git a/ui/batch_status_handler.h b/ui/batch_status_handler.h
index d83f8acc..884f22e5 100644
--- a/ui/batch_status_handler.h
+++ b/ui/batch_status_handler.h
@@ -29,6 +29,7 @@ public:
const zen::TimeComp& timeStamp,
const Zstring& logfileDirectory,
int logfilesCountLimit, //0: logging inactive; < 0: no limit
+ size_t lastSyncsLogFileSizeMax,
const xmlAccess::OnError handleError,
const zen::SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode
zen::FfsReturnCode& returnCode,
@@ -51,6 +52,7 @@ private:
const zen::SwitchToGui& switchBatchToGui_; //functionality to change from batch mode to GUI mode
bool showFinalResults;
bool switchToGuiRequested;
+ const size_t lastSyncsLogFileSizeMax_;
xmlAccess::OnError handleError_;
zen::ErrorLog errorLog; //list of non-resolved errors and warnings
zen::FfsReturnCode& returnCode_;
diff --git a/ui/column_attr.h b/ui/column_attr.h
index 23cff92c..8152c01c 100644
--- a/ui/column_attr.h
+++ b/ui/column_attr.h
@@ -44,8 +44,8 @@ std::vector<ColumnAttributeRim> getDefaultColumnAttributesLeft()
attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, 0, false));
attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, 200, 0, true));
attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, -280, 1, true)); //stretch to full width and substract sum of fixed size widths!
- attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true));
attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false));
+ attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true));
attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false));
return attr;
}
@@ -57,8 +57,8 @@ std::vector<ColumnAttributeRim> getDefaultColumnAttributesRight()
attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, 0, false));
attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, 200, 0, false)); //already shown on left side
attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, -80, 1, true)); //stretch to full width and substract sum of fixed size widths!
- attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true));
attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false));
+ attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true));
attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false));
return attr;
}
@@ -72,7 +72,6 @@ enum ColumnTypeMiddle
COL_TYPE_BORDER
};
-
//------------------------------------------------------------------
enum ColumnTypeNavi
diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp
index e7152905..975dca5a 100644
--- a/ui/custom_grid.cpp
+++ b/ui/custom_grid.cpp
@@ -11,6 +11,7 @@
#include <zen/file_error.h>
#include <zen/basic_math.h>
#include <zen/format_unit.h>
+#include <zen/scope_guard.h>
#include <wx+/tooltip.h>
#include <wx+/string_conv.h>
#include <wx+/rtl.h>
@@ -71,7 +72,7 @@ void refreshCell(Grid& grid, size_t row, ColumnType colType)
}
-std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(Grid& grid) //returns range [from, to)
+std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(const Grid& grid) //returns range [from, to)
{
const wxSize clientSize = grid.getMainWin().GetClientSize();
if (clientSize.GetHeight() > 0)
@@ -79,15 +80,15 @@ std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(Grid& grid) //returns range [from
wxPoint topLeft = grid.CalcUnscrolledPosition(wxPoint(0, 0));
wxPoint bottom = grid.CalcUnscrolledPosition(wxPoint(0, clientSize.GetHeight() - 1));
- ptrdiff_t rowFrom = grid.getRowAtPos(topLeft.y); //returns < 0 if column not found; absolute coordinates!
+ const ptrdiff_t rowCount = grid.getRowCount();
+ const ptrdiff_t rowFrom = grid.getRowAtPos(topLeft.y); //return -1 for invalid position, rowCount if out of range
if (rowFrom >= 0)
{
- ptrdiff_t rowEnd = grid.getRowAtPos(bottom.y); //returns < 0 if column not found; absolute coordinates!
- if (rowEnd < 0)
- rowEnd = grid.getRowCount();
+ const ptrdiff_t rowTo = grid.getRowAtPos(bottom.y);
+ if (0 <= rowTo && rowTo < rowCount)
+ return std::make_pair(rowFrom, rowTo + 1);
else
- ++rowEnd;
- return std::make_pair(rowFrom, rowEnd);
+ return std::make_pair(rowFrom, rowCount);
}
}
return std::make_pair(0, 0);
@@ -448,7 +449,6 @@ private:
static const int CELL_BORDER = 2;
-
virtual void renderCell(Grid& grid, wxDC& dc, const wxRect& rect, size_t row, ColumnType colType)
{
wxRect rectTmp = rect;
@@ -784,20 +784,20 @@ public:
if (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE &&
row < refGrid().getRowCount())
{
- refGrid().clearSelection();
- dragSelection.reset(new std::pair<size_t, BlockPosition>(row, mousePosToBlock(clientPos, row)));
+ refGrid().clearSelection(false); //don't emit event, prevent recursion!
+ dragSelection = make_unique<std::pair<size_t, BlockPosition>>(row, mousePosToBlock(clientPos, row));
}
}
- void onSelectEnd(size_t rowFrom, size_t rowTo) //we cannot reuse row from "onSelectBegin": rowFrom and rowTo may be different if user is holding shift
+ void onSelectEnd(size_t rowFirst, size_t rowLast) //we cannot reuse row from "onSelectBegin": if user is holding shift, this may now be in the middle of the range!
{
- refGrid().clearSelection();
+ refGrid().clearSelection(false); //don't emit event, prevent recursion!
//issue custom event
if (dragSelection)
{
- if (rowFrom < refGrid().getRowCount() &&
- rowTo < refGrid().getRowCount()) //row is -1 on capture lost!
+ if (rowFirst < rowLast && //may be empty? probably not in this context
+ rowLast <= refGrid().getRowCount())
{
if (wxEvtHandler* evtHandler = refGrid().GetEventHandler())
switch (dragSelection->second)
@@ -807,25 +807,25 @@ public:
const FileSystemObject* fsObj = getRawData(dragSelection->first);
const bool setIncluded = fsObj ? !fsObj->isActive() : true;
- CheckRowsEvent evt(rowFrom, rowTo, setIncluded);
+ CheckRowsEvent evt(rowFirst, rowLast, setIncluded);
evtHandler->ProcessEvent(evt);
}
break;
case BLOCKPOS_LEFT:
{
- SyncDirectionEvent evt(rowFrom, rowTo, SYNC_DIR_LEFT);
+ SyncDirectionEvent evt(rowFirst, rowLast, SYNC_DIR_LEFT);
evtHandler->ProcessEvent(evt);
}
break;
case BLOCKPOS_MIDDLE:
{
- SyncDirectionEvent evt(rowFrom, rowTo, SYNC_DIR_NONE);
+ SyncDirectionEvent evt(rowFirst, rowLast, SYNC_DIR_NONE);
evtHandler->ProcessEvent(evt);
}
break;
case BLOCKPOS_RIGHT:
{
- SyncDirectionEvent evt(rowFrom, rowTo, SYNC_DIR_RIGHT);
+ SyncDirectionEvent evt(rowFirst, rowLast, SYNC_DIR_RIGHT);
evtHandler->ProcessEvent(evt);
}
break;
@@ -844,12 +844,13 @@ public:
}
else
{
- if (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE)
+ if (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE &&
+ row < refGrid().getRowCount())
{
if (highlight) //refresh old highlight
refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE));
- highlight.reset(new std::pair<size_t, BlockPosition>(row, mousePosToBlock(clientPos, row)));
+ highlight = make_unique<std::pair<size_t, BlockPosition>>(row, mousePosToBlock(clientPos, row));
refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE));
//show custom tooltip
@@ -1193,7 +1194,8 @@ public:
GridDataMiddle& provMiddle,
GridDataRight& provRight) :
gridL_(gridL), gridC_(gridC), gridR_(gridR), scrollMaster(nullptr),
- provLeft_(provLeft), provMiddle_(provMiddle), provRight_(provRight)
+ provLeft_(provLeft), provMiddle_(provMiddle), provRight_(provRight),
+ scrollbarUpdatePending(false)
{
gridL_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnL), nullptr, this);
gridR_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnR), nullptr, this);
@@ -1238,6 +1240,8 @@ public:
Connect(EVENT_ALIGN_SCROLLBARS, wxEventHandler(GridEventManager::onAlignScrollBars), NULL, this);
}
+ ~GridEventManager() { assert(!scrollbarUpdatePending); }
+
private:
void onCenterSelectBegin(GridClickEvent& event)
{
@@ -1248,15 +1252,14 @@ private:
void onCenterSelectEnd(GridRangeSelectEvent& event)
{
- if (event.positive_) //we do NOT want to react on GridRangeSelectEvent() within Grid::clearSelectionAll() directly following right mouse click!
- provMiddle_.onSelectEnd(event.rowFrom_, event.rowTo_);
+ provMiddle_.onSelectEnd(event.rowFirst_, event.rowLast_);
event.Skip();
}
void onCenterMouseMovement(wxMouseEvent& event)
{
const wxPoint& topLeftAbs = gridC_.CalcUnscrolledPosition(event.GetPosition());
- const int row = gridC_.getRowAtPos(topLeftAbs.y); //returns < 0 if column not found; absolute coordinates!
+ const ptrdiff_t row = gridC_.getRowAtPos(topLeftAbs.y); //return -1 for invalid position, rowCount if one past the end
if (auto colInfo = gridC_.getColumnAtPos(topLeftAbs.x)) //(column type, component position)
{
//redirect mouse movement to middle grid component
@@ -1277,7 +1280,7 @@ private:
void onGridSelection(const Grid& grid, Grid& other)
{
if (!wxGetKeyState(WXK_CONTROL)) //clear other grid unless user is holding CTRL
- other.clearSelection();
+ other.clearSelection(false); //don't emit event, prevent recursion!
}
void onKeyDownL(wxKeyEvent& event) { onKeyDown(event, gridL_); }
@@ -1397,13 +1400,22 @@ private:
//harmonize placement of horizontal scrollbar to avoid grids getting out of sync!
//since this affects the grid that is currently repainted as well, we do work asynchronously!
//avoids at least this problem: remaining graphics artifact when changing from Grid::SB_SHOW_ALWAYS to Grid::SB_SHOW_NEVER at location of old scrollbar (Windows only)
- wxCommandEvent alignEvent(EVENT_ALIGN_SCROLLBARS);
- AddPendingEvent(alignEvent); //waits until next idle event - may take up to a second if the app is busy on wxGTK!
+
+ //perf note: send one async event at most, else they may accumulate and create perf issues, see grid.cpp
+ if (!scrollbarUpdatePending)
+ {
+ scrollbarUpdatePending = true;
+ wxCommandEvent alignEvent(EVENT_ALIGN_SCROLLBARS);
+ AddPendingEvent(alignEvent); //waits until next idle event - may take up to a second if the app is busy on wxGTK!
+ }
}
void onAlignScrollBars(wxEvent& event)
{
- auto needsHorizontalScrollbars = [](Grid& grid) -> bool
+ ZEN_ON_SCOPE_EXIT(scrollbarUpdatePending = false);
+ assert(scrollbarUpdatePending);
+
+ auto needsHorizontalScrollbars = [](const Grid& grid) -> bool
{
const wxWindow& mainWin = grid.getMainWin();
return mainWin.GetVirtualSize().GetWidth() > mainWin.GetClientSize().GetWidth();
@@ -1433,6 +1445,8 @@ private:
GridDataLeft& provLeft_;
GridDataMiddle& provMiddle_;
GridDataRight& provRight_;
+
+ bool scrollbarUpdatePending;
};
}
@@ -1541,6 +1555,7 @@ private:
};
}
+
void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz)
{
auto* provLeft = dynamic_cast<GridDataLeft*>(gridLeft .getDataProvider());
@@ -1548,7 +1563,7 @@ void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, boo
if (provLeft && provRight)
{
- int newRowHeight = 0;
+ int iconHeight = 0;
if (show)
{
auto iconMgr = std::make_shared<IconManager>(sz);
@@ -1556,14 +1571,17 @@ void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, boo
provLeft ->setIconManager(iconMgr);
provRight->setIconManager(iconMgr);
- newRowHeight = iconMgr->iconBuffer.getSize() + 1; //+ 1 for line between rows
+ iconHeight = iconMgr->iconBuffer.getSize();
}
else
{
provLeft ->setIconManager(nullptr);
provRight->setIconManager(nullptr);
- newRowHeight = IconBuffer(IconBuffer::SIZE_SMALL).getSize() + 1; //+ 1 for line between rows
+ iconHeight = IconBuffer(IconBuffer::SIZE_SMALL).getSize();
}
+
+ const int newRowHeight = std::max(iconHeight, gridLeft.getMainWin().GetCharHeight()) + 1; //add some space
+
gridLeft .setRowHeight(newRowHeight);
gridCenter.setRowHeight(newRowHeight);
gridRight .setRowHeight(newRowHeight);
diff --git a/ui/custom_grid.h b/ui/custom_grid.h
index b5a4cce1..6381c8c0 100644
--- a/ui/custom_grid.h
+++ b/ui/custom_grid.h
@@ -48,22 +48,22 @@ extern const wxEventType EVENT_GRID_SYNC_DIRECTION;
struct CheckRowsEvent : public wxCommandEvent
{
- CheckRowsEvent(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool setIncluded) : wxCommandEvent(EVENT_GRID_CHECK_ROWS), rowFrom_(rowFrom), rowTo_(rowTo), setIncluded_(setIncluded) {}
+ CheckRowsEvent(size_t rowFirst, size_t rowLast, bool setIncluded) : wxCommandEvent(EVENT_GRID_CHECK_ROWS), rowFirst_(rowFirst), rowLast_(rowLast), setIncluded_(setIncluded) { assert(rowFirst <= rowLast); }
virtual wxEvent* Clone() const { return new CheckRowsEvent(*this); }
- const ptrdiff_t rowFrom_;
- const ptrdiff_t rowTo_;
+ const size_t rowFirst_; //selected range: [rowFirst_, rowLast_)
+ const size_t rowLast_; //range is empty when clearing selection
const bool setIncluded_;
};
struct SyncDirectionEvent : public wxCommandEvent
{
- SyncDirectionEvent(ptrdiff_t rowFrom, ptrdiff_t rowTo, SyncDirection direction) : wxCommandEvent(EVENT_GRID_SYNC_DIRECTION), rowFrom_(rowFrom), rowTo_(rowTo), direction_(direction) {}
+ SyncDirectionEvent(size_t rowFirst, size_t rowLast, SyncDirection direction) : wxCommandEvent(EVENT_GRID_SYNC_DIRECTION), rowFirst_(rowFirst), rowLast_(rowLast), direction_(direction) { assert(rowFirst <= rowLast); }
virtual wxEvent* Clone() const { return new SyncDirectionEvent(*this); }
- const ptrdiff_t rowFrom_;
- const ptrdiff_t rowTo_;
+ const size_t rowFirst_; //see CheckRowsEvent
+ const size_t rowLast_; //
const SyncDirection direction_;
};
diff --git a/ui/dir_name.cpp b/ui/dir_name.cpp
index 7d4f7a31..a1a00d74 100644
--- a/ui/dir_name.cpp
+++ b/ui/dir_name.cpp
@@ -175,7 +175,7 @@ void DirectoryName<NameControl>::OnSelectDir(wxCommandEvent& event)
}
}
- //wxDirDialog internally uses lame looking SHBrowseForFolder(); Better use IFileDialog() instead! (remembers size and position!)
+ //wxDirDialog internally uses lame-looking SHBrowseForFolder(); we better use IFileDialog() instead! (remembers size and position!)
std::unique_ptr<wxString> newFolder;
#ifdef FFS_WIN
if (vistaOrLater())
diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp
index d132d748..6619b646 100644
--- a/ui/gui_generated.cpp
+++ b/ui/gui_generated.cpp
@@ -41,6 +41,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_menuItemSaveAs = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL );
m_menuFile->Append( m_menuItemSaveAs );
+ m_menuItem7 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("Save as &batch job...") ) , wxEmptyString, wxITEM_NORMAL );
+ m_menuFile->Append( m_menuItem7 );
+
m_menuFile->AppendSeparator();
m_menuItem10 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("1. &Compare") ) + wxT('\t') + wxT("F5"), wxEmptyString, wxITEM_NORMAL );
@@ -76,9 +79,6 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_menuItemGlobSett = new wxMenuItem( m_menuAdvanced, wxID_PREFERENCES, wxString( _("&Global settings...") ) , wxEmptyString, wxITEM_NORMAL );
m_menuAdvanced->Append( m_menuItemGlobSett );
- m_menuItem7 = new wxMenuItem( m_menuAdvanced, wxID_ANY, wxString( _("&Create batch job...") ) , wxEmptyString, wxITEM_NORMAL );
- m_menuAdvanced->Append( m_menuItem7 );
-
wxMenuItem* m_menuItem5;
m_menuItem5 = new wxMenuItem( m_menuAdvanced, wxID_ANY, wxString( _("&Export file list...") ) , wxEmptyString, wxITEM_NORMAL );
m_menuAdvanced->Append( m_menuItem5 );
@@ -346,7 +346,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer1601->Add( bSizer91, 0, wxEXPAND, 5 );
m_scrolledWindowFolderPairs = new wxScrolledWindow( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxHSCROLL|wxVSCROLL );
- m_scrolledWindowFolderPairs->SetScrollRate( 5, 5 );
+ m_scrolledWindowFolderPairs->SetScrollRate( 10, 10 );
m_scrolledWindowFolderPairs->SetMinSize( wxSize( -1,0 ) );
bSizerAddFolderPairs = new wxBoxSizer( wxVERTICAL );
@@ -539,7 +539,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer151 = new wxBoxSizer( wxHORIZONTAL );
m_bpButtonLoad = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW );
- m_bpButtonLoad->SetToolTip( _("Open...") );
+ m_bpButtonLoad->SetToolTip( _("Open") );
bSizer151->Add( m_bpButtonLoad, 0, wxALIGN_CENTER_VERTICAL, 5 );
@@ -548,6 +548,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer151->Add( m_bpButtonSave, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ m_bpButtonBatchJob = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW );
+ m_bpButtonBatchJob->SetToolTip( _("Save as batch job") );
+
+ bSizer151->Add( m_bpButtonBatchJob, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
@@ -846,11 +851,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
this->Connect( m_menuItemLoad->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ) );
this->Connect( m_menuItemSave->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ) );
this->Connect( m_menuItemSaveAs->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ) );
+ this->Connect( m_menuItem7->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuBatchJob ) );
this->Connect( m_menuItem10->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) );
this->Connect( m_menuItem11->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) );
this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) );
this->Connect( m_menuItemGlobSett->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuGlobalSettings ) );
- this->Connect( m_menuItem7->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuBatchJob ) );
this->Connect( m_menuItem5->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) );
this->Connect( m_menuItemManual->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) );
this->Connect( m_menuItemCheckVer->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) );
@@ -864,6 +869,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_bpButtonSwapSides->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this );
m_bpButtonLoad->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this );
m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this );
+ m_bpButtonBatchJob->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnMenuBatchJob ), 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 );
@@ -893,11 +899,11 @@ MainDialogGenerated::~MainDialogGenerated()
this->Disconnect( wxID_OPEN, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ) );
this->Disconnect( wxID_SAVE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ) );
this->Disconnect( wxID_SAVEAS, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ) );
+ this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuBatchJob ) );
this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) );
this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) );
this->Disconnect( wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) );
this->Disconnect( wxID_PREFERENCES, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuGlobalSettings ) );
- this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuBatchJob ) );
this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) );
this->Disconnect( wxID_HELP, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) );
this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuCheckVersion ) );
@@ -911,6 +917,7 @@ MainDialogGenerated::~MainDialogGenerated()
m_bpButtonSwapSides->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this );
m_bpButtonLoad->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigLoad ), NULL, this );
m_bpButtonSave->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this );
+ m_bpButtonBatchJob->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnMenuBatchJob ), 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 );
@@ -1184,31 +1191,31 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer87;
bSizer87 = new wxBoxSizer( wxHORIZONTAL );
- m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxTAB_TRAVERSAL );
+ m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel8->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) );
wxBoxSizer* bSizer72;
bSizer72 = new wxBoxSizer( wxHORIZONTAL );
- m_bitmap27 = new wxStaticBitmap( m_panel8, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,40 ), 0 );
+ m_bitmap27 = new wxStaticBitmap( m_panel8, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 );
bSizer72->Add( m_bitmap27, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
m_staticText56 = new wxStaticText( m_panel8, wxID_ANY, _("Batch job"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText56->Wrap( -1 );
- m_staticText56->SetFont( wxFont( 14, 70, 90, 92, false, wxEmptyString ) );
+ m_staticText56->SetFont( wxFont( 12, 70, 90, 92, false, wxEmptyString ) );
m_staticText56->SetForegroundColour( wxColour( 0, 0, 0 ) );
bSizer72->Add( m_staticText56, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ m_staticText44 = new wxStaticText( m_panel8, wxID_ANY, _("Create a batch file to automate synchronization. Double-click this file or schedule in your system's task planner: FreeFileSync.exe <job name>.ffs_batch"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText44->Wrap( 480 );
+ bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
m_panel8->SetSizer( bSizer72 );
m_panel8->Layout();
bSizer72->Fit( m_panel8 );
- bSizer87->Add( m_panel8, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
-
- m_staticText44 = new wxStaticText( this, wxID_ANY, _("Create a batch file to automate synchronization. Double-click this file or schedule in your system's task planner: FreeFileSync.exe <job name>.ffs_batch"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText44->Wrap( 480 );
- bSizer87->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+ bSizer87->Add( m_panel8, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_bpButtonHelp = new wxBitmapButton( this, wxID_HELP, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW );
m_bpButtonHelp->SetToolTip( _("Help") );
@@ -1216,7 +1223,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer87->Add( m_bpButtonHelp, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer54->Add( bSizer87, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 );
+ bSizer54->Add( bSizer87, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 );
m_notebook1 = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
m_panelOverview = new wxPanel( m_notebook1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
@@ -1305,7 +1312,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer120->Add( 0, 5, 0, 0, 5 );
m_scrolledWindow6 = new wxScrolledWindow( m_panelOverview, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
- m_scrolledWindow6->SetScrollRate( 5, 5 );
+ m_scrolledWindow6->SetScrollRate( 10, 10 );
wxBoxSizer* bSizer141;
bSizer141 = new wxBoxSizer( wxVERTICAL );
@@ -1533,7 +1540,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer117->Fit( m_panelBatchSettings );
m_notebook1->AddPage( m_panelBatchSettings, _("Batch settings"), false );
- bSizer54->Add( m_notebook1, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
+ bSizer54->Add( m_notebook1, 1, wxEXPAND|wxRIGHT|wxLEFT, 5 );
wxBoxSizer* bSizer68;
bSizer68 = new wxBoxSizer( wxHORIZONTAL );
@@ -2505,6 +2512,12 @@ LogControlGenerated::LogControlGenerated( wxWindow* parent, wxWindowID id, const
{
this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+ wxBoxSizer* bSizer179;
+ bSizer179 = new wxBoxSizer( wxVERTICAL );
+
+ m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer179->Add( m_staticline12, 0, wxEXPAND, 5 );
+
wxBoxSizer* bSizer153;
bSizer153 = new wxBoxSizer( wxHORIZONTAL );
@@ -2526,14 +2539,17 @@ LogControlGenerated::LogControlGenerated( wxWindow* parent, wxWindowID id, const
m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
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 );
+ m_gridMessages = new zen::Grid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
+ m_gridMessages->SetScrollRate( 5, 5 );
+ bSizer153->Add( m_gridMessages, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 );
+
+ bSizer179->Add( bSizer153, 1, wxEXPAND, 5 );
- this->SetSizer( bSizer153 );
+
+ this->SetSizer( bSizer179 );
this->Layout();
- bSizer153->Fit( this );
+ bSizer179->Fit( this );
// Connect Events
m_bpButtonErrors->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( LogControlGenerated::OnErrors ), NULL, this );
@@ -2580,7 +2596,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_build = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_build->Wrap( -1 );
- bSizer53->Add( m_build, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+ bSizer53->Add( m_build, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
bSizer53->Add( 0, 5, 0, 0, 5 );
@@ -2730,7 +2746,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer53->Add( m_panel40, 0, wxEXPAND|wxBOTTOM|wxALIGN_CENTER_HORIZONTAL, 5 );
m_scrolledWindowTranslators = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxDOUBLE_BORDER|wxHSCROLL|wxVSCROLL );
- m_scrolledWindowTranslators->SetScrollRate( 5, 5 );
+ m_scrolledWindowTranslators->SetScrollRate( 10, 10 );
m_scrolledWindowTranslators->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
m_scrolledWindowTranslators->SetMinSize( wxSize( -1,180 ) );
@@ -3076,45 +3092,39 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer70 = new wxBoxSizer( wxHORIZONTAL );
bSizer70->SetMinSize( wxSize( 550,-1 ) );
-
- bSizer70->Add( 0, 0, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 );
-
- m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxTAB_TRAVERSAL );
+ m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel8->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) );
wxBoxSizer* bSizer72;
bSizer72 = new wxBoxSizer( wxHORIZONTAL );
- m_bitmap26 = new wxStaticBitmap( m_panel8, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,40 ), 0 );
+ m_bitmap26 = new wxStaticBitmap( m_panel8, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 );
bSizer72->Add( m_bitmap26, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
m_staticTexHeader = new wxStaticText( m_panel8, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTexHeader->Wrap( -1 );
- m_staticTexHeader->SetFont( wxFont( 14, 70, 90, 92, false, wxEmptyString ) );
+ m_staticTexHeader->SetFont( wxFont( 12, 70, 90, 92, false, wxEmptyString ) );
m_staticTexHeader->SetForegroundColour( wxColour( 0, 0, 0 ) );
bSizer72->Add( m_staticTexHeader, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ m_staticText44 = new wxStaticText( m_panel8, wxID_ANY, _("Only files that match all filter settings will be synchronized.\nNote: File names must be relative to base directories!"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_staticText44->Wrap( 480 );
+ bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
m_panel8->SetSizer( bSizer72 );
m_panel8->Layout();
bSizer72->Fit( m_panel8 );
- bSizer70->Add( m_panel8, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
-
- m_staticText44 = new wxStaticText( this, wxID_ANY, _("Only files that match all filter settings will be synchronized.\nNote: File names must be relative to base directories!"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
- m_staticText44->Wrap( 480 );
- bSizer70->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+ bSizer70->Add( m_panel8, 1, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_bpButtonHelp = new wxBitmapButton( this, wxID_HELP, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW );
m_bpButtonHelp->SetToolTip( _("Help") );
- bSizer70->Add( m_bpButtonHelp, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
-
+ bSizer70->Add( m_bpButtonHelp, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer70->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
-
- bSizer21->Add( bSizer70, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT|wxEXPAND, 5 );
+ bSizer21->Add( bSizer70, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 );
wxBoxSizer* bSizer159;
bSizer159 = new wxBoxSizer( wxHORIZONTAL );
@@ -3253,7 +3263,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer159->Add( bSizer160, 0, wxEXPAND, 5 );
- bSizer21->Add( bSizer159, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
+ bSizer21->Add( bSizer159, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 5 );
wxBoxSizer* bSizer22;
bSizer22 = new wxBoxSizer( wxHORIZONTAL );
@@ -3323,33 +3333,27 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind
wxBoxSizer* bSizer95;
bSizer95 = new wxBoxSizer( wxVERTICAL );
- wxBoxSizer* bSizer86;
- bSizer86 = new wxBoxSizer( wxHORIZONTAL );
-
- m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxTAB_TRAVERSAL );
+ m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel8->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) );
wxBoxSizer* bSizer72;
bSizer72 = new wxBoxSizer( wxHORIZONTAL );
- m_bitmapSettings = new wxStaticBitmap( m_panel8, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,40 ), 0 );
+ m_bitmapSettings = new wxStaticBitmap( m_panel8, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 );
bSizer72->Add( m_bitmapSettings, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
m_staticText56 = new wxStaticText( m_panel8, wxID_ANY, _("Global settings"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText56->Wrap( -1 );
- m_staticText56->SetFont( wxFont( 14, 70, 90, 92, false, wxEmptyString ) );
+ m_staticText56->SetFont( wxFont( 12, 70, 90, 92, false, wxEmptyString ) );
m_staticText56->SetForegroundColour( wxColour( 0, 0, 0 ) );
- bSizer72->Add( m_staticText56, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ bSizer72->Add( m_staticText56, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
m_panel8->SetSizer( bSizer72 );
m_panel8->Layout();
bSizer72->Fit( m_panel8 );
- bSizer86->Add( m_panel8, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
-
-
- bSizer95->Add( bSizer86, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 );
+ bSizer95->Add( m_panel8, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 );
wxStaticBoxSizer* sbSizer23;
sbSizer23 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxVERTICAL );
diff --git a/ui/gui_generated.h b/ui/gui_generated.h
index 14e30f75..b14e0034 100644
--- a/ui/gui_generated.h
+++ b/ui/gui_generated.h
@@ -76,12 +76,12 @@ class MainDialogGenerated : public wxFrame
wxMenuItem* m_menuItemLoad;
wxMenuItem* m_menuItemSave;
wxMenuItem* m_menuItemSaveAs;
+ wxMenuItem* m_menuItem7;
wxMenuItem* m_menuItem10;
wxMenuItem* m_menuItem11;
wxMenu* m_menuAdvanced;
wxMenu* m_menuLanguages;
wxMenuItem* m_menuItemGlobSett;
- wxMenuItem* m_menuItem7;
wxMenu* m_menuHelp;
wxMenuItem* m_menuItemManual;
wxMenuItem* m_menuItemCheckVer;
@@ -134,6 +134,7 @@ class MainDialogGenerated : public wxFrame
wxBoxSizer* bSizerConfig;
wxBitmapButton* m_bpButtonLoad;
wxBitmapButton* m_bpButtonSave;
+ wxBitmapButton* m_bpButtonBatchJob;
wxListBox* m_listBoxHistory;
wxPanel* m_panelFilter;
wxBitmapButton* m_bpButtonFilter;
@@ -177,11 +178,11 @@ class MainDialogGenerated : public wxFrame
virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); }
virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); }
virtual void OnConfigSaveAs( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnMenuBatchJob( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCompare( wxCommandEvent& event ) { event.Skip(); }
virtual void OnStartSync( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMenuGlobalSettings( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnMenuBatchJob( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMenuExportFileList( wxCommandEvent& event ) { event.Skip(); }
virtual void OnShowHelp( wxCommandEvent& event ) { event.Skip(); }
virtual void OnMenuCheckVersion( wxCommandEvent& event ) { event.Skip(); }
@@ -366,7 +367,7 @@ class BatchDlgGenerated : public wxDialog
wxBitmapButton* m_bpButtonAltSyncCfg;
FolderHistoryBox* m_comboBoxLogfileDir;
- BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Create a batch job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|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 = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER );
~BatchDlgGenerated();
};
@@ -593,11 +594,12 @@ class LogControlGenerated : public wxPanel
private:
protected:
+ wxStaticLine* m_staticline12;
ToggleButton* m_bpButtonErrors;
ToggleButton* m_bpButtonWarnings;
ToggleButton* m_bpButtonInfo;
wxStaticLine* m_staticline13;
- wxTextCtrl* m_textCtrlInfo;
+ zen::Grid* m_gridMessages;
// Virtual event handlers, overide them in your derived class
virtual void OnErrors( wxCommandEvent& event ) { event.Skip(); }
diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp
index 120eab39..c8311811 100644
--- a/ui/gui_status_handler.cpp
+++ b/ui/gui_status_handler.cpp
@@ -175,16 +175,18 @@ void CompareStatusHandler::abortThisProcess()
requestAbortion(); //just make sure...
throw GuiAbortProcess();
}
-//########################################################################################################
+//########################################################################################################
SyncStatusHandler::SyncStatusHandler(MainDialog* parentDlg,
+ size_t lastSyncsLogFileSizeMax,
OnGuiError handleError,
const std::wstring& jobName,
const std::wstring& execWhenFinished,
std::vector<std::wstring>& execFinishedHistory) :
parentDlg_(parentDlg),
syncStatusFrame(*this, *this, parentDlg, true, jobName, execWhenFinished, execFinishedHistory),
+ lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax),
handleError_(handleError),
jobName_(jobName)
{
@@ -225,12 +227,17 @@ SyncStatusHandler::~SyncStatusHandler()
errorLog.logMsg(finalStatus, TYPE_INFO);
}
- const Utf8String logStream = generateLogStream(errorLog, jobName_, finalStatus,
- getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING),
- getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING), totalTime.Time() / 1000);
+ const SummaryInfo summary =
+ {
+ jobName_, finalStatus,
+ getObjectsCurrent(PHASE_SYNCHRONIZING), getDataCurrent(PHASE_SYNCHRONIZING),
+ getObjectsTotal (PHASE_SYNCHRONIZING), getDataTotal (PHASE_SYNCHRONIZING),
+ totalTime.Time() / 1000
+ };
+
try
{
- saveToLastSyncsLog(logStream); //throw FileError
+ saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError
}
catch (FileError&) {}
diff --git a/ui/gui_status_handler.h b/ui/gui_status_handler.h
index e1940a2d..fb0dbf51 100644
--- a/ui/gui_status_handler.h
+++ b/ui/gui_status_handler.h
@@ -49,6 +49,7 @@ class SyncStatusHandler : public zen::StatusHandler
{
public:
SyncStatusHandler(MainDialog* parentDlg,
+ size_t lastSyncsLogFileSizeMax,
xmlAccess::OnGuiError handleError,
const std::wstring& jobName,
const std::wstring& execWhenFinished,
@@ -69,6 +70,7 @@ private:
MainDialog* parentDlg_;
SyncStatus syncStatusFrame; //the window managed by SyncStatus has longer lifetime than this handler!
+ const size_t lastSyncsLogFileSizeMax_;
xmlAccess::OnGuiError handleError_;
zen::ErrorLog errorLog;
const std::wstring jobName_;
diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp
index 0004d2fc..80c5e18f 100644
--- a/ui/main_dlg.cpp
+++ b/ui/main_dlg.cpp
@@ -594,6 +594,8 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
m_bpButtonSyncConfig->SetBitmapLabel(GlobalResources::getImage(L"syncConfig"));
m_bpButtonCmpConfig ->SetBitmapLabel(GlobalResources::getImage(L"cmpConfig"));
m_bpButtonLoad ->SetBitmapLabel(GlobalResources::getImage(L"load"));
+ m_bpButtonBatchJob ->SetBitmapLabel(GlobalResources::getImage(L"batch"));
+
m_bpButtonAddPair ->SetBitmapLabel(GlobalResources::getImage(L"item_add"));
{
IconBuffer tmp(IconBuffer::SIZE_SMALL);
@@ -955,43 +957,50 @@ typedef Zbase<wchar_t> zxString; //guaranteed exponential growth
void MainDialog::copySelectionToClipboard()
{
- zxString clipboardString;
-
- auto addSelection = [&](const Grid& grid)
+ try
{
- if (auto prov = grid.getDataProvider())
+ zxString clipboardString;
+
+ auto addSelection = [&](const Grid& grid)
{
- std::vector<Grid::ColumnAttribute> colAttr = grid.getColumnConfig();
- vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
- if (!colAttr.empty())
+ if (auto prov = grid.getDataProvider())
{
- const std::vector<size_t> selection = grid.getSelectedRows();
- std::for_each(selection.begin(), selection.end(),
- [&](size_t row)
+ std::vector<Grid::ColumnAttribute> colAttr = grid.getColumnConfig();
+ vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
+ if (!colAttr.empty())
{
- std::for_each(colAttr.begin(), colAttr.end() - 1,
- [&](const Grid::ColumnAttribute& ca)
+ const std::vector<size_t> selection = grid.getSelectedRows();
+ std::for_each(selection.begin(), selection.end(),
+ [&](size_t row)
{
- clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type_));
- clipboardString += L'\t';
+ std::for_each(colAttr.begin(), colAttr.end() - 1,
+ [&](const Grid::ColumnAttribute& ca)
+ {
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type_));
+ clipboardString += L'\t';
+ });
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type_));
+ clipboardString += L'\n';
});
- clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type_));
- clipboardString += L'\n';
- });
+ }
}
- }
- };
+ };
- addSelection(*m_gridMainL);
- addSelection(*m_gridMainR);
+ addSelection(*m_gridMainL);
+ addSelection(*m_gridMainR);
- //finally write to clipboard
- if (!clipboardString.empty())
- if (wxTheClipboard->Open())
- {
- wxTheClipboard->SetData(new wxTextDataObject(copyStringTo<wxString>(clipboardString))); //ownership passed
- wxTheClipboard->Close();
- }
+ //finally write to clipboard
+ if (!clipboardString.empty())
+ if (wxClipboard::Get()->Open())
+ {
+ ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close());
+ wxClipboard::Get()->SetData(new wxTextDataObject(copyStringTo<wxString>(clipboardString))); //ownership passed
+ }
+ }
+ catch (const std::bad_alloc& e)
+ {
+ wxMessageBox(_("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what()), _("Error"), wxOK | wxICON_ERROR);
+ }
}
@@ -1625,7 +1634,7 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide)
case WXK_SPACE:
case WXK_NUMPAD_SPACE:
{
- const std::vector<FileSystemObject*>& selection = getGridSelection();
+ const std::vector<FileSystemObject*>& selection = getGridSelection();
if (!selection.empty())
setFilterManually(selection, !selection[0]->isActive());
}
@@ -1730,7 +1739,7 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
{
m_gridMainL->SetFocus();
- event.SetEventType(wxEVT_KEY_DOWN); //the grid event handler doesn't expect wxEVT_CHAR_HOOK!
+ 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;
@@ -1746,24 +1755,25 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
{
//scroll m_gridMain to user's new selection on m_gridNavi
- int leadRow = -1;
- if (std::unique_ptr<TreeView::Node> node = treeDataView->getLine(event.rowFrom_))
- {
- if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
- leadRow = gridDataView->findRowFirstChild(&(root->baseMap_));
- else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
+ ptrdiff_t leadRow = -1;
+ if (event.rowFirst_ != event.rowLast_)
+ if (std::unique_ptr<TreeView::Node> node = treeDataView->getLine(event.rowFirst_))
{
- leadRow = gridDataView->findRowDirect(&(dir->dirObj_));
- if (leadRow < 0) //directory was filtered out! still on tree view (but NOT on grid view)
- leadRow = gridDataView->findRowFirstChild(&(dir->dirObj_));
+ if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
+ leadRow = gridDataView->findRowFirstChild(&(root->baseMap_));
+ else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
+ {
+ leadRow = gridDataView->findRowDirect(&(dir->dirObj_));
+ if (leadRow < 0) //directory was filtered out! still on tree view (but NOT on grid view)
+ leadRow = gridDataView->findRowFirstChild(&(dir->dirObj_));
+ }
+ else if (const TreeView::FilesNode* files = dynamic_cast<const TreeView::FilesNode*>(node.get()))
+ leadRow = gridDataView->findRowDirect(files->firstFile_.getId());
}
- else if (const TreeView::FilesNode* files = dynamic_cast<const TreeView::FilesNode*>(node.get()))
- leadRow = gridDataView->findRowDirect(files->firstFile_.getId());
- }
if (leadRow >= 0)
{
- leadRow =std::max(0, leadRow - 1); //scroll one more row
+ leadRow = std::max<ptrdiff_t>(0, leadRow - 1); //scroll one more row
m_gridMainL->scrollTo(leadRow);
m_gridMainC->scrollTo(leadRow);
@@ -1857,7 +1867,7 @@ void MainDialog::onNaviGridContext(GridClickEvent& event)
//----------------------------------------------------------------------------------------------------
//CONTEXT_DELETE_FILES
menu.addSeparator();
- menu.addItem(_("Delete") + L"\tDel", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty());
+ menu.addItem(_("Delete") + L"\tDel", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty(), wxID_DELETE);
menu.popup(*this);
}
@@ -2454,7 +2464,7 @@ bool MainDialog::trySaveConfig(const wxString* fileName) //return true if saved
try
{
- xmlAccess::writeConfig(guiCfg, toZ(targetFilename)); //write config to XML
+ xmlAccess::writeConfig(guiCfg, toZ(targetFilename)); //throw FfsXmlError
setLastUsedConfig(targetFilename, guiCfg);
flashStatusInformation(_("Configuration saved!"));
@@ -2686,16 +2696,14 @@ void MainDialog::OnClose(wxCloseEvent& event)
void MainDialog::onCheckRows(CheckRowsEvent& event)
{
- const int rowFirst = std::min(event.rowFrom_, event.rowTo_); // [rowFirst, rowLast)
- int rowLast = std::max(event.rowFrom_, event.rowTo_) + 1; //
- rowLast = std::min(rowLast, static_cast<int>(gridDataView->rowsOnView())); //consider dummy rows
+ std::set<size_t> selectedRows;
- if (0 <= rowFirst && rowFirst < rowLast)
- {
- std::set<size_t> selectedRows;
- for (int i = rowFirst; i < rowLast; ++i)
- selectedRows.insert(i);
+ const size_t rowLast = std::min(event.rowLast_, gridDataView->rowsOnView()); //consider dummy rows
+ for (size_t i = event.rowFirst_; i < rowLast; ++i)
+ selectedRows.insert(i);
+ if (!selectedRows.empty())
+ {
std::vector<FileSystemObject*> objects = gridDataView->getAllFileRef(selectedRows);
setFilterManually(objects, event.setIncluded_);
}
@@ -2704,16 +2712,14 @@ void MainDialog::onCheckRows(CheckRowsEvent& event)
void MainDialog::onSetSyncDirection(SyncDirectionEvent& event)
{
- const int rowFirst = std::min(event.rowFrom_, event.rowTo_); // [rowFirst, rowLast)
- int rowLast = std::max(event.rowFrom_, event.rowTo_) + 1; //
- rowLast = std::min(rowLast, static_cast<int>(gridDataView->rowsOnView())); //consider dummy rows
+ std::set<size_t> selectedRows;
- if (0 <= rowFirst && rowFirst < rowLast)
- {
- std::set<size_t> selectedRows;
- for (int i = rowFirst; i < rowLast; ++i)
- selectedRows.insert(i);
+ const size_t rowLast = std::min(event.rowLast_, gridDataView->rowsOnView()); //consider dummy rows
+ for (size_t i = event.rowFirst_; i < rowLast; ++i)
+ selectedRows.insert(i);
+ if (!selectedRows.empty())
+ {
std::vector<FileSystemObject*> objects = gridDataView->getAllFileRef(selectedRows);
setSyncDirManually(objects, event.direction_);
}
@@ -3172,7 +3178,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
wxWindow* oldFocus = wxWindow::FindFocus();
ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();) //e.g. keep focus on main grid after pressing F5
- int scrollPosX = 0;
+ int scrollPosX = 0;
int scrollPosY = 0;
m_gridMainL->GetViewStart(&scrollPosX, &scrollPosY); //preserve current scroll position
ZEN_ON_SCOPE_EXIT(
@@ -3193,12 +3199,14 @@ void MainDialog::OnCompare(wxCommandEvent& event)
std::unique_ptr<LockHolder> dummy2;
if (globalCfg.createLockFile)
{
- dummy2.reset(new LockHolder(true)); //allow pw prompt
- for (auto iter = cmpConfig.begin(); iter != cmpConfig.end(); ++iter)
+ std::vector<Zstring> dirnames;
+ std::for_each(cmpConfig.begin(), cmpConfig.end(),
+ [&](const FolderPairCfg& fpCfg)
{
- dummy2->addDir(iter->leftDirectoryFmt, statusHandler);
- dummy2->addDir(iter->rightDirectoryFmt, statusHandler);
- }
+ dirnames.push_back(fpCfg.leftDirectoryFmt);
+ dirnames.push_back(fpCfg.rightDirectoryFmt);
+ });
+ dummy2 = make_unique<LockHolder>(dirnames, statusHandler, true); //allow pw prompt
}
//COMPARE DIRECTORIES
@@ -3234,7 +3242,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,7 +3386,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler())
evtHandler->ProcessEvent(dummy2); //synchronous call
- if (folderCmp.empty()) //check if user aborted or error occured, ect...
+ if (folderCmp.empty()) //check if user aborted or error occurred, ect...
return;
}
@@ -3410,6 +3418,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
//class handling status updates and error messages
SyncStatusHandler statusHandler(this, //throw GuiAbortProcess
+ globalCfg.lastSyncsLogFileSizeMax,
currentCfg.handleError,
xmlAccess::extractJobName(activeFileName),
guiCfg.mainCfg.onCompletion,
@@ -3419,12 +3428,13 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
std::unique_ptr<LockHolder> dummy2;
if (globalCfg.createLockFile)
{
- dummy2.reset(new LockHolder(true)); //allow pw prompt
+ std::vector<Zstring> dirnames;
for (auto iter = begin(folderCmp); iter != end(folderCmp); ++iter)
{
- dummy2->addDir(iter->getBaseDirPf<LEFT_SIDE >(), statusHandler);
- dummy2->addDir(iter->getBaseDirPf<RIGHT_SIDE>(), statusHandler);
+ dirnames.push_back(iter->getBaseDirPf<LEFT_SIDE >());
+ dirnames.push_back(iter->getBaseDirPf<RIGHT_SIDE>());
}
+ dummy2 = make_unique<LockHolder>(dirnames, statusHandler, true); //allow pw prompt
}
//START SYNCHRONIZATION
@@ -3465,7 +3475,7 @@ void MainDialog::onGridDoubleClickR(GridClickEvent& event)
onGridDoubleClickRim(event.row_, false);
}
-void MainDialog::onGridDoubleClickRim(int row, bool leftSide)
+void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide)
{
if (!globalCfg.gui.externelApplications.empty())
openExternalApplication(globalCfg.gui.externelApplications[0].second,
@@ -3966,8 +3976,8 @@ void MainDialog::clearAddFolderPairs()
//m_scrolledWindowFolderPairs->SetMinSize(wxSize(-1, 0));
//bSizer1->Layout();
}
-//########################################################################################################
+//########################################################################################################
//menu events
void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event)
@@ -3992,44 +4002,44 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
const Zstring filename = utfCvrtTo<Zstring>(filePicker.GetPath());
- Utf8String buffer; //perf: wxString doesn't model exponential growth and so is out, std::string doesn't give performance guarantee!
- buffer += BYTE_ORDER_MARK_UTF8;
+ Utf8String header; //perf: wxString doesn't model exponential growth and so is out, std::string doesn't give performance guarantee!
+ header += BYTE_ORDER_MARK_UTF8;
//write legend
- buffer += utfCvrtTo<Utf8String>(_("Legend")) + '\n';
+ header += utfCvrtTo<Utf8String>(_("Legend")) + '\n';
if (showSyncAction_)
{
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_EQUAL)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_EQUAL)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_CREATE_NEW_LEFT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_CREATE_NEW_LEFT)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_CREATE_NEW_RIGHT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_CREATE_NEW_RIGHT)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_OVERWRITE_LEFT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_OVERWRITE_LEFT)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_OVERWRITE_RIGHT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_OVERWRITE_RIGHT)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_DELETE_LEFT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_DELETE_LEFT)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_DELETE_RIGHT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_DELETE_RIGHT)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_DO_NOTHING)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_DO_NOTHING)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_UNRESOLVED_CONFLICT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_UNRESOLVED_CONFLICT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_EQUAL)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_EQUAL)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_CREATE_NEW_LEFT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_CREATE_NEW_LEFT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_CREATE_NEW_RIGHT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_CREATE_NEW_RIGHT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_OVERWRITE_LEFT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_OVERWRITE_LEFT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_OVERWRITE_RIGHT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_OVERWRITE_RIGHT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_DELETE_LEFT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_DELETE_LEFT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_DELETE_RIGHT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_DELETE_RIGHT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_DO_NOTHING)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_DO_NOTHING)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getSyncOpDescription(SO_UNRESOLVED_CONFLICT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(SO_UNRESOLVED_CONFLICT)) + '\n';
}
else
{
- buffer += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_EQUAL)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_EQUAL)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_DIFFERENT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_DIFFERENT)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_LEFT_SIDE_ONLY)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_LEFT_SIDE_ONLY)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_RIGHT_SIDE_ONLY)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_RIGHT_SIDE_ONLY)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_LEFT_NEWER)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_LEFT_NEWER)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_RIGHT_NEWER)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_RIGHT_NEWER)) + '\n';
- buffer += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_CONFLICT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_CONFLICT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_EQUAL)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_EQUAL)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_DIFFERENT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_DIFFERENT)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_LEFT_SIDE_ONLY)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_LEFT_SIDE_ONLY)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_RIGHT_SIDE_ONLY)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_RIGHT_SIDE_ONLY)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_LEFT_NEWER)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_LEFT_NEWER)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_RIGHT_NEWER)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_RIGHT_NEWER)) + '\n';
+ header += "\"" + utfCvrtTo<Utf8String>(getCategoryDescription(FILE_CONFLICT)) + "\";" + utfCvrtTo<Utf8String>(getSymbol(FILE_CONFLICT)) + '\n';
}
- buffer += '\n';
+ header += '\n';
//base folders
- buffer += utfCvrtTo<Utf8String>(_("Folder pairs")) + '\n' ;
+ header += utfCvrtTo<Utf8String>(_("Folder pairs")) + '\n' ;
std::for_each(begin(folderCmp), end(folderCmp),
[&](BaseDirMapping& baseMap)
{
- buffer += utfCvrtTo<Utf8String>(baseMap.getBaseDirPf<LEFT_SIDE >()) + ';';
- buffer += utfCvrtTo<Utf8String>(baseMap.getBaseDirPf<RIGHT_SIDE>()) + '\n';
+ header += utfCvrtTo<Utf8String>(baseMap.getBaseDirPf<LEFT_SIDE >()) + ';';
+ header += utfCvrtTo<Utf8String>(baseMap.getBaseDirPf<RIGHT_SIDE>()) + '\n';
});
- buffer += '\n';
+ header += '\n';
//write header
auto provLeft = m_gridMainL->getDataProvider();
@@ -4044,12 +4054,12 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
vector_remove_if(colAttrMiddle, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
vector_remove_if(colAttrRight , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
- auto addCellValue = [&](const wxString& val)
+ auto fmtCellValue = [](const wxString& val) -> Utf8String
{
if (val.find(L';') != wxString::npos)
- buffer += '\"' + utfCvrtTo<Utf8String>(val) + '\"';
+ return '\"' + utfCvrtTo<Utf8String>(val) + '\"';
else
- buffer += utfCvrtTo<Utf8String>(val);
+ return utfCvrtTo<Utf8String>(val);
};
if (provLeft && provMiddle && provRight)
@@ -4057,62 +4067,73 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
std::for_each(colAttrLeft.begin(), colAttrLeft.end(),
[&](const Grid::ColumnAttribute& ca)
{
- addCellValue(provLeft->getColumnLabel(ca.type_));
- buffer += ';';
+ header += fmtCellValue(provLeft->getColumnLabel(ca.type_));
+ header += ';';
});
std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(),
[&](const Grid::ColumnAttribute& ca)
{
- addCellValue(provMiddle->getColumnLabel(ca.type_));
- buffer += ';';
+ header += fmtCellValue(provMiddle->getColumnLabel(ca.type_));
+ header += ';';
});
if (!colAttrRight.empty())
{
std::for_each(colAttrRight.begin(), colAttrRight.end() - 1,
[&](const Grid::ColumnAttribute& ca)
{
- addCellValue(provRight->getColumnLabel(ca.type_));
- buffer += ';';
+ header += fmtCellValue(provRight->getColumnLabel(ca.type_));
+ header += ';';
});
- addCellValue(provRight->getColumnLabel(colAttrRight.back().type_));
+ header += fmtCellValue(provRight->getColumnLabel(colAttrRight.back().type_));
}
- buffer += '\n';
+ header += '\n';
- //main grid
- const size_t rowCount = m_gridMainL->getRowCount();
- for (size_t row = 0; row < rowCount; ++row)
+ try
{
- std::for_each(colAttrLeft.begin(), colAttrLeft.end(),
- [&](const Grid::ColumnAttribute& ca)
- {
- addCellValue(provLeft->getValue(row, ca.type_));
- buffer += ';';
- });
- std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(),
- [&](const Grid::ColumnAttribute& ca)
- {
- addCellValue(provMiddle->getValue(row, ca.type_));
- buffer += ';';
- });
- if (!colAttrRight.empty())
+ //write file
+ FileOutput fileOut(filename, zen::FileOutput::ACC_OVERWRITE); //throw FileError
+
+ replace(header, '\n', LINE_BREAK);
+ fileOut.write(&*header.begin(), header.size()); //throw FileError
+
+ //main grid: write rows one after the other instead of creating one big string: memory allocation might fail; think 1 million rows!
+ /*
+ performance test case "export 600.000 rows" to CSV:
+ aproach 1. assemble single temporary string, then write file: 4.6s
+ aproach 2. write to buffered file output directly for each row: 6.4s
+ */
+ const size_t rowCount = m_gridMainL->getRowCount();
+ for (size_t row = 0; row < rowCount; ++row)
{
- std::for_each(colAttrRight.begin(), colAttrRight.end() - 1,
+ Utf8String tmp;
+
+ std::for_each(colAttrLeft.begin(), colAttrLeft.end(),
[&](const Grid::ColumnAttribute& ca)
{
- addCellValue(provRight->getValue(row, ca.type_));
- buffer += ';';
+ tmp += fmtCellValue(provLeft->getValue(row, ca.type_));
+ tmp += ';';
});
- addCellValue(provRight->getValue(row, colAttrRight.back().type_));
- }
- buffer += '\n';
- }
-
- //write file
- try
- {
- replace(buffer, '\n', LINE_BREAK);
+ std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(),
+ [&](const Grid::ColumnAttribute& ca)
+ {
+ tmp += fmtCellValue(provMiddle->getValue(row, ca.type_));
+ tmp += ';';
+ });
+ if (!colAttrRight.empty())
+ {
+ std::for_each(colAttrRight.begin(), colAttrRight.end() - 1,
+ [&](const Grid::ColumnAttribute& ca)
+ {
+ tmp += fmtCellValue(provRight->getValue(row, ca.type_));
+ tmp += ';';
+ });
+ tmp += fmtCellValue(provRight->getValue(row, colAttrRight.back().type_));
+ }
+ tmp += '\n';
- saveBinStream(filename, buffer); //throw FileError
+ replace(tmp, '\n', LINE_BREAK);
+ fileOut.write(&*tmp.begin(), tmp.size()); //throw FileError
+ }
flashStatusInformation(_("File list exported!"));
}
diff --git a/ui/main_dlg.h b/ui/main_dlg.h
index f5f07624..07687f88 100644
--- a/ui/main_dlg.h
+++ b/ui/main_dlg.h
@@ -163,7 +163,7 @@ private:
void onGridDoubleClickL(zen::GridClickEvent& event);
void onGridDoubleClickR(zen::GridClickEvent& event);
- void onGridDoubleClickRim(int row, bool leftSide);
+ void onGridDoubleClickRim(size_t row, bool leftSide);
void onGridLabelLeftClickC(zen::GridClickEvent& event);
void onGridLabelLeftClickL(zen::GridClickEvent& event);
diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp
index ebbc9edd..caf87428 100644
--- a/ui/progress_indicator.cpp
+++ b/ui/progress_indicator.cpp
@@ -10,12 +10,15 @@
#include <wx/stopwatch.h>
#include <wx/wupdlock.h>
#include <wx/sound.h>
+#include <wx/clipbrd.h>
+#include <wx/msgdlg.h>
#include <zen/basic_math.h>
#include <zen/format_unit.h>
#include <wx+/mouse_move_dlg.h>
#include <wx+/toggle_button.h>
#include <wx+/image_tools.h>
#include <wx+/graph.h>
+#include <wx+/context_menu.h>
#include <wx+/no_flicker.h>
#include <zen/file_handling.h>
#include "gui_generated.h"
@@ -272,7 +275,7 @@ namespace
inline
wxBitmap buttonPressed(const std::string& name)
{
- wxBitmap background = GlobalResources::getImage(wxT("log button pressed"));
+ wxBitmap background = GlobalResources::getImage(L"log button pressed");
return layOver(GlobalResources::getImage(utfCvrtTo<wxString>(name)), background);
}
@@ -287,17 +290,283 @@ wxBitmap buttonReleased(const std::string& name)
zen::move(output, 0, -1); //move image right one pixel
return output;
}
+
+
+//a vector-view on ErrorLog considering multi-line messages: prepare consumption by Grid
+class MessageView
+{
+public:
+ MessageView(const ErrorLog& log) : log_(log) {}
+
+ size_t rowsOnView() const { return viewRef.size(); }
+
+ struct LogEntryView
+ {
+ time_t time;
+ MessageType type;
+ MsgString messageLine;
+ bool firstLine; //if LogEntry::message spans multiple rows
+ };
+ bool getEntry(size_t row, LogEntryView& out) const
+ {
+ if (row < viewRef.size())
+ {
+ const Line& line = viewRef[row];
+ out.time = line.entry_->time;
+ out.type = line.entry_->type;
+ out.messageLine = extractLine(line.entry_->message, line.rowNumber_);
+ out.firstLine = line.rowNumber_ == 0; //this is virtually always correct, unless first line of the original message is empty!
+ return true;
+ }
+ return false;
+ }
+
+ void updateView(int includedTypes) //TYPE_INFO | TYPE_WARNING, ect. see error_log.h
+ {
+ viewRef.clear();
+
+ for (auto iter = log_.begin(); iter != log_.end(); ++iter)
+ if (iter->type & includedTypes)
+ {
+ assert_static((IsSameType<GetCharType<MsgString>::Type, wchar_t>::value));
+ assert(!startsWith(iter->message, L'\n'));
+
+ size_t rowNumber = 0;
+ bool lastCharNewline = true;
+ std::for_each(iter->message.begin(), iter->message.end(),
+ [&](wchar_t c)
+ {
+ typedef Line Line; //workaround MSVC compiler bug!
+
+ if (c == L'\n')
+ {
+ if (!lastCharNewline) //do not reference empty lines!
+ viewRef.push_back(Line(&*iter, rowNumber));
+ ++rowNumber;
+ lastCharNewline = true;
+ }
+ else
+ lastCharNewline = false;
+ });
+ if (!lastCharNewline)
+ viewRef.push_back(Line(&*iter, rowNumber));
+ }
+ }
+
+private:
+ static MsgString extractLine(const MsgString& message, size_t textRow)
+ {
+ auto iter1 = message.begin();
+ for (;;)
+ {
+ auto iter2 = std::find_if(iter1, message.end(), [](wchar_t c) { return c == L'\n'; });
+ if (textRow == 0)
+ return iter1 == message.end() ? MsgString() : MsgString(&*iter1, iter2 - iter1); //must not dereference iterator pointing to "end"!
+
+ if (iter2 == message.end())
+ {
+ assert(false);
+ return MsgString();
+ }
+
+ iter1 = iter2 + 1; //skip newline
+ --textRow;
+ }
+ }
+
+ struct Line
+ {
+ Line(const LogEntry* entry, size_t rowNumber) : entry_(entry), rowNumber_(rowNumber) {}
+ const LogEntry* entry_; //always bound!
+ size_t rowNumber_; //LogEntry::message may span multiple rows
+ };
+
+ std::vector<Line> viewRef; //partial view on log_
+ /* /|\
+ | updateView()
+ | */
+ const ErrorLog log_;
+};
+
+//-----------------------------------------------------------------------------
+
+enum ColumnTypeMsg
+{
+ COL_TYPE_MSG_TIME,
+ COL_TYPE_MSG_CATEGORY,
+ COL_TYPE_MSG_TEXT,
+};
+
+//Grid data implementation referencing MessageView
+class GridDataMessages : public GridData
+{
+ static const int COLUMN_BORDER_LEFT = 4; //for left-aligned text
+
+public:
+ GridDataMessages(const std::shared_ptr<MessageView>& msgView) : msgView_(msgView) {}
+
+ virtual size_t getRowCount() const { return msgView_ ? msgView_->rowsOnView() : 0; }
+
+ virtual wxString getValue(size_t row, ColumnType colType) const
+ {
+ MessageView::LogEntryView entry = {};
+ if (msgView_ && msgView_->getEntry(row, entry))
+ switch (static_cast<ColumnTypeMsg>(colType))
+ {
+ case COL_TYPE_MSG_TIME:
+ if (entry.firstLine)
+ return formatTime<wxString>(FORMAT_TIME, localTime(entry.time));
+ break;
+
+ case COL_TYPE_MSG_CATEGORY:
+ if (entry.firstLine)
+ switch (entry.type)
+ {
+ case TYPE_INFO:
+ return _("Info");
+ case TYPE_WARNING:
+ return _("Warning");
+ case TYPE_ERROR:
+ return _("Error");
+ case TYPE_FATAL_ERROR:
+ return _("Fatal Error");
+ }
+ break;
+
+ case COL_TYPE_MSG_TEXT:
+ return copyStringTo<wxString>(entry.messageLine);
+ }
+ return wxEmptyString;
+ }
+
+ virtual void renderCell(Grid& grid, wxDC& dc, const wxRect& rect, size_t row, ColumnType colType)
+ {
+ wxRect rectTmp = rect;
+
+ const wxColor colorGridLine = wxColour(192, 192, 192); //light grey
+
+ wxDCPenChanger dummy2(dc, wxPen(colorGridLine, 1, wxSOLID));
+ const bool drawBottomLine = [&]() -> bool //don't separate multi-line messages
+ {
+ MessageView::LogEntryView nextEntry = {};
+ if (msgView_ && msgView_->getEntry(row + 1, nextEntry))
+ return nextEntry.firstLine;
+ return true;
+ }();
+
+ if (drawBottomLine)
+ {
+ dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0));
+ --rectTmp.height;
+ }
+
+ //--------------------------------------------------------
+
+ MessageView::LogEntryView entry = {};
+ if (msgView_ && msgView_->getEntry(row, entry))
+ switch (static_cast<ColumnTypeMsg>(colType))
+ {
+ case COL_TYPE_MSG_TIME:
+ drawCellText(dc, rectTmp, getValue(row, colType), grid.IsEnabled(), wxALIGN_CENTER);
+ break;
+
+ case COL_TYPE_MSG_CATEGORY:
+ if (entry.firstLine)
+ switch (entry.type)
+ {
+ case TYPE_INFO:
+ dc.DrawLabel(wxString(), GlobalResources::getImage(L"msg_small_info"), rectTmp, wxALIGN_CENTER);
+ break;
+ case TYPE_WARNING:
+ dc.DrawLabel(wxString(), GlobalResources::getImage(L"msg_small_warning"), rectTmp, wxALIGN_CENTER);
+ break;
+ case TYPE_ERROR:
+ case TYPE_FATAL_ERROR:
+ dc.DrawLabel(wxString(), GlobalResources::getImage(L"msg_small_error"), rectTmp, wxALIGN_CENTER);
+ break;
+ }
+ break;
+
+ case COL_TYPE_MSG_TEXT:
+ {
+ rectTmp.x += COLUMN_BORDER_LEFT;
+ rectTmp.width -= COLUMN_BORDER_LEFT;
+ drawCellText(dc, rectTmp, getValue(row, colType), grid.IsEnabled());
+ }
+ break;
+ }
+ }
+
+ virtual size_t getBestSize(wxDC& dc, size_t row, ColumnType colType)
+ {
+ // -> synchronize renderCell() <-> getBestSize()
+
+ MessageView::LogEntryView entry = {};
+ if (msgView_ && msgView_->getEntry(row, entry))
+ switch (static_cast<ColumnTypeMsg>(colType))
+ {
+ case COL_TYPE_MSG_TIME:
+ return 2 * COLUMN_BORDER_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth();
+
+ case COL_TYPE_MSG_CATEGORY:
+ return GlobalResources::getImage(L"msg_small_info").GetWidth();
+
+ case COL_TYPE_MSG_TEXT:
+ return COLUMN_BORDER_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth();
+ }
+ return 0;
+ }
+
+ static int getColumnTimeDefaultWidth(Grid& grid)
+ {
+ wxClientDC dc(&grid.getMainWin());
+ dc.SetFont(grid.getMainWin().GetFont());
+ return 2 * COLUMN_BORDER_LEFT + dc.GetTextExtent(formatTime<wxString>(FORMAT_TIME)).GetWidth();
+ }
+
+ static int getColumnCategoryDefaultWidth()
+ {
+ return GlobalResources::getImage(L"msg_small_info").GetWidth();
+ }
+
+ static int getRowDefaultHeight(const Grid& grid)
+ {
+ return std::max(GlobalResources::getImage(L"msg_small_info").GetHeight(), grid.getMainWin().GetCharHeight() + 2) + 1; //+ some space + bottom border
+ }
+
+ virtual wxString getToolTip(size_t row, ColumnType colType) const
+ {
+ MessageView::LogEntryView entry = {};
+ if (msgView_ && msgView_->getEntry(row, entry))
+ switch (static_cast<ColumnTypeMsg>(colType))
+ {
+ case COL_TYPE_MSG_TIME:
+ case COL_TYPE_MSG_TEXT:
+ break;
+
+ case COL_TYPE_MSG_CATEGORY:
+ return getValue(row, colType);
+ }
+ return wxEmptyString;
+ }
+
+ virtual wxString getColumnLabel(ColumnType colType) const { return wxEmptyString; }
+
+private:
+ const std::shared_ptr<MessageView> msgView_;
+};
}
class LogControl : public LogControlGenerated
{
public:
- LogControl(wxWindow* parent, const ErrorLog& log) : LogControlGenerated(parent), log_(log)
+ LogControl(wxWindow* parent, const ErrorLog& log) : LogControlGenerated(parent),
+ msgView(std::make_shared<MessageView>(log))
{
- const int errorCount = log_.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR);
- const int warningCount = log_.getItemCount(TYPE_WARNING);
- const int infoCount = log_.getItemCount(TYPE_INFO);
+ const int errorCount = log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR);
+ const int warningCount = log.getItemCount(TYPE_WARNING);
+ const int infoCount = log.getItemCount(TYPE_INFO);
m_bpButtonErrors ->init(buttonPressed ("msg_error" ), buttonReleased("msg_error" ), _("Error" ) + wxString::Format(L" (%d)", errorCount ));
m_bpButtonWarnings->init(buttonPressed ("msg_warning"), buttonReleased("msg_warning"), _("Warning") + wxString::Format(L" (%d)", warningCount));
@@ -311,47 +580,49 @@ public:
m_bpButtonWarnings->Show(warningCount != 0);
m_bpButtonInfo ->Show(infoCount != 0);
- m_textCtrlInfo->SetMaxLength(0); //allow large entries!
+ //init grid, determine default sizes
+ const int rowHeight = GridDataMessages::getRowDefaultHeight(*m_gridMessages);
+ const int colMsgTimeWidth = GridDataMessages::getColumnTimeDefaultWidth(*m_gridMessages);
+ const int colMsgCategoryWidth = GridDataMessages::getColumnCategoryDefaultWidth();
+
+ m_gridMessages->setDataProvider(std::make_shared<GridDataMessages>(msgView));
+ m_gridMessages->setColumnLabelHeight(0);
+ m_gridMessages->showRowLabel(false);
+ m_gridMessages->setRowHeight(rowHeight);
+ std::vector<Grid::ColumnAttribute> attr;
+ attr.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_MSG_TIME ), colMsgTimeWidth, 0));
+ attr.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_MSG_CATEGORY), colMsgCategoryWidth, 0));
+ attr.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_MSG_TEXT ), -colMsgTimeWidth - colMsgCategoryWidth, 1));
+ m_gridMessages->setColumnConfig(attr);
+
+ //support for CTRL + C
+ m_gridMessages->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(LogControl::onGridButtonEvent), nullptr, this);
- updateLogText();
+ m_gridMessages->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(LogControl::onMsgGridContext), nullptr, this);
- m_textCtrlInfo->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(LogControl::onKeyEvent), nullptr, this);
+ updateGrid();
}
private:
virtual void OnErrors(wxCommandEvent& event)
{
m_bpButtonErrors->toggle();
- updateLogText();
+ updateGrid();
}
virtual void OnWarnings(wxCommandEvent& event)
{
m_bpButtonWarnings->toggle();
- updateLogText();
+ updateGrid();
}
virtual void OnInfo(wxCommandEvent& event)
{
m_bpButtonInfo->toggle();
- updateLogText();
+ updateGrid();
}
- void onKeyEvent(wxKeyEvent& event)
- {
- const int keyCode = event.GetKeyCode();
-
- if (event.ControlDown())
- switch (keyCode)
- {
- case 'A': //CTRL + A
- m_textCtrlInfo->SetSelection(-1, -1); //select all
- return;
- }
- event.Skip();
- }
-
- void updateLogText()
+ void updateGrid()
{
int includedTypes = 0;
if (m_bpButtonErrors->isActive())
@@ -363,27 +634,83 @@ private:
if (m_bpButtonInfo->isActive())
includedTypes |= TYPE_INFO;
- //fast replacement for wxString modelling exponential growth
- MsgString logText;
+ msgView->updateView(includedTypes); //update MVC "model"
+ m_gridMessages->Refresh(); //update MVC "view"
+ }
+
- const auto& entries = log_.getEntries();
- for (auto iter = entries.begin(); iter != entries.end(); ++iter)
- if (iter->type & includedTypes)
+ void onGridButtonEvent(wxKeyEvent& event)
+ {
+ int keyCode = event.GetKeyCode();
+
+ if (event.ControlDown())
+ switch (keyCode)
{
- logText += formatMessage(*iter);
- logText += L'\n';
+ case 'C':
+ case WXK_INSERT: //CTRL + C || CTRL + INS
+ copySelectionToClipboard();
+ return; // -> swallow event! don't allow default grid commands!
}
- if (logText.empty()) //if no messages match selected view filter, at least show final status message
- if (!entries.empty())
- logText = formatMessage(entries.back());
+ event.Skip(); //unknown keypress: propagate
+ }
+
+ void onMsgGridContext(GridClickEvent& event)
+ {
+ const std::vector<size_t> selection = m_gridMessages->getSelectedRows();
+
+ ContextMenu menu;
+ menu.addItem(_("Copy") + L"\tCtrl+C", [this] { copySelectionToClipboard(); }, nullptr, !selection.empty(), wxID_COPY);
+ menu.popup(*this);
+ }
+
+ void copySelectionToClipboard()
+ {
+ try
+ {
+ typedef Zbase<wchar_t> zxString; //guaranteed exponential growth, unlike wxString
+ zxString clipboardString;
+
+ if (auto prov = m_gridMessages->getDataProvider())
+ {
+ std::vector<Grid::ColumnAttribute> colAttr = m_gridMessages->getColumnConfig();
+ vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
+ if (!colAttr.empty())
+ {
+ const std::vector<size_t> selection = m_gridMessages->getSelectedRows();
+ std::for_each(selection.begin(), selection.end(),
+ [&](size_t row)
+ {
+#ifdef _MSC_VER
+ typedef zxString zxString; //workaround MSVC compiler bug!
+#endif
+ std::for_each(colAttr.begin(), --colAttr.end(),
+ [&](const Grid::ColumnAttribute& ca)
+ {
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type_));
+ clipboardString += L'\t';
+ });
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type_));
+ clipboardString += L'\n';
+ });
+ }
+ }
- wxWindowUpdateLocker dummy(m_textCtrlInfo);
- m_textCtrlInfo->ChangeValue(copyStringTo<wxString>(logText));
- m_textCtrlInfo->ShowPosition(m_textCtrlInfo->GetLastPosition());
+ //finally write to clipboard
+ if (!clipboardString.empty())
+ if (wxClipboard::Get()->Open())
+ {
+ ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close());
+ wxClipboard::Get()->SetData(new wxTextDataObject(copyStringTo<wxString>(clipboardString))); //ownership passed
+ }
+ }
+ catch (const std::bad_alloc& e)
+ {
+ wxMessageBox(_("Out of memory!") + L" " + utfCvrtTo<std::wstring>(e.what()), _("Error"), wxOK | wxICON_ERROR);
+ }
}
- const ErrorLog log_;
+ std::shared_ptr<MessageView> msgView; //bound!
};
//########################################################################################
@@ -983,12 +1310,12 @@ void SyncStatus::SyncStatusImpl::updateProgress(bool allowYield)
if (paused_)
{
stopTimer();
+ ZEN_ON_SCOPE_EXIT(resumeTimer());
while (paused_)
{
wxMilliSleep(UI_UPDATE_INTERVAL);
updateUiNow(); //receive UI message that ends pause
}
- resumeTimer();
}
/*
/|\
@@ -1242,7 +1569,7 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncResult resultId, const E
m_listbookResult->AddPage(logControl, _("Logging"), false);
//bSizerHoldStretch->Insert(0, logControl, 1, wxEXPAND);
- //show log instead of graph if errors occured! (not required for ignored warnings)
+ //show log instead of graph if errors occurred! (not required for ignored warnings)
if (log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR) > 0)
m_listbookResult->ChangeSelection(posLog);
diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp
index 4b070dce..2b6c9d4a 100644
--- a/ui/small_dlgs.cpp
+++ b/ui/small_dlgs.cpp
@@ -109,14 +109,10 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
bmpLogo = wxBitmap(tmp);
}
{
- wxMemoryDC dc;
- dc.SelectObject(bmpLogo);
-
+ wxMemoryDC dc(bmpLogo);
dc.SetTextForeground(*wxBLACK);
dc.SetFont(wxFont(18, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, L"Tahoma"));
dc.DrawLabel(wxString(L"FreeFileSync ") + zen::currentVersion, wxNullBitmap, wxRect(0, 0, bmpLogo.GetWidth(), bmpLogo.GetHeight()), wxALIGN_CENTER);
-
- dc.SelectObject(wxNullBitmap);
}
m_bitmap11->SetBitmap(bmpLogo);
diff --git a/ui/tree_view.cpp b/ui/tree_view.cpp
index a3a6b2aa..119091a0 100644
--- a/ui/tree_view.cpp
+++ b/ui/tree_view.cpp
@@ -939,7 +939,7 @@ private:
{
const int tolerance = 1;
const int xNodeStatusFirst = -tolerance + cellArea.x + static_cast<int>(node->level_) * widthLevelStep + CELL_BORDER + (showPercentBar ? widthPercentBar + 2 * CELL_BORDER : 0);
- const int xNodeStatusLast = xNodeStatusFirst + widthNodeStatus + 2 * tolerance;
+ const int xNodeStatusLast = (xNodeStatusFirst + tolerance) + widthNodeStatus + tolerance;
// -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft()
if (xNodeStatusFirst <= absX && absX < xNodeStatusLast)
@@ -948,7 +948,7 @@ private:
}
//--------------------------------------------------------------------------------------------------
- if (clickOnNodeStatus && event.row_ >= 0)
+ if (clickOnNodeStatus)
switch (treeDataView_->getStatus(event.row_))
{
case TreeView::STATUS_EXPANDED:
@@ -964,7 +964,7 @@ private:
void onMouseLeftDouble(GridClickEvent& event)
{
- if (event.row_ >= 0 && treeDataView_)
+ if (treeDataView_)
switch (treeDataView_->getStatus(event.row_))
{
case TreeView::STATUS_EXPANDED:
@@ -1102,7 +1102,7 @@ private:
void expandNode(size_t row)
{
treeDataView_->expandNode(row);
- grid_.Refresh(); //this one clears selection (changed row count)
+ grid_.Refresh(); //implicitly clears selection (changed row count after expand)
grid_.setGridCursor(row);
//grid_.autoSizeColumns(); -> doesn't look as good as expected
}
@@ -1110,9 +1110,8 @@ private:
void reduceNode(size_t row)
{
treeDataView_->reduceNode(row);
- grid_.Refresh(); //this one clears selection (changed row count)
+ grid_.Refresh();
grid_.setGridCursor(row);
- //grid_.autoSizeColumns(); -> doesn't look as good as expected
}
std::shared_ptr<TreeView> treeDataView_;
@@ -1134,7 +1133,9 @@ void treeview::init(Grid& grid, const std::shared_ptr<TreeView>& treeDataView)
{
grid.setDataProvider(std::make_shared<GridDataNavi>(grid, treeDataView));
grid.showRowLabel(false);
- grid.setRowHeight(IconBuffer(IconBuffer::SIZE_SMALL).getSize() + 2); //add some space
+
+ const int rowHeight = std::max(IconBuffer(IconBuffer::SIZE_SMALL).getSize(), grid.getMainWin().GetCharHeight()) + 1; //add some space
+ grid.setRowHeight(rowHeight);
}
bgstack15