summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/batch_config.cpp4
-rw-r--r--ui/batch_status_handler.cpp10
-rw-r--r--ui/column_attr.h57
-rw-r--r--ui/custom_grid.cpp448
-rw-r--r--ui/custom_grid.h15
-rw-r--r--ui/grid_view.cpp2
-rw-r--r--ui/gui_generated.cpp40
-rw-r--r--ui/gui_generated.h6
-rw-r--r--ui/gui_status_handler.cpp49
-rw-r--r--ui/main_dlg.cpp925
-rw-r--r--ui/main_dlg.h50
-rw-r--r--ui/search.cpp56
-rw-r--r--ui/search.h4
-rw-r--r--ui/tree_view.cpp10
-rw-r--r--ui/tree_view.h2
-rw-r--r--ui/triple_splitter.cpp230
-rw-r--r--ui/triple_splitter.h87
17 files changed, 1277 insertions, 718 deletions
diff --git a/ui/batch_config.cpp b/ui/batch_config.cpp
index 75033bf8..059996e8 100644
--- a/ui/batch_config.cpp
+++ b/ui/batch_config.cpp
@@ -61,7 +61,7 @@ private:
void OnFilesDropped(FileDropEvent& event);
void addFolderPair(const std::vector<zen::FolderPairEnh>& newPairs, bool addFront = false);
- void removeAddFolderPair(const int pos);
+ void removeAddFolderPair(int pos);
void clearAddFolderPairs();
void updateGuiForFolderPair();
@@ -830,7 +830,7 @@ void BatchDialog::addFolderPair(const std::vector<zen::FolderPairEnh>& newPairs,
}
-void BatchDialog::removeAddFolderPair(const int pos)
+void BatchDialog::removeAddFolderPair(int pos)
{
wxWindowUpdateLocker dummy(m_panelOverview); //avoid display distortion
diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp
index c0958025..baab3ed3 100644
--- a/ui/batch_status_handler.cpp
+++ b/ui/batch_status_handler.cpp
@@ -127,7 +127,8 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
totalTime.Start(); //measure total time
- //::wxSetEnv(L"logfile", logFile->getLogfileName());
+ //if (logFile)
+ // ::wxSetEnv(L"logfile", utfCvrtTo<wxString>(logFile->getFilename())); -> requires a command line interpreter to take advantage of
}
@@ -306,6 +307,8 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool&
ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& errorMessage)
{
+ errorLog.logMsg(errorMessage, TYPE_ERROR); //always, even for "retry"
+
switch (handleError_)
{
case xmlAccess::ON_ERROR_POPUP:
@@ -321,26 +324,23 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
case ReturnErrorDlg::BUTTON_IGNORE:
if (ignoreNextErrors) //falsify only
handleError_ = xmlAccess::ON_ERROR_IGNORE;
- errorLog.logMsg(errorMessage, TYPE_ERROR);
return ProcessCallback::IGNORE_ERROR;
case ReturnErrorDlg::BUTTON_RETRY:
return ProcessCallback::RETRY;
case ReturnErrorDlg::BUTTON_CANCEL:
- errorLog.logMsg(errorMessage, TYPE_ERROR);
abortThisProcess();
+ break;
}
}
break; //used if last switch didn't find a match
case xmlAccess::ON_ERROR_EXIT: //abort
- errorLog.logMsg(errorMessage, TYPE_ERROR);
abortThisProcess();
break;
case xmlAccess::ON_ERROR_IGNORE:
- errorLog.logMsg(errorMessage, TYPE_ERROR);
return ProcessCallback::IGNORE_ERROR;
}
diff --git a/ui/column_attr.h b/ui/column_attr.h
index 517961f4..ed98f403 100644
--- a/ui/column_attr.h
+++ b/ui/column_attr.h
@@ -25,11 +25,12 @@ enum ColumnTypeRim
struct ColumnAttributeRim
{
- ColumnAttributeRim() : type_(COL_TYPE_DIRECTORY), width_(0), visible_(false) {}
- ColumnAttributeRim(ColumnTypeRim type, int width, bool visible) : type_(type), width_(width), visible_(visible) {}
+ ColumnAttributeRim() : type_(COL_TYPE_DIRECTORY), offset_(0), stretch_(0), visible_(false) {}
+ ColumnAttributeRim(ColumnTypeRim type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {}
ColumnTypeRim type_;
- int width_; //negative value stretches proportionally!
+ int offset_;
+ int stretch_;
bool visible_;
};
@@ -39,26 +40,26 @@ namespace
std::vector<ColumnAttributeRim> getDefaultColumnAttributesLeft()
{
std::vector<ColumnAttributeRim> attr;
- attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, false));
- attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, false));
- attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, 200, true));
- attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, 150, true));
- attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, true));
- attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, false));
- attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, false));
+ attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false));
+ 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_EXTENSION, 60, 0, false));
return attr;
}
std::vector<ColumnAttributeRim> getDefaultColumnAttributesRight()
{
std::vector<ColumnAttributeRim> attr;
- attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, false));
- attr.push_back(ColumnAttributeRim(COL_TYPE_DIRECTORY, 200, false));
- attr.push_back(ColumnAttributeRim(COL_TYPE_REL_PATH, 200, false)); //already shown on left side
- attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, 150, true));
- attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, true));
- attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, false));
- attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, false));
+ attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false));
+ 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_EXTENSION, 60, 0, false));
return attr;
}
}
@@ -83,11 +84,12 @@ enum ColumnTypeNavi
struct ColumnAttributeNavi
{
- ColumnAttributeNavi() : type_(COL_TYPE_NAVI_DIRECTORY), width_(0), visible_(false) {}
- ColumnAttributeNavi(ColumnTypeNavi type, int width, bool visible) : type_(type), width_(width), visible_(visible) {}
+ ColumnAttributeNavi() : type_(COL_TYPE_NAVI_DIRECTORY), offset_(0), stretch_(0), visible_(false) {}
+ ColumnAttributeNavi(ColumnTypeNavi type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {}
ColumnTypeNavi type_;
- int width_; //negative value stretches proportionally!
+ int offset_;
+ int stretch_;
bool visible_;
};
@@ -100,19 +102,8 @@ inline
std::vector<ColumnAttributeNavi> getDefaultColumnAttributesNavi()
{
std::vector<ColumnAttributeNavi> attr;
-
- ColumnAttributeNavi newEntry;
-
- newEntry.type_ = COL_TYPE_NAVI_DIRECTORY;
- newEntry.visible_ = true;
- newEntry.width_ = -1; //stretch, old value: 280;
- attr.push_back(newEntry);
-
- newEntry.type_ = COL_TYPE_NAVI_BYTES;
- newEntry.visible_ = true;
- newEntry.width_ = 60; //GTK needs a few pixels more
- attr.push_back(newEntry);
-
+ attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_DIRECTORY, -60, 1, true)); //stretch to full width and substract sum of fixed size widths!
+ attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_BYTES, 60, 0, true)); //GTK needs a few pixels width more
return attr;
}
}
diff --git a/ui/custom_grid.cpp b/ui/custom_grid.cpp
index f340a819..0e27f21e 100644
--- a/ui/custom_grid.cpp
+++ b/ui/custom_grid.cpp
@@ -39,7 +39,8 @@ const wxColour COLOR_NOT_ACTIVE (228, 228, 228); //light grey
const Zstring ICON_FILE_FOLDER = Zstr("folder");
const int CHECK_BOX_IMAGE = 12; //width of checkbox image
-const int CHECK_BOX_WIDTH = CHECK_BOX_IMAGE + 2; //width of first block
+const int CHECK_BOX_SPACE_LEFT = 2;
+const int CHECK_BOX_WIDTH = CHECK_BOX_SPACE_LEFT + CHECK_BOX_IMAGE; //width of first block
const size_t ROW_COUNT_NO_DATA = 10;
@@ -58,9 +59,9 @@ class hierarchy:
-void refreshCell(Grid& grid, size_t row, ColumnType colType, size_t compPos)
+void refreshCell(Grid& grid, size_t row, ColumnType colType)
{
- wxRect cellArea = grid.getCellArea(row, colType, compPos); //returns empty rect if column not found; absolute coordinates!
+ wxRect cellArea = grid.getCellArea(row, colType); //returns empty rect if column not found; absolute coordinates!
if (cellArea.height > 0)
{
cellArea.SetTopLeft(grid.CalcScrolledPosition(cellArea.GetTopLeft()));
@@ -115,7 +116,7 @@ struct IconManager
class GridDataBase : public GridData
{
public:
- GridDataBase(Grid& grid) : grid_(grid) {}
+ GridDataBase(Grid& grid, const std::shared_ptr<const zen::GridView>& gridDataView) : grid_(grid), gridDataView_(gridDataView) {}
void holdOwnership(const std::shared_ptr<GridEventManager>& evtMgr) { evtMgr_ = evtMgr; }
@@ -123,18 +124,42 @@ protected:
Grid& refGrid() { return grid_; }
const Grid& refGrid() const { return grid_; }
+ const GridView* getGridDataView() const { return gridDataView_.get(); }
+
+ const FileSystemObject* getRawData(size_t row) const
+ {
+ if (auto view = getGridDataView())
+ return view->getObject(row);
+ return nullptr;
+ }
+
private:
+ virtual size_t getRowCount() const
+ {
+ if (gridDataView_)
+ {
+ if (gridDataView_->rowsTotal() == 0)
+ return ROW_COUNT_NO_DATA;
+ return gridDataView_->rowsOnView();
+ }
+ else
+ return ROW_COUNT_NO_DATA;
+
+ //return std::max(MIN_ROW_COUNT, gridDataView_ ? gridDataView_->rowsOnView() : 0);
+ }
+
std::shared_ptr<GridEventManager> evtMgr_;
Grid& grid_;
-
+ std::shared_ptr<const GridView> gridDataView_;
};
+
//########################################################################################################
template <SelectedSide side>
class GridDataRim : public GridDataBase
{
public:
- GridDataRim(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid, size_t compPos) : GridDataBase(grid), gridDataView_(gridDataView), compPos_(compPos) {}
+ GridDataRim(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataBase(grid, gridDataView) {}
void setIconManager(const std::shared_ptr<IconManager>& iconMgr) { iconMgr_ = iconMgr; }
@@ -165,7 +190,7 @@ public:
if (iconMgr_->iconBuffer.requestFileIcon(fileName))
{
//do a *full* refresh for *every* failed load to update partial DC updates while scrolling
- refreshCell(refGrid(), currentRow, static_cast<ColumnType>(COL_TYPE_FILENAME), compPos_);
+ refreshCell(refGrid(), currentRow, static_cast<ColumnType>(COL_TYPE_FILENAME));
setFailedLoad(currentRow, false);
}
else //not yet in buffer: mark for async. loading
@@ -203,6 +228,15 @@ protected:
//accessibility, support high-contrast schemes => work with user-defined background color!
const auto backCol = getBackGroundColor(row);
+ auto incChannel = [](unsigned char c, int diff) { return static_cast<unsigned char>(std::max(0, std::min(255, c + diff))); };
+
+ auto getAdjustedColor = [&](int diff)
+ {
+ return wxColor(incChannel(backCol.Red (), diff),
+ incChannel(backCol.Green(), diff),
+ incChannel(backCol.Blue (), diff));
+ };
+
auto colorDist = [](const wxColor& lhs, const wxColor& rhs) //just some metric
{
return numeric::power<2>(static_cast<int>(lhs.Red ()) - static_cast<int>(rhs.Red ())) +
@@ -210,15 +244,10 @@ protected:
numeric::power<2>(static_cast<int>(lhs.Blue ()) - static_cast<int>(rhs.Blue ()));
};
- const int levelDiff = 20;
- const int level = colorDist(backCol, *wxBLACK) < colorDist(backCol, *wxWHITE) ?
- levelDiff : -levelDiff; //brighten or darken
-
- auto incChannel = [level](unsigned char c) { return static_cast<unsigned char>(std::max(0, std::min(255, c + level))); };
+ const int signLevel = colorDist(backCol, *wxBLACK) < colorDist(backCol, *wxWHITE) ? 1 : -1; //brighten or darken
- const wxColor backColAlt(incChannel(backCol.Red ()),
- incChannel(backCol.Green()),
- incChannel(backCol.Blue ()));
+ const wxColor colOutter = getAdjustedColor(signLevel * 20);
+ const wxColor colInner = getAdjustedColor(signLevel * 10);
//clearArea(dc, rect, backColAlt);
@@ -228,8 +257,8 @@ protected:
wxRect rectLower = rect;
rectLower.y += rectUpper.height;
rectLower.height -= rectUpper.height;
- dc.GradientFillLinear(rectUpper, backColAlt, getBackGroundColor(row), wxSOUTH);
- dc.GradientFillLinear(rectLower, backColAlt, getBackGroundColor(row), wxNORTH);
+ dc.GradientFillLinear(rectUpper, colOutter, colInner, wxSOUTH);
+ dc.GradientFillLinear(rectLower, colOutter, colInner, wxNORTH);
}
else
clearArea(dc, rect, getBackGroundColor(row));
@@ -259,8 +288,6 @@ protected:
return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
}
- const FileSystemObject* getRawData(size_t row) const { return gridDataView_ ? gridDataView_->getObject(row) : nullptr; }
-
private:
enum DisplayType
{
@@ -270,7 +297,6 @@ private:
DISP_TYPE_INACTIVE,
};
-
DisplayType getRowDisplayType(size_t row) const
{
const FileSystemObject* fsObj = getRawData(row);
@@ -306,20 +332,6 @@ private:
return output;
}
- virtual size_t getRowCount() const
- {
- if (gridDataView_)
- {
- if (gridDataView_->rowsTotal() == 0)
- return ROW_COUNT_NO_DATA;
- return gridDataView_->rowsOnView();
- }
- else
- return ROW_COUNT_NO_DATA;
-
- //return std::max(MIN_ROW_COUNT, gridDataView_ ? gridDataView_->rowsOnView() : 0);
- }
-
virtual wxString getValue(size_t row, ColumnType colType) const
{
if (const FileSystemObject* fsObj = getRawData(row))
@@ -348,8 +360,9 @@ private:
if (!fsObj_.isEmpty<side>())
value = zen::toGuiString(fileObj.getFileSize<side>());
- //if (!fsObj_.isEmpty<side>()) -> test file id
- // value = toGuiString(fileObj.getFileId<side>().second);
+ // -> test file id
+ //if (!fsObj_.isEmpty<side>())
+ // value = toGuiString(fileObj.getFileId<side>().second) + L" " + toGuiString(fileObj.getFileId<side>().first);
break;
case COL_TYPE_DATE: //date
if (!fsObj_.isEmpty<side>())
@@ -587,12 +600,12 @@ private:
drawColumnLabelText(dc, rectInside, getColumnLabel(colType));
//draw sort marker
- if (gridDataView_)
+ if (getGridDataView())
{
- auto sortInfo = gridDataView_->getSortInfo();
+ auto sortInfo = getGridDataView()->getSortInfo();
if (sortInfo)
{
- if (colType == static_cast<ColumnType>(sortInfo->type_) && (compPos_ == gridview::COMP_LEFT) == sortInfo->onLeft_)
+ if (colType == static_cast<ColumnType>(sortInfo->type_) && (side == LEFT_SIDE) == sortInfo->onLeft_)
{
const wxBitmap& marker = GlobalResources::getImage(sortInfo->ascending_ ? L"sortAscending" : L"sortDescending");
wxPoint markerBegin = rectInside.GetTopLeft() + wxPoint((rectInside.width - marker.GetWidth()) / 2, 0);
@@ -639,7 +652,7 @@ private:
const FileSystemObject* fsObj = getRawData(row);
if (fsObj && !fsObj->isEmpty<side>())
{
- toolTip = toWx(gridDataView_->getFolderPairCount() > 1 ? //gridDataView_ bound in this path
+ toolTip = toWx(getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ?
fsObj->getFullName<side>() :
fsObj->getRelativeName<side>());
@@ -669,10 +682,8 @@ private:
return toolTip;
}
- std::shared_ptr<const zen::GridView> gridDataView_;
std::shared_ptr<IconManager> iconMgr_; //optional
std::vector<char> failedLoads; //effectively a vector<bool> of size "number of rows"
- const size_t compPos_;
std::unique_ptr<wxBitmap> buffer; //avoid costs of recreating this temporal variable
};
@@ -680,7 +691,7 @@ private:
class GridDataLeft : public GridDataRim<LEFT_SIDE>
{
public:
- GridDataLeft(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid, size_t compPos) : GridDataRim<LEFT_SIDE>(gridDataView, grid, compPos) {}
+ GridDataLeft(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataRim<LEFT_SIDE>(gridDataView, grid) {}
void setNavigationMarker(std::vector<const HierarchyObject*>&& markedFiles,
std::vector<const HierarchyObject*>&& markedContainer)
@@ -754,20 +765,17 @@ private:
class GridDataRight : public GridDataRim<RIGHT_SIDE>
{
public:
- GridDataRight(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid, size_t compPos) : GridDataRim<RIGHT_SIDE>(gridDataView, grid, compPos) {}
+ GridDataRight(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataRim<RIGHT_SIDE>(gridDataView, grid) {}
};
-
-
//########################################################################################################
class GridDataMiddle : public GridDataBase
{
public:
GridDataMiddle(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) :
- GridDataBase(grid),
- gridDataView_(gridDataView),
+ GridDataBase(grid, gridDataView),
showSyncAction_(true) {}
void onSelectBegin(const wxPoint& clientPos, size_t row, ColumnType colType)
@@ -775,14 +783,14 @@ public:
if (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE &&
row < refGrid().getRowCount())
{
- refGrid().clearSelection(gridview::COMP_MIDDLE);
+ refGrid().clearSelection();
dragSelection.reset(new 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
{
- refGrid().clearSelection(gridview::COMP_MIDDLE);
+ refGrid().clearSelection();
//issue custom event
if (dragSelection)
@@ -826,7 +834,7 @@ public:
}
}
- void onMouseMovement(const wxPoint& clientPos, size_t row, ColumnType colType, size_t compPos)
+ void onMouseMovement(const wxPoint& clientPos, size_t row, ColumnType colType)
{
//manage block highlighting and custom tooltip
if (dragSelection)
@@ -835,13 +843,13 @@ public:
}
else
{
- if (compPos == gridview::COMP_MIDDLE && static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE)
+ if (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE)
{
if (highlight) //refresh old highlight
- refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE), gridview::COMP_MIDDLE);
+ refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE));
highlight.reset(new std::pair<size_t, BlockPosition>(row, mousePosToBlock(clientPos, row)));
- refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE), gridview::COMP_MIDDLE);
+ refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE));
//show custom tooltip
showToolTip(row, refGrid().getMainWin().ClientToScreen(clientPos));
@@ -855,7 +863,7 @@ public:
{
if (highlight)
{
- refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE), gridview::COMP_MIDDLE);
+ refreshCell(refGrid(), highlight->first, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE));
highlight.reset();
}
@@ -865,8 +873,6 @@ public:
void showSyncAction(bool value) { showSyncAction_ = value; }
private:
- virtual size_t getRowCount() const { return 0; /*if there are multiple grid components, only the first one will be polled for row count!*/ }
-
virtual wxString getValue(size_t row, ColumnType colType) const
{
if (static_cast<ColumnTypeMiddle>(colType) == COL_TYPE_MIDDLE_VALUE)
@@ -891,11 +897,15 @@ private:
{
if (const FileSystemObject* fsObj = getRawData(row))
{
- wxRect rectInside = drawCellBorder(dc, rect);
+ //wxRect rectInside = drawCellBorder(dc, rect);
+ wxRect rectInside = rect;
+
+ rectInside.width -= CHECK_BOX_SPACE_LEFT;
+ rectInside.x += CHECK_BOX_SPACE_LEFT;
//draw checkbox
wxRect checkBoxArea = rectInside;
- checkBoxArea.SetWidth(CHECK_BOX_WIDTH);
+ checkBoxArea.SetWidth(CHECK_BOX_IMAGE);
const bool rowHighlighted = dragSelection ? row == dragSelection->first : highlight ? row == highlight->first : false;
const BlockPosition highlightBlock = dragSelection ? dragSelection->second : highlight ? highlight->second : BLOCKPOS_CHECK_BOX;
@@ -905,8 +915,8 @@ private:
else //default
drawBitmapRtlMirror(dc, GlobalResources::getImage(fsObj->isActive() ? L"checkboxTrue" : L"checkboxFalse" ), checkBoxArea, wxALIGN_CENTER, buffer);
- rectInside.width -= CHECK_BOX_WIDTH;
- rectInside.x += CHECK_BOX_WIDTH;
+ rectInside.width -= CHECK_BOX_IMAGE;
+ rectInside.x += CHECK_BOX_IMAGE;
//synchronization preview
if (showSyncAction_)
@@ -964,8 +974,6 @@ private:
}
}
- const FileSystemObject* getRawData(size_t row) const { return gridDataView_ ? gridDataView_->getObject(row) : nullptr; }
-
wxColor getBackGroundColor(size_t row) const
{
if (const FileSystemObject* fsObj = getRawData(row))
@@ -1019,9 +1027,9 @@ private:
case FILE_EQUAL:
break; //usually white
case FILE_CONFLICT:
+ case FILE_DIFFERENT_METADATA: //= sub-category of equal, but hint via background that sync direction follows conflict-setting
return COLOR_YELLOW;
- case FILE_DIFFERENT_METADATA:
- return COLOR_YELLOW_LIGHT;
+ //return COLOR_YELLOW_LIGHT;
}
}
}
@@ -1042,7 +1050,7 @@ private:
{
const int absX = refGrid().CalcUnscrolledPosition(clientPos).x;
- const wxRect rect = refGrid().getCellArea(row, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE), gridview::COMP_MIDDLE); //returns empty rect if column not found; absolute coordinates!
+ const wxRect rect = refGrid().getCellArea(row, static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE)); //returns empty rect if column not found; absolute coordinates!
if (rect.width > CHECK_BOX_WIDTH && rect.height > 0)
{
const FileSystemObject* const fsObj = getRawData(row);
@@ -1136,9 +1144,8 @@ private:
case FILE_DIFFERENT:
return L"different";
case FILE_EQUAL:
+ case FILE_DIFFERENT_METADATA: //= sub-category of equal
return L"equal";
- case FILE_DIFFERENT_METADATA:
- return L"conflict";
case FILE_CONFLICT:
return L"conflict";
}
@@ -1155,7 +1162,6 @@ private:
virtual wxString getToolTip(ColumnType colType) const { return showSyncAction_ ? _("Action") : _("Category"); }
- std::shared_ptr<const zen::GridView> gridDataView_;
bool showSyncAction_;
std::unique_ptr<std::pair<size_t, BlockPosition>> highlight; //(row, block) current mouse highlight
std::unique_ptr<std::pair<size_t, BlockPosition>> dragSelection; //(row, block)
@@ -1165,58 +1171,108 @@ private:
//########################################################################################################
+const wxEventType EVENT_ALIGN_SCROLLBARS = wxNewEventType();
+
class GridEventManager : private wxEvtHandler
{
public:
- GridEventManager(Grid& grid,
+ GridEventManager(Grid& gridL,
+ Grid& gridC,
+ Grid& gridR,
GridDataLeft& provLeft,
GridDataMiddle& provMiddle,
- GridDataRight& provRight) : grid_(grid), provLeft_(provLeft), provMiddle_(provMiddle), provRight_(provRight)
+ GridDataRight& provRight) :
+ gridL_(gridL), gridC_(gridC), gridR_(gridR),
+ provLeft_(provLeft), provMiddle_(provMiddle), provRight_(provRight)
{
- grid_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumn), nullptr, this);
+ gridL_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnL), nullptr, this);
+ gridR_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnR), nullptr, this);
+
+ gridL_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownL), nullptr, this);
+ gridC_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownC), nullptr, this);
+ gridR_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownR), nullptr, this);
+
+ gridC_.getMainWin().Connect(wxEVT_MOTION, wxMouseEventHandler(GridEventManager::onCenterMouseMovement), nullptr, this);
+ gridC_.getMainWin().Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(GridEventManager::onCenterMouseLeave ), nullptr, this);
+
+ gridC_.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridEventManager::onCenterSelectBegin), nullptr, this);
+ 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);
+
+ //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);
+
+ Connect(EVENT_ALIGN_SCROLLBARS, wxEventHandler(GridEventManager::onAlignScrollBars), NULL, this);
+ }
- grid_.getMainWin().Connect(wxEVT_MOTION, wxMouseEventHandler(GridEventManager::onMouseMovement), nullptr, this);
- grid_.getMainWin().Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(GridEventManager::onMouseLeave ), nullptr, this);
- grid_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDown ), nullptr, this);
+private:
+ void onCenterSelectBegin(GridClickEvent& event)
+ {
- grid_.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridEventManager::onSelectBegin), nullptr, this);
- grid_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onSelectEnd ), nullptr, this);
+ provMiddle_.onSelectBegin(event.GetPosition(), event.row_, event.colType_);
+ event.Skip();
}
-private:
- void onMouseMovement(wxMouseEvent& event)
+ void onCenterSelectEnd(GridRangeSelectEvent& event)
{
- const wxPoint& topLeftAbs = grid_.CalcUnscrolledPosition(event.GetPosition());
- const int row = grid_.getRowAtPos(topLeftAbs.y); //returns < 0 if column not found; absolute coordinates!
- if (auto colInfo = grid_.getColumnAtPos(topLeftAbs.x)) //(column type, component position)
+ 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_);
+ 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!
+ if (auto colInfo = gridC_.getColumnAtPos(topLeftAbs.x)) //(column type, component position)
{
//redirect mouse movement to middle grid component
- provMiddle_.onMouseMovement(event.GetPosition(), row, colInfo->first, colInfo->second);
+ provMiddle_.onMouseMovement(event.GetPosition(), row, colInfo->first);
}
event.Skip();
}
- void onMouseLeave(wxMouseEvent& event)
+ void onCenterMouseLeave(wxMouseEvent& event)
{
provMiddle_.onMouseLeave();
event.Skip();
}
- void onSelectBegin(GridClickEvent& event)
- {
- if (event.compPos_ == gridview::COMP_MIDDLE)
- provMiddle_.onSelectBegin(event.GetPosition(), event.row_, event.colType_);
- event.Skip();
- }
+ void onGridSelectionL(GridRangeSelectEvent& event) { onGridSelection(gridL_, gridR_); event.Skip(); }
+ void onGridSelectionR(GridRangeSelectEvent& event) { onGridSelection(gridR_, gridL_); event.Skip(); }
- void onSelectEnd(GridRangeSelectEvent& event)
+ void onGridSelection(const Grid& grid, Grid& other)
{
- if (event.compPos_ == gridview::COMP_MIDDLE)
- provMiddle_.onSelectEnd(event.rowFrom_, event.rowTo_);
- event.Skip();
+ if (!wxGetKeyState(WXK_CONTROL)) //clear other grid unless user is holding CTRL
+ other.clearSelection();
}
- void onKeyDown(wxKeyEvent& event)
+ void onKeyDownL(wxKeyEvent& event) { onKeyDown(event, gridL_); }
+ void onKeyDownC(wxKeyEvent& event) { onKeyDown(event, gridC_); }
+ void onKeyDownR(wxKeyEvent& event) { onKeyDown(event, gridR_); }
+
+ void onKeyDown(wxKeyEvent& event, const Grid& grid)
{
int keyCode = event.GetKeyCode();
if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
@@ -1233,7 +1289,7 @@ private:
//skip middle component when navigating via keyboard
- const auto row = grid_.getGridCursor().first;
+ const auto row = grid.getGridCursor().first;
if (event.ShiftDown())
;
@@ -1244,47 +1300,119 @@ private:
{
case WXK_LEFT:
case WXK_NUMPAD_LEFT:
- grid_.setGridCursor(row, gridview::COMP_LEFT);
+ gridL_.setGridCursor(row);
+ gridL_.SetFocus();
return; //swallow event
case WXK_RIGHT:
case WXK_NUMPAD_RIGHT:
- grid_.setGridCursor(row, gridview::COMP_RIGHT);
+ gridR_.setGridCursor(row);
+ gridR_.SetFocus();
return; //swallow event
}
event.Skip();
}
- void onResizeColumn(GridColumnResizeEvent& event)
+ void onResizeColumnL(GridColumnResizeEvent& event) { resizeOtherSide(gridL_, gridR_, event.colType_, event.offset_); }
+ void onResizeColumnR(GridColumnResizeEvent& event) { resizeOtherSide(gridR_, gridL_, event.colType_, event.offset_); }
+
+ void resizeOtherSide(const Grid& src, Grid& trg, ColumnType type, ptrdiff_t offset)
{
- auto resizeOtherSide = [&](size_t compPosOther)
+ //find stretch factor of resized column: type is unique due to makeConsistent()!
+ std::vector<Grid::ColumnAttribute> cfgSrc = src.getColumnConfig();
+ auto iter = std::find_if(cfgSrc.begin(), cfgSrc.end(), [&](Grid::ColumnAttribute& ca) { return ca.type_ == type; });
+ if (iter == cfgSrc.end())
+ return;
+ const ptrdiff_t stretchSrc = iter->stretch_;
+
+ //we do not propagate resizings on stretched columns to the other side: awkward user experience
+ if (stretchSrc > 0)
+ return;
+
+ //apply resized offset to other side, but only if stretch factors match!
+ std::vector<Grid::ColumnAttribute> cfgTrg = trg.getColumnConfig();
+ std::for_each(cfgTrg.begin(), cfgTrg.end(), [&](Grid::ColumnAttribute& ca)
{
- std::vector<Grid::ColumnAttribute> colAttr = grid_.getColumnConfig(compPosOther);
+ if (ca.type_ == type && ca.stretch_ == stretchSrc)
+ ca.offset_ = offset;
+ });
+ trg.setColumnConfig(cfgTrg);
+ }
- std::for_each(colAttr.begin(), colAttr.end(), [&](Grid::ColumnAttribute& ca)
- {
- if (ca.type_ == event.colType_)
- ca.width_ = event.width_;
- });
+ void onGridAccessL(wxEvent& event) { gridL_.SetFocus(); event.Skip(); }
+ void onGridAccessR(wxEvent& event) { gridR_.SetFocus(); event.Skip(); }
+
+ void onPaintGridL(wxEvent& event) { onPaintGrid(gridL_); event.Skip(); }
+ void onPaintGridC(wxEvent& event) { onPaintGrid(gridC_); event.Skip(); }
+ void onPaintGridR(wxEvent& event) { onPaintGrid(gridR_); event.Skip(); }
- grid_.setColumnConfig(colAttr, compPosOther); //set column count + widths
+ void onPaintGrid(const Grid& grid)
+ {
+ //align scroll positions of all three grids *synchronously* during paint event! (wxGTK has visible delay when this is done asynchronously, no delay on Windows)
+
+ //determine lead grid
+ const Grid* lead = nullptr;
+ Grid* follow1 = nullptr;
+ Grid* follow2 = nullptr;
+ auto setGrids = [&](const Grid& l, Grid& f1, Grid& f2) { lead = &l; follow1 = &f1; follow2 = &f2; };
+
+ if (wxWindow::FindFocus() == &gridC_.getMainWin())
+ setGrids(gridC_, gridL_, gridR_);
+ else if (wxWindow::FindFocus() == &gridR_.getMainWin())
+ setGrids(gridR_, gridL_, gridC_);
+ else //default: left panel
+ setGrids(gridL_, gridC_, gridR_);
+
+ //align other grids only while repainting the lead grid to avoid scrolling and updating a grid at the same time!
+ if (lead != &grid) return;
+
+ auto scroll = [](Grid& target, int y) //support polling
+ {
+ //scroll vertically only - scrolling horizontally becomes annoying if left and right sides have different widths;
+ //e.g. h-scroll on left would be undone when scrolling vertically on right which doesn't have a h-scrollbar
+ int yOld = 0;
+ target.GetViewStart(nullptr, &yOld);
+ if (yOld != y)
+ target.Scroll(-1, y);
};
+ int y = 0;
+ lead->GetViewStart(nullptr, &y);
+ scroll(*follow1, y);
+ scroll(*follow2, y);
+
+ //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!
+ }
- switch (event.compPos_)
+ void onAlignScrollBars(wxEvent& event)
+ {
+ auto needsHorizontalScrollbars = [](Grid& grid) -> bool
{
- case gridview::COMP_LEFT:
- resizeOtherSide(gridview::COMP_RIGHT);
- break;
- case gridview::COMP_MIDDLE:
- break;
- case gridview::COMP_RIGHT:
- resizeOtherSide(gridview::COMP_LEFT);
- break;
- }
+ const wxWindow& mainWin = grid.getMainWin();
+ return mainWin.GetVirtualSize().GetWidth() > mainWin.GetClientSize().GetWidth();
+ //assuming Grid::updateWindowSizes() does its job well, this should suffice!
+ //CAVEAT: if horizontal and vertical scrollbar are circular dependent from each other
+ //(h-scrollbar is shown due to v-scrollbar consuming horizontal width, ect...)
+ //while in fact both are NOT needed, this special case results in a bogus need for scrollbars!
+ //see https://sourceforge.net/tracker/?func=detail&aid=3514183&group_id=234430&atid=1093083
+ // => since we're outside the Grid abstraction, we should not duplicate code to handle this special case as it seems to be insignificant
+ };
+
+ Grid::ScrollBarStatus sbStatusX = needsHorizontalScrollbars(gridL_) ||
+ needsHorizontalScrollbars(gridR_) ?
+ Grid::SB_SHOW_ALWAYS : Grid::SB_SHOW_NEVER;
+ gridL_.showScrollBars(sbStatusX, Grid::SB_SHOW_NEVER);
+ gridC_.showScrollBars(sbStatusX, Grid::SB_SHOW_NEVER);
+ gridR_.showScrollBars(sbStatusX, Grid::SB_SHOW_AUTOMATIC);
}
- Grid& grid_;
+ Grid& gridL_;
+ Grid& gridC_;
+ Grid& gridR_;
GridDataLeft& provLeft_;
GridDataMiddle& provMiddle_;
GridDataRight& provRight_;
@@ -1293,32 +1421,36 @@ private:
//########################################################################################################
-void gridview::init(Grid& grid, const std::shared_ptr<const zen::GridView>& gridDataView)
+void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr<const zen::GridView>& gridDataView)
{
- grid.setComponentCount(3);
-
- auto provLeft_ = std::make_shared<GridDataLeft >(gridDataView, grid, gridview::COMP_LEFT );
- auto provMiddle_ = std::make_shared<GridDataMiddle>(gridDataView, grid);
- auto provRight_ = std::make_shared<GridDataRight >(gridDataView, grid, gridview::COMP_RIGHT);
+ auto provLeft_ = std::make_shared<GridDataLeft >(gridDataView, gridLeft);
+ auto provMiddle_ = std::make_shared<GridDataMiddle>(gridDataView, gridCenter);
+ auto provRight_ = std::make_shared<GridDataRight >(gridDataView, gridRight);
- grid.setDataProvider(provLeft_ , gridview::COMP_LEFT); //data providers reference grid =>
- grid.setDataProvider(provMiddle_, gridview::COMP_MIDDLE); //ownership must belong *exclusively* to grid!
- grid.setDataProvider(provRight_ , gridview::COMP_RIGHT);
+ gridLeft .setDataProvider(provLeft_); //data providers reference grid =>
+ gridCenter.setDataProvider(provMiddle_); //ownership must belong *exclusively* to grid!
+ gridRight .setDataProvider(provRight_);
- auto evtMgr = std::make_shared<GridEventManager>(grid, *provLeft_, *provMiddle_, *provRight_);
+ auto evtMgr = std::make_shared<GridEventManager>(gridLeft, gridCenter, gridRight, *provLeft_, *provMiddle_, *provRight_);
provLeft_ ->holdOwnership(evtMgr);
provMiddle_->holdOwnership(evtMgr);
provRight_ ->holdOwnership(evtMgr);
- grid.enableColumnMove (false, gridview::COMP_MIDDLE);
- grid.enableColumnResize(false, gridview::COMP_MIDDLE);
+ gridCenter.enableColumnMove (false);
+ gridCenter.enableColumnResize(false);
- std::vector<Grid::ColumnAttribute> attribMiddle;
- attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_BORDER), 5));
- attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE), 60));
- attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_BORDER), 5));
+ gridCenter.showRowLabel(false);
+ gridRight .showRowLabel(false);
- grid.setColumnConfig(attribMiddle, gridview::COMP_MIDDLE);
+ //gridLeft .showScrollBars(Grid::SB_SHOW_AUTOMATIC, Grid::SB_SHOW_NEVER); -> redundant: configuration happens in GridEventManager::onAlignScrollBars()
+ //gridCenter.showScrollBars(Grid::SB_SHOW_NEVER, Grid::SB_SHOW_NEVER);
+
+ gridCenter.SetSize(60 /*+ 2 * 5*/, -1);
+ std::vector<Grid::ColumnAttribute> attribMiddle;
+ //attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_BORDER), 5, 0, true));
+ attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_MIDDLE_VALUE), 60, 0, true));
+ //attribMiddle.push_back(Grid::ColumnAttribute(static_cast<ColumnType>(COL_TYPE_BORDER), 5, 0, true));
+ gridCenter.setColumnConfig(attribMiddle);
}
@@ -1348,7 +1480,7 @@ std::vector<Grid::ColumnAttribute> gridview::convertConfig(const std::vector<Col
std::vector<Grid::ColumnAttribute> output;
std::transform(attribClean.begin(), attribClean.end(), std::back_inserter(output),
- [&](const ColumnAttributeRim& a) { return Grid::ColumnAttribute(static_cast<ColumnType>(a.type_), a.width_, a.visible_); });
+ [&](const ColumnAttributeRim& ca) { return Grid::ColumnAttribute(static_cast<ColumnType>(ca.type_), ca.offset_, ca.stretch_, ca.visible_); });
return output;
}
@@ -1359,7 +1491,7 @@ std::vector<ColumnAttributeRim> gridview::convertConfig(const std::vector<Grid::
std::vector<ColumnAttributeRim> output;
std::transform(attribs.begin(), attribs.end(), std::back_inserter(output),
- [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeRim(static_cast<ColumnTypeRim>(ca.type_), ca.width_, ca.visible_); });
+ [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeRim(static_cast<ColumnTypeRim>(ca.type_), ca.offset_, ca.stretch_, ca.visible_); });
return makeConsistent(output);
}
@@ -1392,13 +1524,14 @@ private:
};
}
-void gridview::setupIcons(Grid& grid, bool show, IconBuffer::IconSize sz)
+void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz)
{
- auto* provLeft = dynamic_cast<GridDataLeft*>(grid.getDataProvider(gridview::COMP_LEFT));
- auto* provRight = dynamic_cast<GridDataRight*>(grid.getDataProvider(gridview::COMP_RIGHT));
+ auto* provLeft = dynamic_cast<GridDataLeft*>(gridLeft .getDataProvider());
+ auto* provRight = dynamic_cast<GridDataRight*>(gridRight.getDataProvider());
if (provLeft && provRight)
{
+ int newRowHeight = 0;
if (show)
{
auto iconMgr = std::make_shared<IconManager>(sz);
@@ -1406,44 +1539,53 @@ void gridview::setupIcons(Grid& grid, bool show, IconBuffer::IconSize sz)
provLeft ->setIconManager(iconMgr);
provRight->setIconManager(iconMgr);
- grid.setRowHeight(iconMgr->iconBuffer.getSize() + 1); //+ 1 for line between rows
+ newRowHeight = iconMgr->iconBuffer.getSize() + 1; //+ 1 for line between rows
}
else
{
provLeft ->setIconManager(nullptr);
provRight->setIconManager(nullptr);
- grid.setRowHeight(IconBuffer(IconBuffer::SIZE_SMALL).getSize() + 1); //+ 1 for line between rows
+ newRowHeight = IconBuffer(IconBuffer::SIZE_SMALL).getSize() + 1; //+ 1 for line between rows
}
- grid.Refresh();
+ gridLeft .setRowHeight(newRowHeight);
+ gridCenter.setRowHeight(newRowHeight);
+ gridRight .setRowHeight(newRowHeight);
}
else
assert(false);
}
-void gridview::clearSelection(Grid& grid)
+void gridview::clearSelection(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
+{
+ gridLeft .clearSelection();
+ gridCenter.clearSelection();
+ gridRight .clearSelection();
+}
+
+void gridview::refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
{
- grid.clearSelection(gridview::COMP_LEFT);
- grid.clearSelection(gridview::COMP_MIDDLE);
- grid.clearSelection(gridview::COMP_RIGHT);
+ gridLeft .Refresh();
+ gridCenter.Refresh();
+ gridRight .Refresh();
}
-void gridview::setNavigationMarker(Grid& grid,
+void gridview::setNavigationMarker(Grid& gridLeft,
std::vector<const HierarchyObject*>&& markedFiles,
std::vector<const HierarchyObject*>&& markedContainer)
{
- if (auto* provLeft = dynamic_cast<GridDataLeft*>(grid.getDataProvider(gridview::COMP_LEFT)))
+ if (auto* provLeft = dynamic_cast<GridDataLeft*>(gridLeft.getDataProvider()))
provLeft->setNavigationMarker(std::move(markedFiles), std::move(markedContainer));
else
assert(false);
- grid.Refresh();
+ gridLeft.Refresh();
}
-void gridview::showSyncAction(Grid& grid, bool value)
+void gridview::showSyncAction(Grid& gridCenter, bool value)
{
- if (auto* provMiddle = dynamic_cast<GridDataMiddle*>(grid.getDataProvider(gridview::COMP_MIDDLE)))
+ if (auto* provMiddle = dynamic_cast<GridDataMiddle*>(gridCenter.getDataProvider()))
provMiddle->showSyncAction(value);
else
assert(false);
@@ -1503,9 +1645,9 @@ wxBitmap zen::getCmpResultImage(CompareFilesResult cmpResult)
case FILE_DIFFERENT:
return GlobalResources::getImage(L"differentSmall");
case FILE_EQUAL:
+ case FILE_DIFFERENT_METADATA: //= sub-category of equal
return GlobalResources::getImage(L"equalSmall");
case FILE_CONFLICT:
- case FILE_DIFFERENT_METADATA:
return GlobalResources::getImage(L"conflictSmall");
}
return wxNullBitmap;
diff --git a/ui/custom_grid.h b/ui/custom_grid.h
index 58a421e6..98092ade 100644
--- a/ui/custom_grid.h
+++ b/ui/custom_grid.h
@@ -17,23 +17,20 @@ namespace zen
//setup grid to show grid view within three components:
namespace gridview
{
-static const size_t COMP_LEFT = 0;
-static const size_t COMP_MIDDLE = 1;
-static const size_t COMP_RIGHT = 2;
-
-void init(Grid& grid, const std::shared_ptr<const GridView>& gridDataView);
+void init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr<const GridView>& gridDataView);
std::vector<Grid::ColumnAttribute> convertConfig(const std::vector<ColumnAttributeRim>& attribs); //+ make consistent
std::vector<ColumnAttributeRim> convertConfig(const std::vector<Grid::ColumnAttribute>& attribs); //
-void showSyncAction(Grid& grid, bool value);
+void showSyncAction(Grid& gridCenter, bool value);
-void setupIcons(Grid& grid, bool show, IconBuffer::IconSize sz);
+void setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz);
-void clearSelection(Grid& grid); //clear all components
+void clearSelection(Grid& gridLeft, Grid& gridCenter, Grid& gridRight); //clear all components
+void refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight);
//mark rows selected in navigation/compressed tree and navigate to leading object
-void setNavigationMarker(Grid& grid,
+void setNavigationMarker(Grid& gridLeft,
std::vector<const HierarchyObject*>&& markedFiles, //mark files/symlinks directly within a container
std::vector<const HierarchyObject*>&& markedContainer); //mark full container including child-objects
}
diff --git a/ui/grid_view.cpp b/ui/grid_view.cpp
index 2162394f..8764e233 100644
--- a/ui/grid_view.cpp
+++ b/ui/grid_view.cpp
@@ -161,11 +161,11 @@ GridView::StatusCmpResult GridView::updateCmpResult(bool hideFiltered, //maps so
if (!differentFilesActive) return false;
break;
case FILE_EQUAL:
+ case FILE_DIFFERENT_METADATA: //= sub-category of equal
output.existsEqual = true;
if (!equalFilesActive) return false;
break;
case FILE_CONFLICT:
- case FILE_DIFFERENT_METADATA: //no extra button on screen
output.existsConflict = true;
if (!conflictFilesActive) return false;
break;
diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp
index c0f83cec..1f58d6a5 100644
--- a/ui/gui_generated.cpp
+++ b/ui/gui_generated.cpp
@@ -359,9 +359,27 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
wxBoxSizer* bSizer1711;
bSizer1711 = new wxBoxSizer( wxVERTICAL );
- m_gridMain = new zen::Grid( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
- m_gridMain->SetScrollRate( 5, 5 );
- bSizer1711->Add( m_gridMain, 1, wxEXPAND, 5 );
+ m_splitterMain = new zen::TripleSplitter( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ wxBoxSizer* bSizer1781;
+ bSizer1781 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_gridMainL = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
+ m_gridMainL->SetScrollRate( 5, 5 );
+ bSizer1781->Add( m_gridMainL, 1, wxEXPAND, 5 );
+
+ m_gridMainC = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
+ m_gridMainC->SetScrollRate( 5, 5 );
+ bSizer1781->Add( m_gridMainC, 0, wxEXPAND, 5 );
+
+ m_gridMainR = new zen::Grid( m_splitterMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
+ m_gridMainR->SetScrollRate( 5, 5 );
+ bSizer1781->Add( m_gridMainR, 1, wxEXPAND, 5 );
+
+
+ m_splitterMain->SetSizer( bSizer1781 );
+ m_splitterMain->Layout();
+ bSizer1781->Fit( m_splitterMain );
+ bSizer1711->Add( m_splitterMain, 1, wxEXPAND, 5 );
m_panelStatusBar = new wxPanel( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL );
wxBoxSizer* bSizer451;
@@ -2676,22 +2694,25 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer184;
bSizer184 = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer178;
+ bSizer178 = new wxBoxSizer( wxVERTICAL );
+
m_staticText83 = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText83->Wrap( -1 );
m_staticText83->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 93, 92, false, wxEmptyString ) );
m_staticText83->SetForegroundColour( wxColour( 0, 0, 0 ) );
- bSizer184->Add( m_staticText83, 0, wxALL, 5 );
-
-
- bSizer184->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer178->Add( m_staticText83, 0, wxALL, 5 );
m_hyperlink3 = new wxHyperlinkCtrl( m_panel39, wxID_ANY, _("Donate with PayPal"), wxT("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=zhnmju123@gmx.de&lc=US&currency_code=EUR"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink3->SetFont( wxFont( 10, 70, 90, 92, true, wxEmptyString ) );
m_hyperlink3->SetBackgroundColour( wxColour( 221, 221, 255 ) );
m_hyperlink3->SetToolTip( _("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=zhnmju123@gmx.de&lc=US&currency_code=EUR") );
- bSizer184->Add( m_hyperlink3, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ bSizer178->Add( m_hyperlink3, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+
+ bSizer184->Add( bSizer178, 1, wxALIGN_CENTER_VERTICAL, 5 );
m_bitmapPaypal = new wxStaticBitmap( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
m_bitmapPaypal->SetToolTip( _("Donate with PayPal") );
@@ -2699,9 +2720,6 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer184->Add( m_bitmapPaypal, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
- bSizer184->Add( 0, 0, 1, wxEXPAND, 5 );
-
-
m_panel39->SetSizer( bSizer184 );
m_panel39->Layout();
bSizer184->Fit( m_panel39 );
diff --git a/ui/gui_generated.h b/ui/gui_generated.h
index fa388377..29ef091d 100644
--- a/ui/gui_generated.h
+++ b/ui/gui_generated.h
@@ -16,6 +16,7 @@
#include "folder_history_box.h"
#include "../wx+/dir_picker.h"
#include "../wx+/grid.h"
+#include "triple_splitter.h"
#include "../wx+/toggle_button.h"
#include "exec_finished_box.h"
#include "../wx+/graph.h"
@@ -106,7 +107,10 @@ protected:
wxBoxSizer* bSizerAddFolderPairs;
zen::Grid* m_gridNavi;
wxPanel* m_panelCenter;
- zen::Grid* m_gridMain;
+ zen::TripleSplitter* m_splitterMain;
+ zen::Grid* m_gridMainL;
+ zen::Grid* m_gridMainC;
+ zen::Grid* m_gridMainR;
wxPanel* m_panelStatusBar;
wxBoxSizer* bSizerStatusLeftDirectories;
wxStaticBitmap* m_bitmapSmallDirectoryLeft;
diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp
index 57785403..b25e4249 100644
--- a/ui/gui_status_handler.cpp
+++ b/ui/gui_status_handler.cpp
@@ -279,37 +279,38 @@ void SyncStatusHandler::reportInfo(const std::wstring& text)
ProcessCallback::Response SyncStatusHandler::reportError(const std::wstring& errorMessage)
{
+ errorLog.logMsg(errorMessage, TYPE_ERROR); //always, even for "retry"
+
switch (handleError_)
{
case ON_GUIERROR_POPUP:
- break;
- case ON_GUIERROR_IGNORE:
- errorLog.logMsg(errorMessage, TYPE_ERROR);
- return ProcessCallback::IGNORE_ERROR;
- }
+ {
+ PauseTimers dummy(syncStatusFrame);
+ forceUiRefresh();
- PauseTimers dummy(syncStatusFrame);
- forceUiRefresh();
+ bool ignoreNextErrors = false;
+ switch (showErrorDlg(parentDlg_,
+ ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL,
+ errorMessage,
+ &ignoreNextErrors))
+ {
+ case ReturnErrorDlg::BUTTON_IGNORE:
+ if (ignoreNextErrors) //falsify only
+ handleError_ = ON_GUIERROR_IGNORE;
+ return ProcessCallback::IGNORE_ERROR;
- bool ignoreNextErrors = false;
- switch (showErrorDlg(parentDlg_,
- ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL,
- errorMessage,
- &ignoreNextErrors))
- {
- case ReturnErrorDlg::BUTTON_IGNORE:
- if (ignoreNextErrors) //falsify only
- handleError_ = ON_GUIERROR_IGNORE;
- errorLog.logMsg(errorMessage, TYPE_ERROR);
- return ProcessCallback::IGNORE_ERROR;
+ case ReturnErrorDlg::BUTTON_RETRY:
+ return ProcessCallback::RETRY;
- case ReturnErrorDlg::BUTTON_RETRY:
- return ProcessCallback::RETRY;
+ case ReturnErrorDlg::BUTTON_CANCEL:
+ abortThisProcess();
+ break;
+ }
+ }
+ break;
- case ReturnErrorDlg::BUTTON_CANCEL:
- errorLog.logMsg(errorMessage, TYPE_ERROR);
- abortThisProcess();
- break;
+ case ON_GUIERROR_IGNORE:
+ return ProcessCallback::IGNORE_ERROR;
}
assert(false);
diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp
index 03a94e33..7681cde2 100644
--- a/ui/main_dlg.cpp
+++ b/ui/main_dlg.cpp
@@ -36,6 +36,7 @@
#include "grid_view.h"
#include "../lib/resources.h"
#include <zen/file_handling.h>
+#include <zen/serialize.h>
#include <zen/file_id.h>
#include <zen/recycler.h>
#include "../lib/resolve_path.h"
@@ -43,7 +44,6 @@
#include <wx+/toggle_button.h>
#include "folder_pair.h"
#include <wx+/rtl.h>
-#include <wx+/serialize.h>
#include "search.h"
#include "../lib/help_provider.h"
#include "batch_config.h"
@@ -54,6 +54,7 @@
#include <wx+/image_tools.h>
#include <wx+/no_flicker.h>
#include <wx+/grid.h>
+#include "../lib/error_log.h"
using namespace zen;
using namespace std::rel_ops;
@@ -91,32 +92,18 @@ public:
DirectoryNameMainImpl(MainDialog& mainDlg,
wxWindow& dropWindow1,
Grid& dropGrid,
- size_t compPos, //accept left or right half only!
wxDirPickerCtrl& dirPicker,
FolderHistoryBox& dirName,
wxStaticText& staticText) :
DirectoryName(dropWindow1, dirPicker, dirName, &staticText, &dropGrid.getMainWin()),
mainDlg_(mainDlg),
- dropGrid_(dropGrid),
- compPos_(compPos) {}
+ dropGrid_(dropGrid) {}
virtual bool acceptDrop(const std::vector<wxString>& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd)
{
if (droppedFiles.empty())
return false;
- if (&wnd == &dropGrid_.getMainWin())
- {
- const wxPoint absPos = dropGrid_.CalcUnscrolledPosition(clientPos);
-
- const Opt<std::pair<ColumnType, size_t>> colInfo = dropGrid_.Grid::getColumnAtPos(absPos.x);
- const bool dropOnLeft = colInfo ? colInfo->second != gridview::COMP_RIGHT : false;
-
- if ((compPos_ == gridview::COMP_LEFT && !dropOnLeft) || //accept left or right half of m_gridMain only!
- (compPos_ == gridview::COMP_RIGHT && dropOnLeft)) //
- return false;
- }
-
switch (xmlAccess::getMergeType(toZ(droppedFiles))) //throw()
{
case xmlAccess::MERGE_BATCH:
@@ -140,7 +127,6 @@ private:
MainDialog& mainDlg_;
Grid& dropGrid_;
- size_t compPos_;
};
//------------------------------------------------------------------
@@ -240,15 +226,13 @@ public:
//prepare drag & drop
dirNameLeft(mainDialog,
*mainDialog.m_panelTopLeft,
- *mainDialog.m_gridMain,
- gridview::COMP_LEFT,
+ *mainDialog.m_gridMainL,
*mainDialog.m_dirPickerLeft,
*mainDialog.m_directoryLeft,
*mainDialog.m_staticTextFinalPathLeft),
dirNameRight(mainDialog,
*mainDialog.m_panelTopRight,
- *mainDialog.m_gridMain,
- gridview::COMP_RIGHT,
+ *mainDialog.m_gridMainR,
*mainDialog.m_dirPickerRight,
*mainDialog.m_directoryRight,
*mainDialog.m_staticTextFinalPathRight) {}
@@ -337,7 +321,7 @@ public:
paneInfo.IsFloating())
return false; //prevent main dialog move
- return true;; //allow dialog move
+ return true; //allow dialog move
}
private:
@@ -347,7 +331,7 @@ private:
//##################################################################################################################################
-MainDialog::MainDialog(const std::vector<wxString>& cfgFileNames, xmlAccess::XmlGlobalSettings& settings) :
+MainDialog::MainDialog(const std::vector<wxString>& cfgFileNames, const xmlAccess::XmlGlobalSettings& globalSettings) :
MainDialogGenerated(nullptr)
{
xmlAccess::XmlGuiConfig guiCfg; //structure to receive gui settings, already defaulted!!
@@ -357,7 +341,7 @@ MainDialog::MainDialog(const std::vector<wxString>& cfgFileNames, xmlAccess::Xml
filenames = cfgFileNames;
else //next: use last used selection
{
- filenames = settings.gui.lastUsedConfigFiles; //2. now try last used files
+ filenames = globalSettings.gui.lastUsedConfigFiles; //2. now try last used files
//------------------------------------------------------------------------------------------
//check existence of all directories in parallel!
@@ -403,7 +387,7 @@ MainDialog::MainDialog(const std::vector<wxString>& cfgFileNames, xmlAccess::Xml
const bool startComparisonImmediately = !cfgFileNames.empty() && loadCfgSuccess;
init(guiCfg,
- settings,
+ globalSettings,
startComparisonImmediately);
setLastUsedConfig(filenames, loadCfgSuccess ? guiCfg : xmlAccess::XmlGuiConfig()); //simulate changed config on parsing errors
@@ -412,12 +396,12 @@ MainDialog::MainDialog(const std::vector<wxString>& cfgFileNames, xmlAccess::Xml
MainDialog::MainDialog(const std::vector<wxString>& referenceFiles,
const xmlAccess::XmlGuiConfig& guiCfg,
- xmlAccess::XmlGlobalSettings& settings,
+ const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison) :
MainDialogGenerated(nullptr)
{
init(guiCfg,
- settings,
+ globalSettings,
startComparison);
setLastUsedConfig(referenceFiles, guiCfg);
@@ -426,40 +410,44 @@ MainDialog::MainDialog(const std::vector<wxString>& referenceFiles,
MainDialog::~MainDialog()
{
- wxWindowUpdateLocker dummy(this);
-
- writeGlobalSettings(); //set before saving last used config since "activeConfigFiles" will be replaced
+ try //save "GlobalSettings.xml"
+ {
+ xmlAccess::writeConfig(getGlobalCfgBeforeExit()); //throw FfsXmlError
+ }
+ catch (const xmlAccess::FfsXmlError& e)
+ {
+ wxMessageBox(e.toString().c_str(), _("Error"), wxOK | wxICON_ERROR, this);
+ }
- //save "LastRun.ffs_gui" configuration
- const xmlAccess::XmlGuiConfig guiCfg = getConfig();
- try
+ try //save "LastRun.ffs_gui"
{
- xmlAccess::writeConfig(guiCfg, toZ(lastRunConfigName()));
- //setLastUsedConfig(lastRunConfigName(), guiCfg); -> may be removed!?
+ xmlAccess::writeConfig(getConfig(), toZ(lastRunConfigName())); //throw FfsXmlError
}
- //don't annoy users on read-only drives: no error checking should be fine since this is not a config the user explicitly wanted to save
+ //don't annoy users on read-only drives: it's enough to show a single error message when saving global config
catch (const xmlAccess::FfsXmlError&) {}
//important! event source wxTheApp is NOT dependent on this instance -> disconnect!
wxTheApp->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this);
wxTheApp->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnGlobalKeyEvent), nullptr, this);
- //no need for wxEventHandler::Disconnect() here; event sources are components of this window and are destroyed, too
-
auiMgr.UnInit();
+
+ //no need for wxEventHandler::Disconnect() here; event sources are components of this window and are destroyed, too
}
void MainDialog::onQueryEndSession()
{
- writeGlobalSettings();
+ try { xmlAccess::writeConfig(getGlobalCfgBeforeExit()); }
+ catch (const xmlAccess::FfsXmlError&) {} //we try our best do to something useful in this extreme situation - no reason to notify or even log errors here!
+
try { xmlAccess::writeConfig(getConfig(), toZ(lastRunConfigName())); }
catch (const xmlAccess::FfsXmlError&) {}
}
void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg,
- xmlAccess::XmlGlobalSettings& settings,
+ const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison)
{
showSyncAction_ = false;
@@ -470,6 +458,10 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg,
m_directoryLeft ->init(folderHistoryLeft);
m_directoryRight->init(folderHistoryRight);
+ //setup sash: detach + reparent:
+ m_splitterMain->SetSizer(nullptr); //alas wxFormbuilder doesn't allow us to have child windows without a sizer, so we have to remove it here
+ m_splitterMain->setupWindows(m_gridMainL, m_gridMainC, m_gridMainR);
+
wxWindowUpdateLocker dummy(this); //avoid display distortion
//---------------- support for dockable gui style --------------------------------
@@ -532,23 +524,30 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg,
//----------------------------------------------------------------------------------
//register context: quick variant selection
- m_bpButtonCmpConfig ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnCompSettingsContext), nullptr, this);
- m_bpButtonSyncConfig->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnSyncSettingsContext), nullptr, this);
+ m_bpButtonCmpConfig ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler (MainDialog::OnCompSettingsContext), nullptr, this);
+ m_bpButtonSyncConfig->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler (MainDialog::OnSyncSettingsContext), nullptr, this);
m_bpButtonFilter ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(MainDialog::OnGlobalFilterContext), nullptr, this);
//sort grids
- m_gridMain->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClick ), nullptr, this );
- m_gridMain->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContext), nullptr, this );
+ m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickL ), nullptr, this );
+ m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickC ), nullptr, this );
+ m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridClickEventHandler(MainDialog::onGridLabelLeftClickR ), nullptr, this );
+
+ m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this );
+ m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this );
+ m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this );
//grid context menu
- m_gridMain->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContext), nullptr, this);
- m_gridNavi->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onNaviGridContext), nullptr, this);
+ m_gridMainL->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextL), nullptr, this);
+ m_gridMainC->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextC), nullptr, this);
+ m_gridMainR->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextR), nullptr, this);
+ m_gridNavi ->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onNaviGridContext ), nullptr, this);
- m_gridMain->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClick), nullptr, this );
+ m_gridMainL->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickL), nullptr, this );
+ m_gridMainR->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickR), nullptr, this );
m_gridNavi->Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(MainDialog::onNaviSelection), nullptr, this);
- globalSettings = &settings;
gridDataView.reset(new zen::GridView);
treeDataView.reset(new zen::TreeView);
@@ -570,11 +569,11 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg,
initViewFilterButtons();
//init grid settings
- gridview::init(*m_gridMain, gridDataView);
+ gridview::init(*m_gridMainL, *m_gridMainC, *m_gridMainR, gridDataView);
treeview::init(*m_gridNavi, treeDataView);
//initialize and load configuration
- readGlobalSettings();
+ setGlobalCfgOnInit(globalSettings);
setConfig(guiCfg);
//set icons for this dialog
@@ -645,7 +644,10 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg,
});
//support for CTRL + C and DEL on grids
- m_gridMain->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEvent), nullptr, this);
+ m_gridMainL->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventL), nullptr, this);
+ m_gridMainC->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventC), nullptr, this);
+ m_gridMainR->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridButtonEventR), nullptr, this);
+
m_gridNavi->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onTreeButtonEvent), nullptr, this);
//register global hotkeys (without explicit menu entry)
@@ -674,8 +676,8 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg,
OnResizeStatisticsPanel(dummy3); //
//event handler for manual (un-)checking of rows and setting of sync direction
- m_gridMain->Connect(EVENT_GRID_CHECK_ROWS, CheckRowsEventHandler (MainDialog::onCheckRows), nullptr, this);
- m_gridMain->Connect(EVENT_GRID_SYNC_DIRECTION, SyncDirectionEventHandler(MainDialog::onSetSyncDirection), nullptr, this);
+ m_gridMainC->Connect(EVENT_GRID_CHECK_ROWS, CheckRowsEventHandler (MainDialog::onCheckRows), nullptr, this);
+ m_gridMainC->Connect(EVENT_GRID_SYNC_DIRECTION, SyncDirectionEventHandler(MainDialog::onSetSyncDirection), nullptr, this);
//mainly to update row label sizes...
updateGui();
@@ -740,41 +742,46 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig& guiCfg,
}
-void MainDialog::readGlobalSettings()
+void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings)
{
+ globalCfg = globalSettings;
+
+ setLanguage(globalSettings.programLanguage);
+
//set dialog size and position: test ALL parameters at once, since width/height are invalid if the window is minimized (eg x,y == -32000; height = 28, width = 160)
//note: negative values for x and y are possible when using multiple monitors!
- if (globalSettings->gui.dlgSize.GetWidth () > 0 &&
- globalSettings->gui.dlgSize.GetHeight() > 0 &&
- globalSettings->gui.dlgPos.x >= -3360 &&
- globalSettings->gui.dlgPos.y >= -200)
- //wxDisplay::GetFromPoint(globalSettings->gui.dlgPos) != wxNOT_FOUND) //make sure upper left corner is in visible view -> not required
- SetSize(wxRect(globalSettings->gui.dlgPos, globalSettings->gui.dlgSize));
+ if (globalSettings.gui.dlgSize.GetWidth () > 0 &&
+ globalSettings.gui.dlgSize.GetHeight() > 0 &&
+ globalSettings.gui.dlgPos.x >= -3360 &&
+ globalSettings.gui.dlgPos.y >= -200)
+ //wxDisplay::GetFromPoint(globalSettings.gui.dlgPos) != wxNOT_FOUND) //make sure upper left corner is in visible view -> not required
+ SetSize(wxRect(globalSettings.gui.dlgPos, globalSettings.gui.dlgSize));
else
Centre();
- Maximize(globalSettings->gui.isMaximized);
+ Maximize(globalSettings.gui.isMaximized);
//set column attributes
- m_gridMain->setColumnConfig(gridview::convertConfig(globalSettings->gui.columnAttribLeft), gridview::COMP_LEFT);
- m_gridMain->setColumnConfig(gridview::convertConfig(globalSettings->gui.columnAttribRight), gridview::COMP_RIGHT);
+ m_gridMainL ->setColumnConfig(gridview::convertConfig(globalSettings.gui.columnAttribLeft));
+ m_gridMainR ->setColumnConfig(gridview::convertConfig(globalSettings.gui.columnAttribRight));
+ m_splitterMain->setSashOffset(globalSettings.gui.sashOffset);
- m_gridNavi->setColumnConfig(treeview::convertConfig(globalSettings->gui.columnAttribNavi));
- treeview::setShowPercentage(*m_gridNavi, globalSettings->gui.showPercentBar);
+ m_gridNavi->setColumnConfig(treeview::convertConfig(globalSettings.gui.columnAttribNavi));
+ treeview::setShowPercentage(*m_gridNavi, globalSettings.gui.showPercentBar);
- treeDataView->setSortDirection(globalSettings->gui.naviLastSortColumn, globalSettings->gui.naviLastSortAscending);
+ treeDataView->setSortDirection(globalSettings.gui.naviLastSortColumn, globalSettings.gui.naviLastSortAscending);
//load list of last used configuration files
- std::vector<wxString> cfgFileNames = globalSettings->gui.cfgFileHistory;
+ std::vector<wxString> cfgFileNames = globalSettings.gui.cfgFileHistory;
cfgFileNames.push_back(lastRunConfigName()); //make sure <Last session> is always part of history list
addFileToCfgHistory(cfgFileNames);
//load list of last used folders
- *folderHistoryLeft = FolderHistory(globalSettings->gui.folderHistoryLeft, globalSettings->gui.folderHistMax);
- *folderHistoryRight = FolderHistory(globalSettings->gui.folderHistoryRight, globalSettings->gui.folderHistMax);
+ *folderHistoryLeft = FolderHistory(globalSettings.gui.folderHistoryLeft, globalSettings.gui.folderHistMax);
+ *folderHistoryRight = FolderHistory(globalSettings.gui.folderHistoryRight, globalSettings.gui.folderHistMax);
//show/hide file icons
- gridview::setupIcons(*m_gridMain, globalSettings->gui.showIcons, convert(globalSettings->gui.iconSize));
+ gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.showIcons, convert(globalSettings.gui.iconSize));
//------------------------------------------------------------------------------------------------
//wxAuiManager erroneously loads panel captions, we don't want that
@@ -784,7 +791,7 @@ void MainDialog::readGlobalSettings()
for (size_t i = 0; i < paneArray.size(); ++i)
captionNameMap.push_back(std::make_pair(paneArray[i].caption, paneArray[i].name));
- auiMgr.LoadPerspective(globalSettings->gui.guiPerspectiveLast);
+ auiMgr.LoadPerspective(globalSettings.gui.guiPerspectiveLast);
//restore original captions
for (CaptionNameMapping::const_iterator i = captionNameMap.begin(); i != captionNameMap.end(); ++i)
@@ -793,30 +800,38 @@ void MainDialog::readGlobalSettings()
}
-void MainDialog::writeGlobalSettings()
+xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
{
+ Freeze(); //no need to Thaw() again!!
+ // wxWindowUpdateLocker dummy(this);
+
+ xmlAccess::XmlGlobalSettings globalSettings = globalCfg;
+
+ globalSettings.programLanguage = getLanguage();
+
//write global settings to (global) variable stored in application instance
if (IsIconized()) //we need to (reliably) retrieve non-iconized, non-maximized size and position
Iconize(false);
- globalSettings->gui.isMaximized = IsMaximized(); //evaluate AFTER uniconizing!
+ globalSettings.gui.isMaximized = IsMaximized(); //evaluate AFTER uniconizing!
if (IsMaximized())
Maximize(false);
- globalSettings->gui.dlgSize = GetSize();
- globalSettings->gui.dlgPos = GetPosition();
+ globalSettings.gui.dlgSize = GetSize();
+ globalSettings.gui.dlgPos = GetPosition();
//retrieve column attributes
- globalSettings->gui.columnAttribLeft = gridview::convertConfig(m_gridMain->getColumnConfig(gridview::COMP_LEFT));
- globalSettings->gui.columnAttribRight = gridview::convertConfig(m_gridMain->getColumnConfig(gridview::COMP_RIGHT));
+ globalSettings.gui.columnAttribLeft = gridview::convertConfig(m_gridMainL->getColumnConfig());
+ globalSettings.gui.columnAttribRight = gridview::convertConfig(m_gridMainR->getColumnConfig());
+ globalSettings.gui.sashOffset = m_splitterMain->getSashOffset();
- globalSettings->gui.columnAttribNavi = treeview::convertConfig(m_gridNavi->getColumnConfig());
- globalSettings->gui.showPercentBar = treeview::getShowPercentage(*m_gridNavi);
+ globalSettings.gui.columnAttribNavi = treeview::convertConfig(m_gridNavi->getColumnConfig());
+ globalSettings.gui.showPercentBar = treeview::getShowPercentage(*m_gridNavi);
- const auto sortInfo = treeDataView->getSortDirection();
- globalSettings->gui.naviLastSortColumn = sortInfo.first;
- globalSettings->gui.naviLastSortAscending = sortInfo.second;
+ const std::pair<ColumnTypeNavi, bool> sortInfo = treeDataView->getSortDirection();
+ globalSettings.gui.naviLastSortColumn = sortInfo.first;
+ globalSettings.gui.naviLastSortAscending = sortInfo.second;
//write list of last used configuration files
std::vector<wxString> cfgFileHistory;
@@ -824,14 +839,16 @@ void MainDialog::writeGlobalSettings()
if (auto clientString = dynamic_cast<wxClientDataString*>(m_listBoxHistory->GetClientObject(i)))
cfgFileHistory.push_back(clientString->name_);
- globalSettings->gui.cfgFileHistory = cfgFileHistory;
- globalSettings->gui.lastUsedConfigFiles = activeConfigFiles;
+ globalSettings.gui.cfgFileHistory = cfgFileHistory;
+ globalSettings.gui.lastUsedConfigFiles = activeConfigFiles;
//write list of last used folders
- globalSettings->gui.folderHistoryLeft = folderHistoryLeft ->getList();
- globalSettings->gui.folderHistoryRight = folderHistoryRight->getList();
+ globalSettings.gui.folderHistoryLeft = folderHistoryLeft ->getList();
+ globalSettings.gui.folderHistoryRight = folderHistoryRight->getList();
+
+ globalSettings.gui.guiPerspectiveLast = auiMgr.SavePerspective();
- globalSettings->gui.guiPerspectiveLast = auiMgr.SavePerspective();
+ return globalSettings;
}
@@ -877,16 +894,15 @@ void MainDialog::copySelectionToClipboard()
{
zxString clipboardString; //perf: wxString doesn't model exponential growth and so is out
- auto addSelection = [&](size_t compPos)
+ auto addSelection = [&](const Grid& grid)
{
- auto prov = m_gridMain->getDataProvider(compPos);
- if (prov)
+ if (auto prov = grid.getDataProvider())
{
- std::vector<Grid::ColumnAttribute> colAttr = m_gridMain->getColumnConfig(compPos);
+ std::vector<Grid::ColumnAttribute> colAttr = grid.getColumnConfig();
vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
if (!colAttr.empty())
{
- const std::vector<size_t> selection = m_gridMain->getSelectedRows(compPos);
+ const std::vector<size_t> selection = grid.getSelectedRows();
std::for_each(selection.begin(), selection.end(),
[&](size_t row)
{
@@ -903,8 +919,8 @@ void MainDialog::copySelectionToClipboard()
}
};
- addSelection(gridview::COMP_LEFT);
- addSelection(gridview::COMP_RIGHT);
+ addSelection(*m_gridMainL);
+ addSelection(*m_gridMainR);
//finally write to clipboard
if (!clipboardString.empty())
@@ -920,17 +936,17 @@ std::vector<FileSystemObject*> MainDialog::getGridSelection(bool fromLeft, bool
{
std::set<size_t> selectedRows;
- auto addSelection = [&](size_t compPos)
+ auto addSelection = [&](const Grid& grid)
{
- const std::vector<size_t>& sel = m_gridMain->getSelectedRows(compPos);
+ const std::vector<size_t>& sel = grid.getSelectedRows();
selectedRows.insert(sel.begin(), sel.end());
};
if (fromLeft)
- addSelection(gridview::COMP_LEFT);
+ addSelection(*m_gridMainL);
if (fromRight)
- addSelection(gridview::COMP_RIGHT);
+ addSelection(*m_gridMainR);
return gridDataView->getAllFileRef(selectedRows);
}
@@ -997,18 +1013,16 @@ public:
mainDlg->enableAllElements();
}
- virtual Response reportError(const std::wstring& errorMessage)
+ virtual Response reportError(const std::wstring& msg)
{
- if (abortRequested)
- throw AbortDeleteProcess();
-
if (ignoreErrors)
return DeleteFilesHandler::IGNORE_ERROR;
+ updateGUI();
bool ignoreNextErrors = false;
switch (showErrorDlg(mainDlg,
ReturnErrorDlg::BUTTON_IGNORE | ReturnErrorDlg::BUTTON_RETRY | ReturnErrorDlg::BUTTON_CANCEL,
- errorMessage, &ignoreNextErrors))
+ msg, &ignoreNextErrors))
{
case ReturnErrorDlg::BUTTON_IGNORE:
ignoreErrors = ignoreNextErrors;
@@ -1023,10 +1037,35 @@ public:
return DeleteFilesHandler::IGNORE_ERROR; //dummy return value
}
+ virtual void reportWarning(const std::wstring& msg, bool& warningActive)
+ {
+ if (!warningActive || ignoreErrors)
+ return;
+
+ updateGUI();
+ bool dontWarnAgain = false;
+ switch (showWarningDlg(mainDlg, ReturnWarningDlg::BUTTON_IGNORE | ReturnWarningDlg::BUTTON_CANCEL, msg, dontWarnAgain))
+ {
+ case ReturnWarningDlg::BUTTON_SWITCH:
+ assert(false);
+ case ReturnWarningDlg::BUTTON_CANCEL:
+ throw AbortDeleteProcess();
+
+ case ReturnWarningDlg::BUTTON_IGNORE:
+ warningActive = !dontWarnAgain;
+ break;
+ }
+ }
+
virtual void notifyDeletion(const Zstring& currentObject) //called for each file/folder that has been deleted
{
++deletionCount;
+ updateGUI();
+ }
+private:
+ void updateGUI()
+ {
if (updateUiIsAllowed()) //test if specific time span between ui updates is over
{
mainDlg->setStatusInformation(replaceCpy(_P("Object deleted successfully!", "%x objects deleted successfully!", deletionCount),
@@ -1038,7 +1077,7 @@ public:
throw AbortDeleteProcess();
}
-private:
+ //context: C callstack message loop => throw()!
void OnAbortDeletion(wxCommandEvent& event) //handle abort button click
{
abortRequested = true; //don't throw exceptions in a GUI-Callback!!! (throw zen::AbortThisProcess())
@@ -1056,7 +1095,6 @@ private:
event.Skip();
}
-
MainDialog* const mainDlg;
bool abortRequested;
@@ -1076,8 +1114,8 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
if (zen::showDeleteDialog(this,
selectionLeft,
selectionRight,
- globalSettings->gui.deleteOnBothSides,
- globalSettings->gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY)
+ globalCfg.gui.deleteOnBothSides,
+ globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY)
{
try
{
@@ -1088,9 +1126,12 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
selectionRight,
folderCmp,
extractDirectionCfg(getConfig().mainCfg),
- globalSettings->gui.deleteOnBothSides,
- globalSettings->gui.useRecyclerForManualDeletion,
- statusHandler);
+ globalCfg.gui.deleteOnBothSides,
+ globalCfg.gui.useRecyclerForManualDeletion,
+ statusHandler,
+ globalCfg.optDialogs.warningRecyclerMissing);
+
+ gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR); //do not clear, if aborted!
}
catch (AbortDeleteProcess&) {}
@@ -1099,8 +1140,6 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
//redraw grid neccessary to update new dimensions and for UI-Backend data linkage
updateGui(); //call immediately after deleteFromGridAndHD!!!
-
- gridview::clearSelection(*m_gridMain);
}
}
}
@@ -1118,26 +1157,11 @@ wxString extractLastValidDir(const FileSystemObject& fsObj)
}
-void MainDialog::openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, size_t compPos) //fsObj may be nullptr
+void MainDialog::openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, bool leftSide) //fsObj may be nullptr
{
if (commandline.empty())
return;
- bool leftSide = true;
- switch (compPos)
- {
- case gridview::COMP_LEFT:
- break;
- case gridview::COMP_MIDDLE:
- return; //we don't want to start external apps when double-clicking here!
- case gridview::COMP_RIGHT:
- leftSide = false;
- break;
- default: //area to the right of main grid
- leftSide = false;
- fsObj = nullptr; //do not evaluate row when outside grid!
- }
-
wxString name;
wxString nameCo;
wxString dir;
@@ -1275,8 +1299,9 @@ void MainDialog::disableAllElements(bool enableAbort)
m_panelConfig ->Disable();
m_bpButtonSyncConfig ->Disable();
m_buttonStartSync ->Disable();
- m_gridMain ->Disable();
- m_gridMain ->Disable();
+ m_gridMainL ->Disable();
+ m_gridMainC ->Disable();
+ m_gridMainR ->Disable();
m_panelStatistics ->Disable();
m_gridNavi ->Disable();
m_panelDirectoryPairs->Disable();
@@ -1311,8 +1336,9 @@ void MainDialog::enableAllElements()
m_panelConfig ->Enable();
m_bpButtonSyncConfig ->Enable();
m_buttonStartSync ->Enable();
- m_gridMain ->Enable();
- m_gridMain ->Enable();
+ m_gridMainL ->Enable();
+ m_gridMainC ->Enable();
+ m_gridMainR ->Enable();
m_panelStatistics ->Enable();
m_gridNavi ->Enable();
m_panelDirectoryPairs->Enable();
@@ -1402,7 +1428,7 @@ void MainDialog::OnResizeFolderPairs(wxEvent& event)
void MainDialog::onTreeButtonEvent(wxKeyEvent& event)
{
int keyCode = event.GetKeyCode();
- if (m_gridMain->GetLayoutDirection() == wxLayout_RightToLeft)
+ if (m_gridNavi->GetLayoutDirection() == wxLayout_RightToLeft)
{
if (keyCode == WXK_LEFT)
keyCode = WXK_RIGHT;
@@ -1459,10 +1485,23 @@ void MainDialog::onTreeButtonEvent(wxKeyEvent& event)
}
-void MainDialog::onGridButtonEvent(wxKeyEvent& event)
+void MainDialog::onGridButtonEventL(wxKeyEvent& event)
+{
+ onGridButtonEvent(event, *m_gridMainL, true);
+}
+void MainDialog::onGridButtonEventC(wxKeyEvent& event)
+{
+ onGridButtonEvent(event, *m_gridMainC, true);
+}
+void MainDialog::onGridButtonEventR(wxKeyEvent& event)
+{
+ onGridButtonEvent(event, *m_gridMainR, false);
+}
+
+void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide)
{
int keyCode = event.GetKeyCode();
- if (m_gridMain->GetLayoutDirection() == wxLayout_RightToLeft)
+ if (grid.GetLayoutDirection() == wxLayout_RightToLeft)
{
if (keyCode == WXK_LEFT)
keyCode = WXK_RIGHT;
@@ -1488,19 +1527,19 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event)
{
case WXK_NUMPAD_LEFT:
case WXK_LEFT: //ALT + <-
- setSyncDirManually(getGridSelection(), zen::SYNC_DIR_LEFT);
+ setSyncDirManually(getGridSelection(), SYNC_DIR_LEFT);
return;
case WXK_NUMPAD_RIGHT:
case WXK_RIGHT: //ALT + ->
- setSyncDirManually(getGridSelection(), zen::SYNC_DIR_RIGHT);
+ setSyncDirManually(getGridSelection(), SYNC_DIR_RIGHT);
return;
case WXK_NUMPAD_UP:
case WXK_NUMPAD_DOWN:
case WXK_UP: /* ALT + /|\ */
case WXK_DOWN: /* ALT + \|/ */
- setSyncDirManually(getGridSelection(), zen::SYNC_DIR_NONE);
+ setSyncDirManually(getGridSelection(), SYNC_DIR_NONE);
return;
}
@@ -1524,13 +1563,12 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event)
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
- if (!globalSettings->gui.externelApplications.empty())
+ if (!globalCfg.gui.externelApplications.empty())
{
- const wxString commandline = globalSettings->gui.externelApplications[0].second; //open with first external application
- auto cursorPos = m_gridMain->getGridCursor();
- const size_t row = cursorPos.first;
- const size_t compPos = cursorPos.second;
- openExternalApplication(commandline, gridDataView->getObject(row), compPos);
+ const wxString commandline = globalCfg.gui.externelApplications[0].second; //open with first external application
+ auto cursorPos = grid.getGridCursor();
+ const size_t row = cursorPos.first;
+ openExternalApplication(commandline, gridDataView->getObject(row), leftSide);
}
return;
}
@@ -1552,11 +1590,13 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
{
//avoid recursion!!! -> this ugly construct seems to be the only (portable) way to avoid re-entrancy
//recursion may happen in multiple situations: e.g. modal dialogs, wxGrid::ProcessEvent()!
- if (processingGlobalKeyEvent ||
- !IsShown() ||
- !IsActive() ||
- !IsEnabled() || //only handle if main window is in use
- !m_gridMain->IsEnabled()) //
+ if (processingGlobalKeyEvent ||
+ !IsShown() ||
+ !IsActive() ||
+ !IsEnabled() ||
+ !m_gridMainL->IsEnabled() || //
+ !m_gridMainC->IsEnabled() || //only handle if main window is in use
+ !m_gridMainR->IsEnabled()) //
{
event.Skip();
return;
@@ -1573,7 +1613,7 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
switch (keyCode)
{
case 'F': //CTRL + F
- zen::startFind(this, *m_gridMain, gridview::COMP_LEFT, gridview::COMP_RIGHT, globalSettings->gui.textSearchRespectCase);
+ zen::startFind(this, *m_gridMainL, *m_gridMainR, globalCfg.gui.textSearchRespectCase);
return; //-> swallow event!
}
@@ -1581,7 +1621,7 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
{
case WXK_F3: //F3
case WXK_NUMPAD_F3: //
- zen::findNext(this, *m_gridMain, gridview::COMP_LEFT, gridview::COMP_RIGHT, globalSettings->gui.textSearchRespectCase);
+ zen::findNext(this, *m_gridMainL, *m_gridMainR, globalCfg.gui.textSearchRespectCase);
return; //-> swallow event!
case WXK_F8: //F8
@@ -1608,15 +1648,17 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
case WXK_NUMPAD_END:
{
const wxWindow* focus = wxWindow::FindFocus();
- if (!isPartOf(focus, m_gridMain ) && //don't propagate keyboard commands if grid is already in focus
+ 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_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_gridMain->GetEventHandler())
+ if (wxEvtHandler* evtHandler = m_gridMainL->GetEventHandler())
{
- m_gridMain->SetFocus();
+ m_gridMainL->SetFocus();
evtHandler->ProcessEvent(event); //propagating event catched at wxTheApp to child leads to recursion, but we prevented it...
event.Skip(false); //definitively handled now!
return;
@@ -1648,7 +1690,16 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
}
if (leadRow >= 0)
- m_gridMain->scrollTo(std::max(0, leadRow - 1)); //scroll one more row
+ {
+ leadRow =std::max(0, leadRow - 1); //scroll one more row
+
+ m_gridMainL->scrollTo(leadRow);
+ m_gridMainC->scrollTo(leadRow);
+ m_gridMainR->scrollTo(leadRow);
+
+ m_gridNavi->getMainWin().Update(); //draw cursor immediately rather than on next idle event (required for slow CPUs, netbook)
+
+ }
//get selection on navigation tree and set corresponding markers on main grid
std::vector<const HierarchyObject*> markedFiles; //mark files/symlinks directly within a container
@@ -1669,7 +1720,7 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
}
});
- gridview::setNavigationMarker(*m_gridMain, std::move(markedFiles), std::move(markedContainer));
+ gridview::setNavigationMarker(*m_gridMainL, std::move(markedFiles), std::move(markedContainer));
event.Skip();
}
@@ -1740,138 +1791,145 @@ void MainDialog::onNaviGridContext(GridClickEvent& event)
}
-void MainDialog::onMainGridContext(GridClickEvent& event)
+void MainDialog::onMainGridContextC(GridClickEvent& event)
{
- const auto& selection = getGridSelection(); //referenced by lambdas!
ContextMenu menu;
- switch (event.compPos_)
+ menu.addItem(_("Include all"), [&]
{
- case gridview::COMP_MIDDLE:
- menu.addItem(_("Include all"), [&]
- {
- zen::setActiveStatus(true, folderCmp);
- updateGui();
- }, nullptr, gridDataView->rowsTotal() > 0);
+ zen::setActiveStatus(true, folderCmp);
+ updateGui();
+ }, nullptr, gridDataView->rowsTotal() > 0);
- menu.addItem(_("Exclude all"), [&]
- {
- zen::setActiveStatus(false, folderCmp);
- updateGuiAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true
- }, nullptr, gridDataView->rowsTotal() > 0);
- break;
-
- case gridview::COMP_LEFT:
- case gridview::COMP_RIGHT:
- default: //area to the right of main grid
- //----------------------------------------------------------------------------------------------------
- if (showSyncAction_ && !selection.empty())
- {
- auto getImage = [&](SyncDirection dir, SyncOperation soDefault)
- {
- return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ?
- selection[0]->testSyncOperation(dir) : soDefault));
- };
- const wxBitmap opRight = getImage(SYNC_DIR_RIGHT, SO_OVERWRITE_RIGHT);
- const wxBitmap opNone = getImage(SYNC_DIR_NONE, SO_DO_NOTHING );
- const wxBitmap opLeft = getImage(SYNC_DIR_LEFT, SO_OVERWRITE_LEFT );
-
- wxString shortCutLeft = L"\tAlt+Left";
- wxString shortCutRight = L"\tAlt+Right";
- if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
- std::swap(shortCutLeft, shortCutRight);
-
- menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight);
- menu.addItem(_("Set direction:") + L" -" L"\tAlt+Up", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone);
- menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft);
- //Gtk needs a direction, "<-", because it has no context menu icons!
- //Gtk requires "no spaces" for shortcut identifiers!
- menu.addSeparator();
- }
- //----------------------------------------------------------------------------------------------------
- if (!selection.empty())
- {
- if (selection[0]->isActive())
- menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, false); }, &GlobalResources::getImage(L"checkboxFalse"));
- else
- menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, true); }, &GlobalResources::getImage(L"checkboxTrue"));
- }
- else
- menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false);
+ menu.addItem(_("Exclude all"), [&]
+ {
+ zen::setActiveStatus(false, folderCmp);
+ updateGuiAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true
+ }, nullptr, gridDataView->rowsTotal() > 0);
- //----------------------------------------------------------------------------------------------------
- //EXCLUDE FILTER
- if (selection.size() == 1)
- {
- ContextMenu submenu;
+ menu.popup(*this);
+}
- //by extension
- if (dynamic_cast<const DirMapping*>(selection[0]) == nullptr) //non empty && no directory
- {
- const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR);
- if (contains(filename, Zchar('.'))) //be careful: AfterLast would return the whole string if '.' were not found!
- {
- const Zstring extension = afterLast(filename, Zchar('.'));
+void MainDialog::onMainGridContextL(GridClickEvent& event)
+{
+ onMainGridContextRim(true);
+}
- submenu.addItem(L"*." + utfCvrtTo<wxString>(extension),
- [this, extension] { excludeExtension(extension); });
- }
- }
+void MainDialog::onMainGridContextR(GridClickEvent& event)
+{
+ onMainGridContextRim(false);
+}
- //by short name
- submenu.addItem(utfCvrtTo<wxString>(Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName()),
- [this, &selection] { excludeShortname(*selection[0]); });
+void MainDialog::onMainGridContextRim(bool leftSide)
+{
+ const auto& selection = getGridSelection(); //referenced by lambdas!
+ ContextMenu menu;
- //by relative path
- submenu.addItem(utfCvrtTo<wxString>(FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()),
- [this, &selection] { excludeItems(selection); });
+ if (showSyncAction_ && !selection.empty())
+ {
+ auto getImage = [&](SyncDirection dir, SyncOperation soDefault)
+ {
+ return mirrorIfRtl(getSyncOpImage(selection[0]->getSyncOperation() != SO_EQUAL ?
+ selection[0]->testSyncOperation(dir) : soDefault));
+ };
+ const wxBitmap opRight = getImage(SYNC_DIR_RIGHT, SO_OVERWRITE_RIGHT);
+ const wxBitmap opNone = getImage(SYNC_DIR_NONE, SO_DO_NOTHING );
+ const wxBitmap opLeft = getImage(SYNC_DIR_LEFT, SO_OVERWRITE_LEFT );
- menu.addSubmenu(_("Exclude via filter:"), submenu, &GlobalResources::getImage(L"filterSmall"));
- }
- else if (selection.size() > 1)
+ wxString shortCutLeft = L"\tAlt+Left";
+ wxString shortCutRight = L"\tAlt+Right";
+ if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
+ std::swap(shortCutLeft, shortCutRight);
+
+ menu.addItem(_("Set direction:") + L" ->" + shortCutRight, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_RIGHT); }, &opRight);
+ menu.addItem(_("Set direction:") + L" -" L"\tAlt+Up", [this, &selection] { setSyncDirManually(selection, SYNC_DIR_NONE); }, &opNone);
+ menu.addItem(_("Set direction:") + L" <-" + shortCutLeft, [this, &selection] { setSyncDirManually(selection, SYNC_DIR_LEFT); }, &opLeft);
+ //Gtk needs a direction, "<-", because it has no context menu icons!
+ //Gtk requires "no spaces" for shortcut identifiers!
+ menu.addSeparator();
+ }
+ //----------------------------------------------------------------------------------------------------
+ if (!selection.empty())
+ {
+ if (selection[0]->isActive())
+ menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, false); }, &GlobalResources::getImage(L"checkboxFalse"));
+ else
+ menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setManualFilter(selection, true); }, &GlobalResources::getImage(L"checkboxTrue"));
+ }
+ else
+ menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false);
+
+ //----------------------------------------------------------------------------------------------------
+ //EXCLUDE FILTER
+ if (selection.size() == 1)
+ {
+ ContextMenu submenu;
+
+ //by extension
+ if (dynamic_cast<const DirMapping*>(selection[0]) == nullptr) //non empty && no directory
+ {
+ const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR);
+ if (contains(filename, Zchar('.'))) //be careful: AfterLast would return the whole string if '.' were not found!
{
- //by relative path
- menu.addItem(_("Exclude via filter:") + L" " + _("<multiple selection>"),
- [this, &selection] { excludeItems(selection); }, &GlobalResources::getImage(L"filterSmall"));
+ const Zstring extension = afterLast(filename, Zchar('.'));
+
+ submenu.addItem(L"*." + utfCvrtTo<wxString>(extension),
+ [this, extension] { excludeExtension(extension); });
}
+ }
- //----------------------------------------------------------------------------------------------------
- //CONTEXT_EXTERNAL_APP
- if (!globalSettings->gui.externelApplications.empty())
- {
- menu.addSeparator();
+ //by short name
+ submenu.addItem(utfCvrtTo<wxString>(Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName()),
+ [this, &selection] { excludeShortname(*selection[0]); });
- for (auto iter = globalSettings->gui.externelApplications.begin();
- iter != globalSettings->gui.externelApplications.end();
- ++iter)
- {
- //some trick to translate default external apps on the fly: 1. "open in explorer" 2. "start directly"
- wxString description = zen::implementation::translate(iter->first);
- if (description.empty())
- description = L" "; //wxWidgets doesn't like empty items
+ //by relative path
+ submenu.addItem(utfCvrtTo<wxString>(FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()),
+ [this, &selection] { excludeItems(selection); });
- const wxString command = iter->second;
+ menu.addSubmenu(_("Exclude via filter:"), submenu, &GlobalResources::getImage(L"filterSmall"));
+ }
+ else if (selection.size() > 1)
+ {
+ //by relative path
+ menu.addItem(_("Exclude via filter:") + L" " + _("<multiple selection>"),
+ [this, &selection] { excludeItems(selection); }, &GlobalResources::getImage(L"filterSmall"));
+ }
- auto openApp = [this, &selection, command, event] { openExternalApplication(command, selection.empty() ? nullptr : selection[0], event.compPos_); };
+ //----------------------------------------------------------------------------------------------------
+ //CONTEXT_EXTERNAL_APP
+ if (!globalCfg.gui.externelApplications.empty())
+ {
+ menu.addSeparator();
- if (iter == globalSettings->gui.externelApplications.begin())
- menu.addItem(description + L"\tEnter", openApp);
- else
- menu.addItem(description, openApp, nullptr, !selection.empty());
- }
- }
- //----------------------------------------------------------------------------------------------------
- //CONTEXT_DELETE_FILES
- menu.addSeparator();
+ for (auto iter = globalCfg.gui.externelApplications.begin();
+ iter != globalCfg.gui.externelApplications.end();
+ ++iter)
+ {
+ //some trick to translate default external apps on the fly: 1. "open in explorer" 2. "start directly"
+ wxString description = zen::implementation::translate(iter->first);
+ if (description.empty())
+ description = L" "; //wxWidgets doesn't like empty items
- menu.addItem(_("Delete") + L"\tDel", [this]
- {
- deleteSelectedFiles(
- getGridSelection(true, false),
- getGridSelection(false, true));
- }, nullptr, !selection.empty());
- break;
+ const wxString command = iter->second;
+
+ auto openApp = [this, &selection, leftSide, command] { openExternalApplication(command, selection.empty() ? nullptr : selection[0], leftSide); };
+
+ if (iter == globalCfg.gui.externelApplications.begin())
+ menu.addItem(description + L"\tEnter", openApp);
+ else
+ menu.addItem(description, openApp, nullptr, !selection.empty());
+ }
}
+ //----------------------------------------------------------------------------------------------------
+ //CONTEXT_DELETE_FILES
+ menu.addSeparator();
+
+ menu.addItem(_("Delete") + L"\tDel", [this]
+ {
+ deleteSelectedFiles(
+ getGridSelection(true, false),
+ getGridSelection(false, true));
+ }, nullptr, !selection.empty());
menu.popup(*this);
}
@@ -1951,92 +2009,99 @@ void MainDialog::excludeItems(const std::vector<FileSystemObject*>& selection)
}
-void MainDialog::onGridLabelContext(GridClickEvent& event)
+void MainDialog::onGridLabelContextC(GridClickEvent& event)
{
ContextMenu menu;
+ menu.addItem(_("Category") + L"\tF8", [&] { showSyncAction(false); }, showSyncAction_ ? nullptr : &GlobalResources::getImage(L"compareSmall"));
+ menu.addItem(_("Action"), [&] { showSyncAction(true ); }, showSyncAction_ ? &GlobalResources::getImage(L"syncSmall") : nullptr);
+ menu.popup(*this);
+}
- if (event.compPos_ == gridview::COMP_LEFT ||
- event.compPos_ == gridview::COMP_RIGHT)
- {
- auto toggleColumn = [&](const Grid::ColumnAttribute& ca, size_t compPos)
- {
- auto colAttr = m_gridMain->getColumnConfig(compPos);
- for (auto iter = colAttr.begin(); iter != colAttr.end(); ++iter)
- if (iter->type_ == ca.type_)
- {
- iter->visible_ = !ca.visible_;
- m_gridMain->setColumnConfig(colAttr, compPos);
- return;
- }
- };
+void MainDialog::onGridLabelContextL(GridClickEvent& event)
+{
+ onGridLabelContext(*m_gridMainL, static_cast<ColumnTypeRim>(event.colType_), getDefaultColumnAttributesLeft());
+}
+void MainDialog::onGridLabelContextR(GridClickEvent& event)
+{
+ onGridLabelContext(*m_gridMainR, static_cast<ColumnTypeRim>(event.colType_), getDefaultColumnAttributesRight());
+}
- if (auto prov = m_gridMain->getDataProvider(event.compPos_))
- {
- const auto& colAttr = m_gridMain->getColumnConfig(event.compPos_);
- for (auto iter = colAttr.begin(); iter != colAttr.end(); ++iter)
- {
- const Grid::ColumnAttribute& ca = *iter;
- const size_t compPos = event.compPos_;
- menu.addCheckBox(prov->getColumnLabel(ca.type_), [ca, compPos, toggleColumn] { toggleColumn(ca, compPos); },
- ca.visible_, ca.type_ != static_cast<ColumnType>(COL_TYPE_FILENAME)); //do not allow user to hide file name column!
- }
- }
- //----------------------------------------------------------------------------------------------
- menu.addSeparator();
+void MainDialog::onGridLabelContext(Grid& grid, ColumnTypeRim type, const std::vector<ColumnAttributeRim>& defaultColumnAttributes)
+{
+ ContextMenu menu;
- auto setDefault = [&]
- {
- m_gridMain->setColumnConfig(gridview::convertConfig(event.compPos_ == gridview::COMP_LEFT ?
- getDefaultColumnAttributesLeft() :
- getDefaultColumnAttributesRight()), event.compPos_);
- };
- menu.addItem(_("&Default"), setDefault); //'&' -> reuse text from "default" buttons elsewhere
- //----------------------------------------------------------------------------------------------
- menu.addSeparator();
- menu.addCheckBox(_("Show icons:"), [&]
- {
- globalSettings->gui.showIcons = !globalSettings->gui.showIcons;
- gridview::setupIcons(*m_gridMain, globalSettings->gui.showIcons, convert(globalSettings->gui.iconSize));
+ auto toggleColumn = [&](const Grid::ColumnAttribute& ca)
+ {
+ auto colAttr = grid.getColumnConfig();
- }, globalSettings->gui.showIcons);
+ for (auto iter = colAttr.begin(); iter != colAttr.end(); ++iter)
+ if (iter->type_ == ca.type_)
+ {
+ iter->visible_ = !ca.visible_;
+ grid.setColumnConfig(colAttr);
+ return;
+ }
+ };
- auto setIconSize = [&](xmlAccess::FileIconSize sz)
- {
- globalSettings->gui.iconSize = sz;
- gridview::setupIcons(*m_gridMain, globalSettings->gui.showIcons, convert(sz));
- };
- auto addSizeEntry = [&](const wxString& label, xmlAccess::FileIconSize sz)
- {
- auto setIconSize2 = setIconSize; //bring into scope
- menu.addRadio(label, [sz, setIconSize2] { setIconSize2(sz); }, globalSettings->gui.iconSize == sz, globalSettings->gui.showIcons);
- };
- addSizeEntry(L" " + _("Small" ), xmlAccess::ICON_SIZE_SMALL );
- addSizeEntry(L" " + _("Medium"), xmlAccess::ICON_SIZE_MEDIUM);
- addSizeEntry(L" " + _("Large" ), xmlAccess::ICON_SIZE_LARGE );
- //----------------------------------------------------------------------------------------------
- if (static_cast<ColumnTypeRim>(event.colType_) == COL_TYPE_DATE)
+ if (auto prov = grid.getDataProvider())
+ {
+ const auto& colAttr = grid.getColumnConfig();
+ for (auto iter = colAttr.begin(); iter != colAttr.end(); ++iter)
{
- menu.addSeparator();
+ const Grid::ColumnAttribute& ca = *iter;
- auto selectTimeSpan = [&]
- {
- if (showSelectTimespanDlg(this, manualTimeSpanFrom, manualTimeSpanTo) == ReturnSmallDlg::BUTTON_OKAY)
- {
- applyTimeSpanFilter(folderCmp, manualTimeSpanFrom, manualTimeSpanTo); //overwrite current active/inactive settings
- updateGuiAfterFilterChange(400);
- }
- };
- menu.addItem(_("Select time span..."), selectTimeSpan);
+ menu.addCheckBox(prov->getColumnLabel(ca.type_), [ca, toggleColumn] { toggleColumn(ca); },
+ ca.visible_, ca.type_ != static_cast<ColumnType>(COL_TYPE_FILENAME)); //do not allow user to hide file name column!
}
}
+ //----------------------------------------------------------------------------------------------
+ menu.addSeparator();
- else if (event.compPos_ == gridview::COMP_MIDDLE)
+ auto setDefault = [&]
{
- menu.addItem(_("Category") + L"\tF8", [&] { showSyncAction(false); }, showSyncAction_ ? nullptr : &GlobalResources::getImage(L"compareSmall"));
- menu.addItem(_("Action"), [&] { showSyncAction(true ); }, showSyncAction_ ? &GlobalResources::getImage(L"syncSmall") : nullptr);
+ grid.setColumnConfig(gridview::convertConfig(defaultColumnAttributes));
+ };
+ menu.addItem(_("&Default"), setDefault); //'&' -> reuse text from "default" buttons elsewhere
+ //----------------------------------------------------------------------------------------------
+ menu.addSeparator();
+ menu.addCheckBox(_("Show icons:"), [&]
+ {
+ globalCfg.gui.showIcons = !globalCfg.gui.showIcons;
+ gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg.gui.showIcons, convert(globalCfg.gui.iconSize));
+
+ }, globalCfg.gui.showIcons);
+
+ auto setIconSize = [&](xmlAccess::FileIconSize sz)
+ {
+ globalCfg.gui.iconSize = sz;
+ gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg.gui.showIcons, convert(sz));
+ };
+ auto addSizeEntry = [&](const wxString& label, xmlAccess::FileIconSize sz)
+ {
+ auto setIconSize2 = setIconSize; //bring into scope
+ menu.addRadio(label, [sz, setIconSize2] { setIconSize2(sz); }, globalCfg.gui.iconSize == sz, globalCfg.gui.showIcons);
+ };
+ addSizeEntry(L" " + _("Small" ), xmlAccess::ICON_SIZE_SMALL );
+ addSizeEntry(L" " + _("Medium"), xmlAccess::ICON_SIZE_MEDIUM);
+ addSizeEntry(L" " + _("Large" ), xmlAccess::ICON_SIZE_LARGE );
+ //----------------------------------------------------------------------------------------------
+ if (type == COL_TYPE_DATE)
+ {
+ menu.addSeparator();
+
+ auto selectTimeSpan = [&]
+ {
+ if (showSelectTimespanDlg(this, manualTimeSpanFrom, manualTimeSpanTo) == ReturnSmallDlg::BUTTON_OKAY)
+ {
+ applyTimeSpanFilter(folderCmp, manualTimeSpanFrom, manualTimeSpanTo); //overwrite current active/inactive settings
+ updateGuiAfterFilterChange(400);
+ }
+ };
+ menu.addItem(_("Select time span..."), selectTimeSpan);
}
+
menu.popup(*this);
}
@@ -2048,6 +2113,8 @@ void MainDialog::OnContextSetLayout(wxMouseEvent& event)
menu.addItem(_("Default view"), [&]
{
auiMgr.LoadPerspective(defaultPerspective);
+
+ m_splitterMain->setSashOffset(0);
updateGuiForFolderPair();
});
//----------------------------------------------------------------------------------------
@@ -2296,13 +2363,13 @@ bool MainDialog::saveOldConfig() //return false on user abort
if (lastConfigurationSaved != getConfig())
{
//notify user about changed settings
- if (globalSettings->optDialogs.popupOnConfigChange)
+ if (globalCfg.optDialogs.popupOnConfigChange)
if (activeConfigFiles.size() == 1 && activeConfigFiles[0] != lastRunConfigName())
//only if check is active and non-default config file loaded
{
const wxString filename = activeConfigFiles[0];
- bool neverSave = !globalSettings->optDialogs.popupOnConfigChange;
+ bool neverSave = !globalCfg.optDialogs.popupOnConfigChange;
CheckBox cb(_("Never save changes"), neverSave);
switch (showQuestionDlg(this,
@@ -2320,7 +2387,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
nullptr);
case ReturnQuestionDlg::BUTTON_NO:
- globalSettings->optDialogs.popupOnConfigChange = !neverSave;
+ globalCfg.optDialogs.popupOnConfigChange = !neverSave;
break;
case ReturnQuestionDlg::BUTTON_CANCEL:
@@ -2645,8 +2712,11 @@ void MainDialog::updateGuiAfterFilterChange(int delay)
if (currentCfg.hideFilteredElements)
{
- m_gridMain->Refresh();
- m_gridMain->Update();
+ 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
}
@@ -2941,9 +3011,16 @@ 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
+
int scrollPosX = 0;
int scrollPosY = 0;
- m_gridMain->GetViewStart(&scrollPosX, &scrollPosY); //preserve current scroll position
+ m_gridMainL->GetViewStart(&scrollPosX, &scrollPosY); //preserve current scroll position
+ ZEN_ON_SCOPE_EXIT(
+ m_gridMainL->Scroll(scrollPosX, scrollPosY); //
+ m_gridMainR->Scroll(scrollPosX, scrollPosY); //restore
+ m_gridMainC->Scroll(-1, scrollPosY); ) //
clearGrid(false); //avoid memory peak by clearing old data
@@ -2956,7 +3033,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
std::unique_ptr<LockHolder> dummy2;
- if (globalSettings->createLockFile)
+ if (globalCfg.createLockFile)
{
dummy2.reset(new LockHolder(true)); //allow pw prompt
for (auto iter = cmpConfig.begin(); iter != cmpConfig.end(); ++iter)
@@ -2967,23 +3044,18 @@ void MainDialog::OnCompare(wxCommandEvent& event)
}
//begin comparison
- zen::CompareProcess compProc(globalSettings->fileTimeTolerance,
- globalSettings->optDialogs,
+ zen::CompareProcess compProc(globalCfg.fileTimeTolerance,
+ globalCfg.optDialogs,
true, //allow pw prompt
- globalSettings->runWithBackgroundPriority,
+ globalCfg.runWithBackgroundPriority,
statusHandler);
//technical representation of comparison data
- compProc.startCompareProcess(cmpConfig, folderCmp); //throw
-
- //play (optional) sound notification after sync has completed (GUI and batch mode)
- const wxString soundFile = toWx(zen::getResourceDir()) + wxT("Compare_Complete.wav");
- if (fileExists(toZ(soundFile)))
- wxSound::Play(soundFile, wxSOUND_ASYNC);
+ compProc.startCompareProcess(cmpConfig, folderCmp); //throw GuiAbortProcess
}
catch (GuiAbortProcess&)
{
- if (m_buttonCompare->IsShownOnScreen()) m_buttonCompare->SetFocus();
+ // if (m_buttonCompare->IsShownOnScreen()) m_buttonCompare->SetFocus();
updateGui(); //refresh grid in ANY case! (also on abort)
return;
}
@@ -2991,15 +3063,19 @@ void MainDialog::OnCompare(wxCommandEvent& event)
gridDataView->setData(folderCmp); //update view on data
treeDataView->setData(folderCmp); //
updateGui();
- m_gridMain->Scroll(scrollPosX, scrollPosY); //restore
+
updateSyncEnabledStatus(); //enable the sync button
- if (m_buttonStartSync->IsShownOnScreen())
- m_buttonStartSync->SetFocus();
+ // if (m_buttonStartSync->IsShownOnScreen()) m_buttonStartSync->SetFocus();
- gridview::clearSelection(*m_gridMain);
+ gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR);
m_gridNavi->clearSelection();
+ //play (optional) sound notification after sync has completed (GUI and batch mode)
+ const wxString soundFile = toWx(zen::getResourceDir()) + L"Compare_Complete.wav";
+ if (fileExists(toZ(soundFile)))
+ wxSound::Play(soundFile, wxSOUND_ASYNC);
+
//add to folder history after successful comparison only
folderHistoryLeft ->addItem(toZ(m_directoryLeft->GetValue()));
folderHistoryRight->addItem(toZ(m_directoryRight->GetValue()));
@@ -3070,8 +3146,8 @@ void MainDialog::updateStatistics()
void MainDialog::OnSyncSettings(wxCommandEvent& event)
{
ExecWhenFinishedCfg ewfCfg = { &currentCfg.mainCfg.onCompletion,
- &globalSettings->gui.onCompletionHistory,
- globalSettings->gui.onCompletionHistoryMax
+ &globalCfg.gui.onCompletionHistory,
+ globalCfg.gui.onCompletionHistoryMax
};
if (showSyncConfigDlg(this,
@@ -3145,7 +3221,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
}
//show sync preview screen
- if (globalSettings->optDialogs.showSummaryBeforeSync)
+ if (globalCfg.optDialogs.showSummaryBeforeSync)
{
bool dontShowAgain = false;
@@ -3155,7 +3231,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY)
return;
- globalSettings->optDialogs.showSummaryBeforeSync = !dontShowAgain;
+ globalCfg.optDialogs.showSummaryBeforeSync = !dontShowAgain;
}
wxBusyCursor dummy; //show hourglass cursor
@@ -3175,11 +3251,11 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
currentCfg.handleError,
xmlAccess::extractJobName(activeFileName),
guiCfg.mainCfg.onCompletion,
- globalSettings->gui.onCompletionHistory);
+ globalCfg.gui.onCompletionHistory);
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
std::unique_ptr<LockHolder> dummy2;
- if (globalSettings->createLockFile)
+ if (globalCfg.createLockFile)
{
dummy2.reset(new LockHolder(true)); //allow pw prompt
for (auto iter = begin(folderCmp); iter != end(folderCmp); ++iter)
@@ -3192,12 +3268,12 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
//start synchronization and mark all elements processed
zen::SyncProcess syncProc(xmlAccess::extractJobName(activeFileName),
formatTime<std::wstring>(L"%Y-%m-%d %H%M%S"),
- globalSettings->optDialogs,
- globalSettings->verifyFileCopy,
- globalSettings->copyLockedFiles,
- globalSettings->copyFilePermissions,
- globalSettings->transactionalFileCopy,
- globalSettings->runWithBackgroundPriority,
+ globalCfg.optDialogs,
+ globalCfg.verifyFileCopy,
+ globalCfg.copyLockedFiles,
+ globalCfg.copyFilePermissions,
+ globalCfg.transactionalFileCopy,
+ globalCfg.runWithBackgroundPriority,
statusHandler);
const std::vector<zen::FolderPairSyncCfg> syncProcessCfg = zen::extractSyncCfg(guiCfg.mainCfg);
@@ -3225,48 +3301,52 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
}
-void MainDialog::onGridDoubleClick(GridClickEvent& event)
+void MainDialog::onGridDoubleClickL(GridClickEvent& event)
{
- if (!globalSettings->gui.externelApplications.empty())
- openExternalApplication(globalSettings->gui.externelApplications[0].second,
- gridDataView->getObject(event.row_), //optional
- event.compPos_);
+ onGridDoubleClickRim(event.row_, true);
+}
+void MainDialog::onGridDoubleClickR(GridClickEvent& event)
+{
+ onGridDoubleClickRim(event.row_, false);
}
-
-void MainDialog::onGridLabelLeftClick(GridClickEvent& event)
+void MainDialog::onGridDoubleClickRim(int row, bool leftSide)
{
- auto sortSide = [&](bool onLeft)
- {
- auto sortInfo = gridDataView->getSortInfo();
+ if (!globalCfg.gui.externelApplications.empty())
+ openExternalApplication(globalCfg.gui.externelApplications[0].second,
+ gridDataView->getObject(row), //optional
+ leftSide);
+}
- ColumnTypeRim type = static_cast<ColumnTypeRim>(event.colType_);
- bool sortAscending = GridView::getDefaultSortDirection(type);
- if (sortInfo && sortInfo->onLeft_ == onLeft && sortInfo->type_ == type)
- sortAscending = !sortInfo->ascending_;
+void MainDialog::onGridLabelLeftClick(bool onLeft, ColumnTypeRim type)
+{
+ auto sortInfo = gridDataView->getSortInfo();
- gridDataView->sortView(type, onLeft, sortAscending);
+ bool sortAscending = GridView::getDefaultSortDirection(type);
+ if (sortInfo && sortInfo->onLeft_ == onLeft && sortInfo->type_ == type)
+ sortAscending = !sortInfo->ascending_;
- gridview::clearSelection(*m_gridMain);
- updateGui(); //refresh gridDataView
- };
+ gridDataView->sortView(type, onLeft, sortAscending);
- switch (event.compPos_)
- {
- case gridview::COMP_LEFT:
- sortSide(true);
- break;
+ gridview::clearSelection(*m_gridMainL, *m_gridMainC, *m_gridMainR);
+ updateGui(); //refresh gridDataView
+}
+
+void MainDialog::onGridLabelLeftClickL(GridClickEvent& event)
+{
+ onGridLabelLeftClick(true, static_cast<ColumnTypeRim>(event.colType_));
+}
+void MainDialog::onGridLabelLeftClickR(GridClickEvent& event)
+{
+ onGridLabelLeftClick(false, static_cast<ColumnTypeRim>(event.colType_));
+}
- case gridview::COMP_MIDDLE:
- //sorting middle grid is more or less useless: therefore let's toggle view instead!
- showSyncAction(!showSyncAction_); //toggle view
- break;
- case gridview::COMP_RIGHT:
- sortSide(false);
- break;
- }
+void MainDialog::onGridLabelLeftClickC(GridClickEvent& event)
+{
+ //sorting middle grid is more or less useless: therefore let's toggle view instead!
+ showSyncAction(!showSyncAction_); //toggle view
}
@@ -3434,7 +3514,7 @@ void MainDialog::updateGridViewData()
m_panelViewFilter->Hide();
}
//all three grids retrieve their data directly via gridDataView
- m_gridMain->Refresh();
+ gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR);
//navigation tree
if (showSyncAction_)
@@ -3510,7 +3590,7 @@ void MainDialog::applySyncConfig()
zen::redetermineSyncDirection(getConfig().mainCfg, folderCmp,
[&](const std::wstring& warning)
{
- bool& warningActive = globalSettings->optDialogs.warningSyncDatabase;
+ bool& warningActive = globalCfg.optDialogs.warningSyncDatabase;
if (warningActive)
{
bool dontWarnAgain = false;
@@ -3618,7 +3698,7 @@ void MainDialog::updateGuiForFolderPair()
{
int pairHeight = additionalFolderPairs[0]->GetSize().GetHeight();
addPairMinimalHeight = std::min<double>(1.5, additionalFolderPairs.size()) * pairHeight; //have 0.5 * height indicate that more folders are there
- addPairOptimalHeight = std::min<double>(globalSettings->gui.maxFolderPairsVisible - 1 + 0.5, //subtract first/main folder pair and add 0.5 to indicate additional folders
+ addPairOptimalHeight = std::min<double>(globalCfg.gui.maxFolderPairsVisible - 1 + 0.5, //subtract first/main folder pair and add 0.5 to indicate additional folders
additionalFolderPairs.size()) * pairHeight;
addPairOptimalHeight = std::max(addPairOptimalHeight, addPairMinimalHeight); //implicitly handle corrupted values for "maxFolderPairsVisible"
@@ -3719,7 +3799,7 @@ void MainDialog::removeAddFolderPair(size_t pos)
//set size of scrolled window
//const size_t additionalRows = additionalFolderPairs.size();
- //if (additionalRows <= globalSettings->gui.addFolderPairCountMax) //up to "addFolderPairCountMax" additional pairs shall be shown
+ //if (additionalRows <= globalCfg.gui.addFolderPairCountMax) //up to "addFolderPairCountMax" additional pairs shall be shown
// m_scrolledWindowFolderPairs->SetMinSize(wxSize(-1, pairHeight * static_cast<int>(additionalRows)));
//update controls
@@ -3756,7 +3836,7 @@ void MainDialog::clearAddFolderPairs()
//menu events
void MainDialog::OnMenuGlobalSettings(wxCommandEvent& event)
{
- zen::showGlobalSettingsDlg(this, *globalSettings);
+ zen::showGlobalSettingsDlg(this, globalCfg);
}
@@ -3816,13 +3896,13 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
buffer += '\n';
//write header
- auto provLeft = m_gridMain->getDataProvider(gridview::COMP_LEFT);
- auto provMiddle = m_gridMain->getDataProvider(gridview::COMP_MIDDLE);
- auto provRight = m_gridMain->getDataProvider(gridview::COMP_RIGHT);
+ auto provLeft = m_gridMainL->getDataProvider();
+ auto provMiddle = m_gridMainC->getDataProvider();
+ auto provRight = m_gridMainR->getDataProvider();
- auto colAttrLeft = m_gridMain->getColumnConfig(gridview::COMP_LEFT);
- auto colAttrMiddle = m_gridMain->getColumnConfig(gridview::COMP_MIDDLE);
- auto colAttrRight = m_gridMain->getColumnConfig(gridview::COMP_RIGHT);
+ auto colAttrLeft = m_gridMainL->getColumnConfig();
+ auto colAttrMiddle = m_gridMainC->getColumnConfig();
+ auto colAttrRight = m_gridMainR->getColumnConfig();
vector_remove_if(colAttrLeft , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
vector_remove_if(colAttrMiddle, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
@@ -3863,7 +3943,7 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
buffer += '\n';
//main grid
- const size_t rowCount = m_gridMain->getRowCount();
+ const size_t rowCount = m_gridMainL->getRowCount();
for (size_t row = 0; row < rowCount; ++row)
{
std::for_each(colAttrLeft.begin(), colAttrLeft.end(),
@@ -3921,8 +4001,8 @@ void MainDialog::OnMenuBatchJob(wxCommandEvent& event)
referenceFile, batchCfg,
folderHistoryLeft,
folderHistoryRight,
- globalSettings->gui.onCompletionHistory,
- globalSettings->gui.onCompletionHistoryMax);
+ globalCfg.gui.onCompletionHistory,
+ globalCfg.gui.onCompletionHistoryMax);
}
@@ -3937,7 +4017,7 @@ void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event)
//execute just once per startup!
Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this);
- zen::checkForUpdatePeriodically(this, globalSettings->gui.lastUpdateCheck);
+ zen::checkForUpdatePeriodically(this, globalCfg.gui.lastUpdateCheck);
}
@@ -3978,18 +4058,13 @@ void MainDialog::OnMenuQuit(wxCommandEvent& event)
//#########################################################################################################
//language selection
-void MainDialog::switchProgramLanguage(const int langID)
+void MainDialog::switchProgramLanguage(int langID)
{
//create new dialog with respect to new language
zen::setLanguage(langID); //language is a global attribute
- const xmlAccess::XmlGuiConfig currentGuiCfg = getConfig();
- auto activeFiles = activeConfigFiles;
-
- writeGlobalSettings(); //updating global settings before creating new dialog
-
//create new main window and delete old one
- MainDialog* frame = new MainDialog(activeFiles, currentGuiCfg, *globalSettings, false);
+ MainDialog* frame = new MainDialog(activeConfigFiles, getConfig(), getGlobalCfgBeforeExit(), false);
frame->Show();
Destroy();
@@ -4011,7 +4086,7 @@ void MainDialog::showSyncAction(bool value)
showSyncAction_ = value;
//toggle display of sync preview in middle grid
- gridview::showSyncAction(*m_gridMain, value);
+ gridview::showSyncAction(*m_gridMainC, value);
updateGui();
}
diff --git a/ui/main_dlg.h b/ui/main_dlg.h
index 0e9a7830..63eef25a 100644
--- a/ui/main_dlg.h
+++ b/ui/main_dlg.h
@@ -29,11 +29,11 @@ class MainDialog : public MainDialogGenerated
{
public:
MainDialog(const std::vector<wxString>& cfgFileNames, //default behavior, application start
- xmlAccess::XmlGlobalSettings& settings);
+ const xmlAccess::XmlGlobalSettings& globalSettings); //take over ownership => save on exit
MainDialog(const std::vector<wxString>& referenceFiles,
const xmlAccess::XmlGuiConfig& guiCfg,
- xmlAccess::XmlGlobalSettings& settings,
+ const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit
bool startComparison);
~MainDialog();
@@ -41,7 +41,7 @@ public:
void disableAllElements(bool enableAbort); //dis-/enables all elements (except abort button) that might receive user input
void enableAllElements(); //during long-running processes: comparison, deletion
- void onQueryEndSession(); //last chance to do something before getting killed!
+ void onQueryEndSession(); //last chance to do something useful before killing the application!
private:
friend class CompareStatusHandler;
@@ -56,7 +56,7 @@ private:
MainDialog();
void init(const xmlAccess::XmlGuiConfig& guiCfg,
- xmlAccess::XmlGlobalSettings& settings,
+ const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison);
//configuration load/save
@@ -66,6 +66,9 @@ private:
xmlAccess::XmlGuiConfig getConfig() const;
void setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg);
+ 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);
@@ -78,9 +81,6 @@ private:
//used when saving configuration
std::vector<wxString> activeConfigFiles; //name of currently loaded config file (may be more than 1)
- void readGlobalSettings();
- void writeGlobalSettings();
-
void initViewFilterButtons();
void updateFilterButtons();
@@ -106,7 +106,7 @@ private:
void deleteSelectedFiles(const std::vector<zen::FileSystemObject*>& selectionLeft,
const std::vector<zen::FileSystemObject*>& selectionRight);
- void openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, size_t compPos); //fsObj may be nullptr
+ void openExternalApplication(const wxString& commandline, const zen::FileSystemObject* fsObj, bool leftSide); //fsObj may be nullptr
//work to be done in idle time
void OnIdleEvent(wxEvent& event);
@@ -116,7 +116,11 @@ private:
void flashStatusInformation(const wxString& msg); //temporarily show different status
//events
- void onGridButtonEvent (wxKeyEvent& event);
+ void onGridButtonEventL(wxKeyEvent& event);
+ void onGridButtonEventC(wxKeyEvent& event);
+ void onGridButtonEventR(wxKeyEvent& event);
+ void onGridButtonEvent(wxKeyEvent& event, zen::Grid& grid, bool leftSide);
+
void onTreeButtonEvent (wxKeyEvent& event);
void OnContextSetLayout (wxMouseEvent& event);
void OnGlobalKeyEvent (wxKeyEvent& event);
@@ -128,7 +132,11 @@ private:
void applyCompareConfig(bool changePreviewStatus = true);
//context menu handler methods
- void onMainGridContext(zen::GridClickEvent& event);
+ void onMainGridContextL(zen::GridClickEvent& event);
+ void onMainGridContextC(zen::GridClickEvent& event);
+ void onMainGridContextR(zen::GridClickEvent& event);
+ void onMainGridContextRim(bool leftSide);
+
void onNaviGridContext(zen::GridClickEvent& event);
void onNaviSelection(zen::GridRangeSelectEvent& event);
@@ -139,9 +147,19 @@ private:
void onCheckRows (zen::CheckRowsEvent& event);
void onSetSyncDirection(zen::SyncDirectionEvent& event);
- void onGridDoubleClick (zen::GridClickEvent& event);
- void onGridLabelLeftClick (zen::GridClickEvent& event);
- void onGridLabelContext(zen::GridClickEvent& event);
+ void onGridDoubleClickL(zen::GridClickEvent& event);
+ void onGridDoubleClickR(zen::GridClickEvent& event);
+ void onGridDoubleClickRim(int row, bool leftSide);
+
+ void onGridLabelLeftClickC (zen::GridClickEvent& event);
+ void onGridLabelLeftClickL (zen::GridClickEvent& event);
+ void onGridLabelLeftClickR (zen::GridClickEvent& event);
+ void onGridLabelLeftClick(bool onLeft, zen::ColumnTypeRim type);
+
+ void onGridLabelContextL(zen::GridClickEvent& event);
+ void onGridLabelContextC(zen::GridClickEvent& event);
+ void onGridLabelContextR(zen::GridClickEvent& event);
+ void onGridLabelContext(zen::Grid& grid, zen::ColumnTypeRim type, const std::vector<zen::ColumnAttributeRim>& defaultColumnAttributes);
void OnLeftOnlyFiles( wxCommandEvent& event);
void OnRightOnlyFiles( wxCommandEvent& event);
@@ -209,7 +227,7 @@ private:
void OnMenuQuit( wxCommandEvent& event);
void OnMenuLanguageSwitch( wxCommandEvent& event);
- void switchProgramLanguage(const int langID);
+ void switchProgramLanguage(int langID);
typedef int MenuItemID;
typedef int LanguageID;
@@ -218,8 +236,8 @@ private:
//***********************************************
//application variables are stored here:
- //global settings used by GUI and batch mode
- xmlAccess::XmlGlobalSettings* globalSettings; //always bound!
+ //global settings shared by GUI and batch mode
+ xmlAccess::XmlGlobalSettings globalCfg;
//UI view of FolderComparison structure (partially owns folderCmp)
std::shared_ptr<zen::GridView> gridDataView; //always bound!
diff --git a/ui/search.cpp b/ui/search.cpp
index 80e4aa26..2a9aabfb 100644
--- a/ui/search.cpp
+++ b/ui/search.cpp
@@ -107,13 +107,12 @@ private:
template <bool respectCase>
ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found
- size_t compPos,
const wxString& searchString,
size_t rowFirst, //specify area to search:
size_t rowLast) // [rowFirst, rowLast)
{
- auto prov = grid.getDataProvider(compPos);
- std::vector<Grid::ColumnAttribute> colAttr = grid.getColumnConfig(compPos);
+ auto prov = grid.getDataProvider();
+ std::vector<Grid::ColumnAttribute> colAttr = grid.getColumnConfig();
vector_remove_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
if (!colAttr.empty() && prov)
{
@@ -129,16 +128,15 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found
//syntactic sugar...
-ptrdiff_t findRow(const Grid& grid,
- size_t compPos,
+ptrdiff_t findRow(Grid& grid,
bool respectCase,
const wxString& searchString,
size_t rowFirst, //specify area to search:
size_t rowLast) // [rowFirst, rowLast)
{
return respectCase ?
- findRow<true>( grid, compPos, searchString, rowFirst, rowLast) :
- findRow<false>(grid, compPos, searchString, rowFirst, rowLast);
+ findRow<true>( grid, searchString, rowFirst, rowLast) :
+ findRow<false>(grid, searchString, rowFirst, rowLast);
}
@@ -148,8 +146,7 @@ wxString lastSearchString; //this variable really is conceptionally global...
void executeSearch(bool forceShowDialog,
bool& respectCase,
wxWindow* parent,
- Grid& grid,
- size_t compPosLeft, size_t compPosRight)
+ Grid* gridL, Grid* gridR)
{
bool searchDialogWasShown = false;
@@ -162,36 +159,35 @@ void executeSearch(bool forceShowDialog,
searchDialogWasShown = true;
}
- const size_t rowCount = grid.getRowCount();
- auto cursorPos = grid.getGridCursor(); //(row, component pos)
+ if (wxWindow::FindFocus() == &gridR->getMainWin())
+ std::swap(gridL, gridR); //select side to start with
- size_t cursorRow = cursorPos.first;
- if (cursorRow >= rowCount)
- cursorRow = 0;
-
- if (cursorPos.second == compPosRight)
- std::swap(compPosLeft, compPosRight); //select side to start with
- else if (cursorPos.second != compPosLeft)
- cursorRow = 0;
+ const size_t rowCountL = gridL->getRowCount();
+ const size_t rowCountR = gridR->getRowCount();
+ auto cursorPos = gridL->getGridCursor(); //(row, component pos)
+ size_t cursorRowL = cursorPos.first;
+ if (cursorRowL >= rowCountL)
+ cursorRowL = 0;
{
wxBusyCursor showHourGlass;
- auto finishSearch = [&](size_t compPos, size_t rowFirst, size_t rowLast) -> bool
+ auto finishSearch = [&](Grid& grid, size_t rowFirst, size_t rowLast) -> bool
{
- const ptrdiff_t targetRow = findRow(grid, compPos, respectCase, lastSearchString, rowFirst, rowLast);
+ const ptrdiff_t targetRow = findRow(grid, respectCase, lastSearchString, rowFirst, rowLast);
if (targetRow >= 0)
{
- grid.setGridCursor(targetRow, compPos);
+ //gridOther.clearSelection(); -> not needed other grids are automatically cleared after selection
+ grid.setGridCursor(targetRow);
grid.SetFocus();
return true;
}
return false;
};
- if (finishSearch(compPosLeft , cursorRow + 1, rowCount) ||
- finishSearch(compPosRight, 0, rowCount) ||
- finishSearch(compPosLeft , 0, cursorRow + 1))
+ if (finishSearch(*gridL, cursorRowL + 1, rowCountL) ||
+ finishSearch(*gridR, 0, rowCountR) ||
+ finishSearch(*gridL, 0, cursorRowL + 1))
return;
}
@@ -199,18 +195,18 @@ void executeSearch(bool forceShowDialog,
//show search dialog again
if (searchDialogWasShown)
- executeSearch(true, respectCase, parent, grid, compPosLeft, compPosRight);
+ executeSearch(true, respectCase, parent, gridL, gridR);
}
//###########################################################################################
-void zen::startFind(wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase) //Strg + F
+void zen::startFind(wxWindow* parent, Grid& gridL, Grid& gridR, bool& respectCase) //Strg + F
{
- executeSearch(true, respectCase, parent, grid, compPosLeft, compPosRight);
+ executeSearch(true, respectCase, parent, &gridL, &gridR);
}
-void zen::findNext(wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase) //F3
+void zen::findNext(wxWindow* parent, Grid& gridL, Grid& gridR, bool& respectCase) //F3
{
- executeSearch(false, respectCase, parent, grid, compPosLeft, compPosRight);
+ executeSearch(false, respectCase, parent, &gridL, &gridR);
}
diff --git a/ui/search.h b/ui/search.h
index 6120562e..95811244 100644
--- a/ui/search.h
+++ b/ui/search.h
@@ -11,8 +11,8 @@
namespace zen
{
-void startFind(wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase); //Strg + F
-void findNext( wxWindow* parent, Grid& grid, size_t compPosLeft, size_t compPosRight, bool& respectCase); //F3
+void startFind(wxWindow* parent, Grid& gridL, Grid& gridR, bool& respectCase); //Strg + F
+void findNext( wxWindow* parent, Grid& gridL, Grid& gridR, bool& respectCase); //F3
}
#endif // SEARCH_H_INCLUDED
diff --git a/ui/tree_view.cpp b/ui/tree_view.cpp
index ea73d979..e4dc022e 100644
--- a/ui/tree_view.cpp
+++ b/ui/tree_view.cpp
@@ -478,9 +478,9 @@ void TreeView::updateCmpResult(bool hideFiltered,
case FILE_DIFFERENT:
return differentFilesActive;
case FILE_EQUAL:
+ case FILE_DIFFERENT_METADATA: //= sub-category of equal
return equalFilesActive;
case FILE_CONFLICT:
- case FILE_DIFFERENT_METADATA:
return conflictFilesActive;
}
assert(false);
@@ -612,8 +612,8 @@ const wxColour COLOR_LEVEL11(0xff, 0xcc, 0x99);
const wxColour COLOR_PERCENTAGE_BORDER(198, 198, 198);
const wxColour COLOR_PERCENTAGE_BACKGROUND(0xf8, 0xf8, 0xf8);
-//const wxColor COLOR_TREE_SELECTION_GRADIENT_FROM = wxColor( 89, 255, 99); //green: H:88 S:255 V:172
-//const wxColor COLOR_TREE_SELECTION_GRADIENT_TO = wxColor(225, 255, 227); // H:88 S:255 V:240
+//const wxColor COLOR_TREE_SELECTION_GRADIENT_FROM = wxColor( 89, 255, 99); //green: HSV: 88, 255, 172
+//const wxColor COLOR_TREE_SELECTION_GRADIENT_TO = wxColor(225, 255, 227); // HSV: 88, 255, 240
const wxColor COLOR_TREE_SELECTION_GRADIENT_FROM = getColorSelectionGradientFrom();
const wxColor COLOR_TREE_SELECTION_GRADIENT_TO = getColorSelectionGradientTo ();
@@ -1182,7 +1182,7 @@ std::vector<Grid::ColumnAttribute> treeview::convertConfig(const std::vector<Col
std::vector<Grid::ColumnAttribute> output;
std::transform(attribClean.begin(), attribClean.end(), std::back_inserter(output),
- [&](const ColumnAttributeNavi& a) { return Grid::ColumnAttribute(static_cast<ColumnType>(a.type_), a.width_, a.visible_); });
+ [&](const ColumnAttributeNavi& ca) { return Grid::ColumnAttribute(static_cast<ColumnType>(ca.type_), ca.offset_, ca.stretch_, ca.visible_); });
return output;
}
@@ -1193,7 +1193,7 @@ std::vector<ColumnAttributeNavi> treeview::convertConfig(const std::vector<Grid:
std::vector<ColumnAttributeNavi> output;
std::transform(attribs.begin(), attribs.end(), std::back_inserter(output),
- [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeNavi(static_cast<ColumnTypeNavi>(ca.type_), ca.width_, ca.visible_); });
+ [&](const Grid::ColumnAttribute& ca) { return ColumnAttributeNavi(static_cast<ColumnTypeNavi>(ca.type_), ca.offset_, ca.stretch_, ca.visible_); });
return makeConsistent(output);
}
diff --git a/ui/tree_view.h b/ui/tree_view.h
index 01c737bc..331e1f30 100644
--- a/ui/tree_view.h
+++ b/ui/tree_view.h
@@ -147,7 +147,7 @@ private:
template <bool ascending> static void sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi columnType);
template <bool ascending> struct LessShortName;
- std::vector<TreeLine> flatTree; //collapsable/expandable sub-tree of partial view -> always sorted!
+ std::vector<TreeLine> flatTree; //collapsable/expandable sub-tree of folderCmpView -> always sorted!
/* /|\
| (update...)
| */
diff --git a/ui/triple_splitter.cpp b/ui/triple_splitter.cpp
new file mode 100644
index 00000000..5783bc4f
--- /dev/null
+++ b/ui/triple_splitter.cpp
@@ -0,0 +1,230 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#include "triple_splitter.h"
+#include <algorithm>
+
+using namespace zen;
+
+
+namespace
+{
+//------------ Grid Constants -------------------------------
+const int SASH_HIT_TOLERANCE = 5; //currently onla a placebo!
+const int SASH_SIZE = 10;
+const double SASH_GRAVITY = 0.5; //value within [0, 1]; 1 := resize left only, 0 := resize right only
+const int CHILD_WINDOW_MIN_SIZE = 50; //min. size of managed windows
+
+const wxColor COLOR_SASH_GRADIENT_FROM = wxColour(192, 192, 192); //light grey
+const wxColor COLOR_SASH_GRADIENT_TO = *wxWHITE;
+}
+
+TripleSplitter::TripleSplitter(wxWindow* parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style) : wxWindow(parent, id, pos, size, style | wxTAB_TRAVERSAL), //tab between windows
+ centerOffset(0),
+ windowL(nullptr),
+ windowC(nullptr),
+ windowR(nullptr)
+{
+ Connect(wxEVT_PAINT, wxPaintEventHandler(TripleSplitter::onPaintEvent ), nullptr, this);
+ Connect(wxEVT_SIZE, wxSizeEventHandler (TripleSplitter::onSizeEvent ), nullptr, this);
+ //http://wiki.wxwidgets.org/Flicker-Free_Drawing
+ Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(TripleSplitter::onEraseBackGround), nullptr, this);
+#if wxCHECK_VERSION(2, 9, 1)
+ SetBackgroundStyle(wxBG_STYLE_PAINT);
+#else
+ SetBackgroundStyle(wxBG_STYLE_CUSTOM);
+#endif
+ Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(TripleSplitter::onMouseLeftDown ), nullptr, this);
+ Connect(wxEVT_LEFT_UP, wxMouseEventHandler(TripleSplitter::onMouseLeftUp ), nullptr, this);
+ Connect(wxEVT_MOTION, wxMouseEventHandler(TripleSplitter::onMouseMovement ), nullptr, this);
+ Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(TripleSplitter::onLeaveWindow ), nullptr, this);
+ Connect(wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEventHandler(TripleSplitter::onMouseCaptureLost), nullptr, this);
+ Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(TripleSplitter::onMouseLeftDouble), nullptr, this);
+}
+
+
+void TripleSplitter::updateWindowSizes()
+{
+ if (windowL && windowC && windowR)
+ {
+ const int centerPosX = getCenterPosX();
+ const int centerWidth = getCenterWidth();
+
+ const wxRect clientRect = GetClientRect();
+
+ const int widthL = centerPosX;
+ const int windowRposX = widthL + centerWidth;
+ const int widthR = clientRect.width - windowRposX;
+
+ windowL->SetSize(0, 0, widthL, clientRect.height);
+ windowC->SetSize(widthL + SASH_SIZE, 0, windowC->GetSize().GetWidth(), clientRect.height);
+ windowR->SetSize(windowRposX, 0, widthR, clientRect.height);
+
+ wxClientDC dc(this);
+ drawSash(dc);
+ }
+}
+
+
+class TripleSplitter::SashMove
+{
+public:
+ SashMove(wxWindow& wnd, int mousePosX, int centerPosX) : wnd_(wnd), mousePosX_(mousePosX), centerPosX_(centerPosX)
+ {
+ wnd_.SetCursor(wxCURSOR_SIZEWE);
+ wnd_.CaptureMouse();
+ }
+ ~SashMove()
+ {
+ wnd_.SetCursor(*wxSTANDARD_CURSOR);
+ if (wnd_.HasCapture())
+ wnd_.ReleaseMouse();
+ }
+ int getMousePosXStart () { return mousePosX_; }
+ int getCenterPosXStart() { return centerPosX_; }
+
+private:
+ wxWindow& wnd_;
+ const int mousePosX_;
+ const int centerPosX_;
+};
+
+
+inline
+int TripleSplitter::getCenterWidth() const
+{
+ return 2 * SASH_SIZE + (windowC ? windowC->GetSize().GetWidth() : 0);
+}
+
+
+int TripleSplitter::getCenterPosXOptimal() const
+{
+ const wxRect clientRect = GetClientRect();
+ const int centerWidth = getCenterWidth();
+ return (clientRect.width - centerWidth) * SASH_GRAVITY; //allowed to be negative for extreme client widths!
+}
+
+int TripleSplitter::getCenterPosX() const
+{
+ const wxRect clientRect = GetClientRect();
+ const int centerWidth = getCenterWidth();
+ const int centerPosXOptimal = getCenterPosXOptimal();
+
+ //normalize "centerPosXOptimal + centerOffset"
+ if (clientRect.width < 2 * CHILD_WINDOW_MIN_SIZE + centerWidth) //continuous transition between conditional branches!
+ //use fixed "centeroffset" when "clientRect.width == 2 * CHILD_WINDOW_MIN_SIZE + centerWidth"
+ return centerPosXOptimal + CHILD_WINDOW_MIN_SIZE - static_cast<int>(2 * CHILD_WINDOW_MIN_SIZE * SASH_GRAVITY); //avoid rounding error
+ else
+ return std::max(CHILD_WINDOW_MIN_SIZE, //make sure centerPosXOptimal + offset is within bounds
+ std::min(centerPosXOptimal + centerOffset, clientRect.width - CHILD_WINDOW_MIN_SIZE - centerWidth));
+}
+
+
+void TripleSplitter::drawSash(wxDC& dc)
+{
+ const int centerPosX = getCenterPosX();
+ const int centerWidth = getCenterWidth();
+
+ auto draw = [&](wxRect rect)
+ {
+ const int sash2ndHalf = 3;
+ rect.width -= sash2ndHalf;
+ dc.GradientFillLinear(rect, COLOR_SASH_GRADIENT_FROM, COLOR_SASH_GRADIENT_TO, wxEAST);
+
+ rect.x += rect.width;
+ rect.width = sash2ndHalf;
+ dc.GradientFillLinear(rect, COLOR_SASH_GRADIENT_FROM, COLOR_SASH_GRADIENT_TO, wxWEST);
+
+ static_assert(SASH_SIZE > sash2ndHalf, "");
+ };
+
+ const wxRect rectSashL(centerPosX, 0, SASH_SIZE, GetClientRect().height);
+ const wxRect rectSashR(centerPosX + centerWidth - SASH_SIZE, 0, SASH_SIZE, GetClientRect().height);
+
+ draw(rectSashL);
+ draw(rectSashR);
+}
+
+
+bool TripleSplitter::hitOnSashLine(int posX) const
+{
+ const int centerPosX = getCenterPosX();
+ const int centerWidth = getCenterWidth();
+
+ //we don't get events outside of sash, so SASH_HIT_TOLERANCE is currently *useless*
+ auto hitSash = [&](int sashX) { return sashX - SASH_HIT_TOLERANCE <= posX && posX < sashX + SASH_SIZE + SASH_HIT_TOLERANCE; };
+
+ return hitSash(centerPosX) || hitSash(centerPosX + centerWidth - SASH_SIZE); //hit one of the two sash lines
+}
+
+
+void TripleSplitter::onMouseLeftDown(wxMouseEvent& event)
+{
+ activeMove.reset();
+
+ const int posX = event.GetPosition().x;
+ if (hitOnSashLine(posX))
+ activeMove.reset(new SashMove(*this, posX, getCenterPosX()));
+ event.Skip();
+}
+
+
+void TripleSplitter::onMouseLeftUp(wxMouseEvent& event)
+{
+ activeMove.reset(); //nothing else to do, actual work done by onMouseMovement()
+ event.Skip();
+}
+
+
+void TripleSplitter::onMouseMovement(wxMouseEvent& event)
+{
+ if (activeMove)
+ {
+ centerOffset = activeMove->getCenterPosXStart() - getCenterPosXOptimal() + event.GetPosition().x - activeMove->getMousePosXStart();
+
+ updateWindowSizes();
+ Update(); //no time to wait until idle event!
+ }
+ else
+ {
+ //we receive those only while above the sash, not the managed windows!
+ SetCursor(wxCURSOR_SIZEWE); //set window-local only!
+ }
+ event.Skip();
+}
+
+
+void TripleSplitter::onLeaveWindow(wxMouseEvent& event)
+{
+ //even called when moving from sash over to managed windows!
+ if (!activeMove)
+ SetCursor(*wxSTANDARD_CURSOR);
+ event.Skip();
+}
+
+
+void TripleSplitter::onMouseCaptureLost(wxMouseCaptureLostEvent& event)
+{
+ activeMove.reset();
+ updateWindowSizes();
+ //event.Skip(); -> we DID handle it!
+}
+
+
+void TripleSplitter::onMouseLeftDouble(wxMouseEvent& event)
+{
+ const int posX = event.GetPosition().x;
+ if (hitOnSashLine(posX))
+ {
+ centerOffset = 0; //reset sash according to gravity
+ updateWindowSizes();
+ }
+ event.Skip();
+}
diff --git a/ui/triple_splitter.h b/ui/triple_splitter.h
new file mode 100644
index 00000000..e95671c7
--- /dev/null
+++ b/ui/triple_splitter.h
@@ -0,0 +1,87 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef TRIPPLE_SPLIT_HEADER_8257804292846842573942534254
+#define TRIPPLE_SPLIT_HEADER_8257804292846842573942534254
+
+#include <cassert>
+#include <memory>
+#include <wx/window.h>
+#include <wx/dcclient.h>
+
+//a not-so-crappy splitter window
+
+/* manage three contained windows:
+ 1. left and right window are stretched
+ 2. middle window is fixed size
+ 3. middle window position can be changed via mouse with two sash lines
+ -----------------
+ | | | |
+ | | | |
+ | | | |
+ -----------------
+*/
+
+namespace zen
+{
+class TripleSplitter : public wxWindow
+{
+public:
+ TripleSplitter(wxWindow* parent,
+ wxWindowID id = wxID_ANY,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = 0);
+
+ void setupWindows(wxWindow* winL, wxWindow* winC, wxWindow* winR)
+ {
+ assert(winL->GetParent() == this && winC->GetParent() == this && winR->GetParent() == this && !GetSizer());
+ windowL = winL;
+ windowC = winC;
+ windowR = winR;
+ updateWindowSizes();
+ }
+
+ int getSashOffset() const { return centerOffset; }
+ void setSashOffset(int off) { centerOffset = off; updateWindowSizes(); }
+
+private:
+ void onEraseBackGround(wxEraseEvent& event) {}
+ void onSizeEvent(wxSizeEvent& event) { updateWindowSizes(); event.Skip(); }
+
+ void onPaintEvent(wxPaintEvent& event)
+ {
+ wxPaintDC dc(this);
+ drawSash(dc);
+ }
+
+ void updateWindowSizes();
+ int getCenterWidth() const;
+ int getCenterPosX() const; //return normalized posX
+ int getCenterPosXOptimal() const;
+
+ void drawSash(wxDC& dc);
+ bool hitOnSashLine(int posX) const;
+
+ void onMouseLeftDown(wxMouseEvent& event);
+ void onMouseLeftUp(wxMouseEvent& event);
+ void onMouseMovement(wxMouseEvent& event);
+ void onLeaveWindow(wxMouseEvent& event);
+ void onMouseCaptureLost(wxMouseCaptureLostEvent& event);
+ void onMouseLeftDouble(wxMouseEvent& event);
+
+ class SashMove;
+ std::unique_ptr<SashMove> activeMove;
+
+ int centerOffset; //offset to add after "gravity" stretching
+
+ wxWindow* windowL;
+ wxWindow* windowC;
+ wxWindow* windowR;
+};
+}
+
+#endif //TRIPPLE_SPLIT_HEADER_8257804292846842573942534254
bgstack15