summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:22:18 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:22:18 +0200
commitbcc5cc28c6dc5178e8f4fd0cc521034ae5def388 (patch)
treebacc60d27b435d32172f97643576c5e4e953177d /wx+
parent5.9 (diff)
downloadFreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.tar.gz
FreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.tar.bz2
FreeFileSync-bcc5cc28c6dc5178e8f4fd0cc521034ae5def388.zip
5.10
Diffstat (limited to 'wx+')
-rw-r--r--wx+/button.cpp5
-rw-r--r--wx+/context_menu.h4
-rw-r--r--wx+/file_drop.h14
-rw-r--r--wx+/grid.cpp329
-rw-r--r--wx+/grid.h87
-rw-r--r--wx+/zlib_wrap.h2
6 files changed, 221 insertions, 220 deletions
diff --git a/wx+/button.cpp b/wx+/button.cpp
index 8fce99f4..a67624b8 100644
--- a/wx+/button.cpp
+++ b/wx+/button.cpp
@@ -128,8 +128,7 @@ wxBitmap BitmapButton::createBitmapFromText(const wxString& text)
wxBitmap newBitmap(sizeNeeded.GetWidth(), sizeNeeded.GetHeight());
{
- wxMemoryDC dc;
- dc.SelectObject(newBitmap);
+ wxMemoryDC dc(newBitmap);
//set up white background
dc.SetBackground(*wxWHITE_BRUSH);
@@ -150,8 +149,6 @@ wxBitmap BitmapButton::createBitmapFromText(const wxString& text)
dc.SetFont(currentFont);
dc.DrawLabel(textLabelFormatted, wxNullBitmap, wxRect(0, 0, newBitmap.GetWidth(), newBitmap.GetHeight()), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, indexAccel);
-
- dc.SelectObject(wxNullBitmap);
}
//add alpha channel to image
diff --git a/wx+/context_menu.h b/wx+/context_menu.h
index 2557737a..cb6cb86d 100644
--- a/wx+/context_menu.h
+++ b/wx+/context_menu.h
@@ -30,9 +30,9 @@ class ContextMenu : private wxEvtHandler
public:
ContextMenu() : menu(new wxMenu) {}
- void addItem(const wxString& label, const std::function<void()>& command, const wxBitmap* bmp = nullptr, bool enabled = true)
+ void addItem(const wxString& label, const std::function<void()>& command, const wxBitmap* bmp = nullptr, bool enabled = true, int id = wxID_ANY)
{
- wxMenuItem* newItem = new wxMenuItem(menu.get(), wxID_ANY, label); //menu owns item!
+ wxMenuItem* newItem = new wxMenuItem(menu.get(), id, label); //menu owns item!
if (bmp) newItem->SetBitmap(*bmp); //do not set AFTER appending item! wxWidgets screws up for yet another crappy reason
menu->Append(newItem);
if (!enabled) newItem->Enable(false); //do not enable BEFORE appending item! wxWidgets screws up for yet another crappy reason
diff --git a/wx+/file_drop.h b/wx+/file_drop.h
index 22a6542c..7b6020ac 100644
--- a/wx+/file_drop.h
+++ b/wx+/file_drop.h
@@ -13,6 +13,8 @@
namespace zen
{
//register simple file drop event (without issue of freezing dialogs and without wxFileDropTarget overdesign)
+//CAVEAT: a drop target window must not be directly or indirectly contained within a wxStaticBoxSizer until the following wxGTK bug
+//is fixed. According to wxWidgets release cycles this is expected to be: never http://trac.wxwidgets.org/ticket/2763
//1. setup a window to emit EVENT_DROP_FILE
void setupFileDrop(wxWindow& wnd);
@@ -39,7 +41,8 @@ void setupFileDrop(wxWindow& wnd);
-
+namespace impl
+{
inline
wxEventType createNewEventType()
{
@@ -47,9 +50,11 @@ wxEventType createNewEventType()
static wxEventType dummy = wxNewEventType();
return dummy;
}
+}
+
//define new event type
-const wxEventType EVENT_DROP_FILE = createNewEventType();
+const wxEventType EVENT_DROP_FILE = impl::createNewEventType();
class FileDropEvent : public wxCommandEvent
{
@@ -78,6 +83,8 @@ typedef void (wxEvtHandler::*FileDropEventFunction)(FileDropEvent&);
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FileDropEventFunction, &func)
+namespace impl
+{
class WindowDropTarget : public wxFileDropTarget
{
public:
@@ -99,12 +106,13 @@ private:
wxWindow& dropWindow_;
};
+}
inline
void setupFileDrop(wxWindow& wnd)
{
- wnd.SetDropTarget(new WindowDropTarget(wnd)); //takes ownership
+ wnd.SetDropTarget(new impl::WindowDropTarget(wnd)); //takes ownership
}
}
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 22a8bba1..ff45224d 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -41,11 +41,10 @@ namespace
{
//------------ Grid Constants --------------------------------
const double MOUSE_DRAG_ACCELERATION = 1.5; //unit: [rows / (pixel * sec)] -> same value as Explorer!
-const int DEFAULT_ROW_HEIGHT = 20;
const int DEFAULT_COL_LABEL_HEIGHT = 24;
const int COLUMN_BORDER_LEFT = 4; //for left-aligned text
const int COLUMN_LABEL_BORDER = COLUMN_BORDER_LEFT;
-const int COLUMN_MOVE_DELAY = 5; //unit: [pixel] (from Explorer)
+const int COLUMN_MOVE_DELAY = 5; //unit: [pixel] (from Explorer)
const int COLUMN_MIN_WIDTH = 40; //only honored when resizing manually!
const int ROW_LABEL_BORDER = 3;
const int COLUMN_RESIZE_TOLERANCE = 6; //unit [pixel]
@@ -200,10 +199,10 @@ void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bo
{
if (selected)
{
- if (hasFocus)
- dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST);
- else
- dc.GradientFillLinear(rect, COLOR_SELECTION_GRADIENT_NO_FOCUS_FROM, COLOR_SELECTION_GRADIENT_NO_FOCUS_TO, wxEAST);
+ //if (hasFocus)
+ dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST);
+ //else -> doesn't look too good...
+ // dc.GradientFillLinear(rect, COLOR_SELECTION_GRADIENT_NO_FOCUS_FROM, COLOR_SELECTION_GRADIENT_NO_FOCUS_TO, wxEAST);
}
else
clearArea(dc, rect, backgroundColor);
@@ -323,7 +322,6 @@ void GridData::drawColumnLabelText(wxDC& dc, const wxRect& rect, const wxString&
}
//----------------------------------------------------------------------------------------------------------------
-
/*
SubWindow
/|\
@@ -495,7 +493,8 @@ class Grid::RowLabelWin : public SubWindow
public:
RowLabelWin(Grid& parent) :
SubWindow(parent),
- rowHeight(DEFAULT_ROW_HEIGHT) {}
+ rowHeight(parent.GetCharHeight() + 2 + 1) {} //default height; don't call any functions on "parent" other than those from wxWindow during construction!
+ //2 for some more space, 1 for bottom border (gives 15 + 2 + 1 on Windows, 17 + 2 + 1 on Ubuntu)
int getBestWidth(ptrdiff_t rowFrom, ptrdiff_t rowTo)
{
@@ -509,13 +508,12 @@ public:
size_t getLogicalHeight() const { return refParent().getRowCount() * rowHeight; }
- ptrdiff_t getRowAtPos(ptrdiff_t posY) const //returns < 0 if row not found
+ ptrdiff_t getRowAtPos(ptrdiff_t posY) const //returns < 0 on invalid input, else row number within: [0, rowCount]; rowCount if out of range
{
if (posY >= 0 && rowHeight > 0)
{
const size_t row = posY / rowHeight;
- if (row < refParent().getRowCount())
- return row;
+ return std::min(row, refParent().getRowCount());
}
return -1;
}
@@ -912,11 +910,14 @@ public:
ColLabelWin& colLabelWin) : SubWindow(parent),
rowLabelWin_(rowLabelWin),
colLabelWin_(colLabelWin),
- selectionAnchor(0)
+ selectionAnchor(0),
+ gridUpdatePending(false)
{
- Connect(EVENT_GRID_HAS_SCROLLED, wxCommandEventHandler(MainWin::updateAfterScroll), nullptr, this);
+ Connect(EVENT_GRID_HAS_SCROLLED, wxEventHandler(MainWin::onRequestWindowUpdate), nullptr, this);
}
+ ~MainWin() { assert(!gridUpdatePending); }
+
void makeRowVisible(size_t row)
{
const wxRect labelRect = rowLabelWin_.getRowLabelArea(row); //returns empty rect if column not found
@@ -978,7 +979,7 @@ private:
const int rowHeight = rowLabelWin_.getRowHeight();
- //why again aren't we using RowLabelWin::getRowsOnClient() here?
+ //why again aren't we using RowLabelWin::getRowsOnClient() here?
const wxPoint topLeft = refParent().CalcUnscrolledPosition(rect.GetTopLeft());
const wxPoint bottomRight = refParent().CalcUnscrolledPosition(rect.GetBottomRight());
@@ -1026,15 +1027,15 @@ private:
}
}
- void drawBackground(GridData& prov, wxDC& dc, const wxRect& rect, int row, size_t compPos)
+ void drawBackground(GridData& prov, wxDC& dc, const wxRect& rect, size_t row, size_t compPos)
{
Grid& grid = refParent();
//check if user is currently selecting with mouse
bool drawSelection = grid.isSelected(row, compPos);
if (activeSelection)
{
- const int rowFrom = std::min(activeSelection->getStartRow(), activeSelection->getCurrentRow());
- const int rowTo = std::max(activeSelection->getStartRow(), activeSelection->getCurrentRow());
+ const size_t rowFrom = std::min(activeSelection->getStartRow(), activeSelection->getCurrentRow());
+ const size_t rowTo = std::max(activeSelection->getStartRow(), activeSelection->getCurrentRow());
if (compPos == activeSelection->getComponentPos() && rowFrom <= row && row <= rowTo)
drawSelection = activeSelection->isPositiveSelect(); //overwrite default
@@ -1051,13 +1052,16 @@ private:
virtual void onMouseLeftDouble(wxMouseEvent& event)
{
const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
- const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 if no row at this position
- const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos)
+ const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ if (row >= 0)
+ {
+ const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos)
- const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE;
- const ptrdiff_t compPos = colInfo ? colInfo->second : -1;
- //client is interested in all double-clicks, even those outside of the grid!
- sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType, compPos));
+ const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE;
+ const ptrdiff_t compPos = colInfo ? colInfo->second : -1;
+ //client is interested in all double-clicks, even those outside of the grid!
+ sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType, compPos));
+ }
event.Skip();
}
@@ -1067,76 +1071,83 @@ private:
SetFocus();
const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
-
- const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 if no row at this position
- const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos)
- const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE;
- const ptrdiff_t compPos = colInfo ? colInfo->second : -1;
-
- //notify event
- GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, colType, compPos);
- if (!sendEventNow(mouseEvent)) //if event was not processed externally...
+ const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ if (row >= 0)
{
+ const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos)
+ const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE;
+ const ptrdiff_t compPos = colInfo ? colInfo->second : -1;
+
if (!event.RightDown() || !refParent().isSelected(row, compPos)) //do NOT start a new selection if user right-clicks on a selected area!
{
- if (row >= 0 && compPos >= 0)
- cursor = std::make_pair(row, compPos);
-
if (event.ControlDown())
{
- if (row >= 0 && compPos >= 0)
+ if (compPos >= 0)
activeSelection.reset(new MouseSelection(*this, row, compPos, !refParent().isSelected(row, compPos)));
- selectionAnchor = cursor.first; //[!] anchor is coupled with cursor, *not* row
}
else if (event.ShiftDown())
{
- if (row >= 0 && compPos >= 0)
+ if (compPos >= 0)
activeSelection.reset(new MouseSelection(*this, selectionAnchor, compPos, true));
- else
- selectionAnchor = cursor.first;
- refParent().clearSelectionAll();
+ refParent().clearSelectionAllAndNotify();
}
else
{
- if (row >= 0 && compPos >= 0)
+ if (compPos >= 0)
activeSelection.reset(new MouseSelection(*this, row, compPos, true));
- selectionAnchor = cursor.first;
- refParent().clearSelectionAll();
+ refParent().clearSelectionAllAndNotify();
}
}
+
+ //notify event *after* potential "clearSelectionAllAndNotify()" above: a client should first receive a GridRangeSelectEvent for clearing the grid, if necessary,
+ //then GridClickEvent and the associated GridRangeSelectEvent one after the other
+ GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, colType, compPos);
+ sendEventNow(mouseEvent);
+
Refresh();
}
-
event.Skip(); //allow changing focus
}
void onMouseUp(wxMouseEvent& event)
{
- //const int currentRow = clientPosToRow(event.GetPosition()); -> this one may point to row which is not in visible area!
-
if (activeSelection)
{
- const auto rowFrom = activeSelection->getStartRow();
- const auto rowTo = activeSelection->getCurrentRow();
- const auto compPos = activeSelection->getComponentPos();
- const bool positive = activeSelection->isPositiveSelect();
-
- cursor.first = activeSelection->getCurrentRow(); //slight deviation from Explorer: change cursor while dragging mouse! -> unify behavior with shift + direction keys
- refParent().selectRange(rowFrom, rowTo, compPos, positive);
+ const size_t rowCount = refParent().getRowCount();
+ if (rowCount > 0)
+ {
+ if (activeSelection->getCurrentRow() < rowCount)
+ {
+ cursor.first = activeSelection->getCurrentRow();
+ selectionAnchor = activeSelection->getStartRow(); //allowed to be "out of range"
+ }
+ else if (activeSelection->getStartRow() < rowCount) //don't change cursor if "to" and "from" are out of range
+ {
+ cursor.first = rowCount - 1;
+ selectionAnchor = activeSelection->getStartRow(); //allowed to be "out of range"
+ }
+ else //total selection "out of range"
+ selectionAnchor = cursor.first;
+ }
+ //slight deviation from Explorer: change cursor while dragging mouse! -> unify behavior with shift + direction keys
+ refParent().selectRangeAndNotify(activeSelection->getStartRow (), //from
+ activeSelection->getCurrentRow(), //to
+ activeSelection->getComponentPos(),
+ activeSelection->isPositiveSelect());
activeSelection.reset();
}
//this one may point to row which is not in visible area!
const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
- const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 if no row at this position
+ const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns optional pair (column type, compPos)
const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE; //we probably should notify even if colInfo is invalid!
const ptrdiff_t compPos = colInfo ? colInfo->second : -1;
- //notify event
+ //notify click event after the range selection! e.g. this makes sure the selection is applied before showing a context menu
sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, colType, compPos));
Refresh();
@@ -1158,11 +1169,12 @@ private:
//change tooltip
const wxString toolTip = [&]() -> wxString
{
+ const ptrdiff_t rowCount = refParent().getRowCount();
const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition());
- const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 if no row at this position
+ const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos)
- if (colInfo && row >= 0)
+ if (colInfo && 0 <= row && row < rowCount)
{
if (auto prov = refParent().getDataProvider(colInfo->second))
return prov->getToolTip(row, colInfo->first);
@@ -1212,7 +1224,7 @@ private:
auto& comp = refParent().comp;
std::for_each(comp.begin(), comp.end(), [](Grid::Component& c) { c.selection.clear(); }); //clear selection, do NOT fire event
- refParent().selectRange(selectionAnchor, row, cursor.second); //set new selection + fire event
+ refParent().selectRangeAndNotify(selectionAnchor, row, cursor.second); //set new selection + fire event
cursor.first = row; //don't call setCursor() since it writes to "selectionAnchor"!
this->makeRowVisible(row);
@@ -1303,7 +1315,7 @@ private:
case 'A': //Ctrl + A - select all
if (event.ControlDown())
- refParent().selectRange(0, rowCount, cursor.second);
+ refParent().selectRangeAndNotify(0, rowCount, cursor.second);
break;
case WXK_NUMPAD_ADD: //CTRL + '+' - auto-size all
@@ -1320,7 +1332,7 @@ private:
class MouseSelection : private wxEvtHandler
{
public:
- MouseSelection(MainWin& wnd, ptrdiff_t rowStart, size_t compPos, bool positiveSelect) :
+ MouseSelection(MainWin& wnd, size_t rowStart, size_t compPos, bool positiveSelect) :
wnd_(wnd), rowStart_(rowStart), compPos_(compPos), rowCurrent_(rowStart), positiveSelect_(positiveSelect), toScrollX(0), toScrollY(0),
tickCountLast(getTicks()),
ticksPerSec_(ticksPerSec())
@@ -1332,10 +1344,10 @@ private:
}
~MouseSelection() { if (wnd_.HasCapture()) wnd_.ReleaseMouse(); }
- ptrdiff_t getStartRow () const { return rowStart_; }
- size_t getComponentPos () const { return compPos_; }
- ptrdiff_t getCurrentRow () const { return rowCurrent_; }
- bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting?
+ size_t getStartRow () const { return rowStart_; }
+ size_t getComponentPos () const { return compPos_; }
+ size_t getCurrentRow () const { return rowCurrent_; }
+ bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting?
void evalMousePos()
{
@@ -1343,7 +1355,7 @@ private:
if (ticksPerSec_ > 0)
{
const TickVal now = getTicks(); //0 on error
- deltaTime = static_cast<double>(now - tickCountLast) / ticksPerSec_; //unit: [sec]
+ deltaTime = static_cast<double>(dist(tickCountLast, now)) / ticksPerSec_; //unit: [sec]
tickCountLast = now;
}
@@ -1389,18 +1401,14 @@ private:
wxPoint clientPosTrimmed = clientPos;
numeric::confine(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window!
- wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed);
-
- //make sure "current row" is always at a valid position while moving!
- ptrdiff_t currentRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 if no row at this position
- if (currentRow < 0)
- currentRow = wnd_.refParent().getRowCount() - 1; //seems, we hit the empty space at the end: empty size covered!
-
- if (currentRow >= 0 && rowCurrent_ != currentRow)
- {
- rowCurrent_ = currentRow;
- wnd_.Refresh();
- }
+ const wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed);
+ const ptrdiff_t newRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ if (newRow >= 0)
+ if (rowCurrent_ != newRow)
+ {
+ rowCurrent_ = newRow;
+ wnd_.Refresh();
+ }
}
}
@@ -1408,7 +1416,7 @@ private:
void onTimer(wxEvent& event) { evalMousePos(); }
MainWin& wnd_;
- const ptrdiff_t rowStart_;
+ const size_t rowStart_;
const size_t compPos_;
ptrdiff_t rowCurrent_;
const bool positiveSelect_;
@@ -1429,12 +1437,22 @@ private:
//which *first* calls us, MainWin::ScrollWindow(), and *then* internally updates m_yScrollPosition
//=> we cannot use CalcUnscrolledPosition() here which gives the wrong/outdated value!!!
//=> we need to update asynchronously:
- wxCommandEvent scrollEvent(EVENT_GRID_HAS_SCROLLED);
- AddPendingEvent(scrollEvent); //asynchronously call updateAfterScroll()
+ //=> don't use plain async event => severe performance issues on wxGTK!
+ //=> can't use idle event neither: too few idle events on Windows, e.g. NO idle events while mouse drag-scrolling!
+ //=> solution: send single async event at most!
+ if (!gridUpdatePending) //without guarding, the number of outstanding async events can get very high during scrolling!! test case: Ubuntu: 170; Windows: 20
+ {
+ gridUpdatePending = true;
+ wxCommandEvent scrollEvent(EVENT_GRID_HAS_SCROLLED);
+ AddPendingEvent(scrollEvent); //asynchronously call updateAfterScroll()
+ }
}
- void updateAfterScroll(wxCommandEvent&)
+ void onRequestWindowUpdate(wxEvent& event)
{
+ ZEN_ON_SCOPE_EXIT(gridUpdatePending = false);
+ assert(gridUpdatePending);
+
refParent().updateWindowSizes(false); //row label width has changed -> do *not* update scrollbars: recursion on wxGTK! -> still a problem, now that we're called async??
rowLabelWin_.Update(); //update while dragging scroll thumb
}
@@ -1446,6 +1464,7 @@ private:
std::pair<ptrdiff_t, ptrdiff_t> cursor; //(row, component position), always valid! still unsigned type to facilitate "onKeyDown()"
size_t selectionAnchor;
+ bool gridUpdatePending;
};
//----------------------------------------------------------------------------------------------------------------
@@ -1642,15 +1661,8 @@ void Grid::scrollDelta(int deltaX, int deltaY)
scrollPosX += deltaX;
scrollPosY += deltaY;
- scrollPosX = std::max(0, scrollPosX); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"!
- scrollPosY = std::max(0, scrollPosY); //
-
- //const int unitsTotalX = GetScrollLines(wxHORIZONTAL);
- //const int unitsTotalY = GetScrollLines(wxVERTICAL);
-
- //if (unitsTotalX <= 0 || unitsTotalY <= 0) return; -> premature
- //numeric::confine(scrollPosX, 0, unitsTotalX - 1); //make sure scroll target is in valid range
- //numeric::confine(scrollPosY, 0, unitsTotalY - 1); //
+ scrollPosX = std::max(0, scrollPosX); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"!
+ scrollPosY = std::max(0, scrollPosY); //
Scroll(scrollPosX, scrollPosY);
updateWindowSizes(); //may show horizontal scroll bar
@@ -1710,7 +1722,7 @@ void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr, size_
visibleCols.push_back(Grid::VisibleColumn(ca.type_, ca.offset_, ca.stretch_));
});
- //set ownership of visible columns
+ //"ownership" of visible columns is now within Grid
comp[compPos].visibleCols = visibleCols;
updateWindowSizes();
@@ -1723,34 +1735,30 @@ std::vector<Grid::ColumnAttribute> Grid::getColumnConfig(size_t compPos) const
{
if (compPos < comp.size())
{
- auto iterVcols = comp[compPos].visibleCols.begin();
- auto iterVcolsend = comp[compPos].visibleCols.end();
-
- std::set<ColumnType> visibleTypes;
- std::transform(iterVcols, iterVcolsend, std::inserter(visibleTypes, visibleTypes.begin()),
- [](const VisibleColumn& vc) { return vc.type_; });
-
//get non-visible columns (+ outdated visible ones)
std::vector<ColumnAttribute> output = comp[compPos].oldColAttributes;
+ auto iterVcols = comp[compPos].visibleCols.begin();
+ auto iterVcolsend = comp[compPos].visibleCols.end();
+
//update visible columns but keep order of non-visible ones!
std::for_each(output.begin(), output.end(),
[&](ColumnAttribute& ca)
{
- if (visibleTypes.find(ca.type_) != visibleTypes.end())
+ if (ca.visible_)
{
if (iterVcols != iterVcolsend)
{
- ca.visible_ = true; //paranoia
ca.type_ = iterVcols->type_;
ca.stretch_ = iterVcols->stretch_;
ca.offset_ = iterVcols->offset_;
++iterVcols;
}
+ else
+ assert(false);
}
- else
- ca.visible_ = false; //paranoia
});
+ assert(iterVcols == iterVcolsend);
return output;
}
@@ -1835,13 +1843,16 @@ void Grid::SetScrollbar(int orientation, int position, int thumbSize, int range,
WXLRESULT Grid::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
- //we land here if wxWindowMSW::MSWWindowProc() couldn't handle the message
- //http://msdn.microsoft.com/en-us/library/windows/desktop/ms645614(v=vs.85).aspx
+ //we land here if wxWindowMSW::MSWWindowProc() couldn't handle the message
+ //http://msdn.microsoft.com/en-us/library/windows/desktop/ms645614(v=vs.85).aspx
if (nMsg == WM_MOUSEHWHEEL) //horizontal wheel
{
const int distance = GET_WHEEL_DELTA_WPARAM(wParam);
const int delta = WHEEL_DELTA;
- const int rotations = distance / delta;
+ int rotations = distance / delta;
+
+ if (GetLayoutDirection() == wxLayout_RightToLeft)
+ rotations = -rotations;
static int linesPerRotation = -1;
if (linesPerRotation < 0)
@@ -1849,7 +1860,7 @@ WXLRESULT Grid::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
linesPerRotation = 3;
scrollDelta(rotations * linesPerRotation, 0); //in scroll units
- return 0; //"If an application processes this message, it should return zero."
+ return 0; //"If an application processes this message, it should return zero."
}
return wxScrolledWindow::MSWDefWindowProc(nMsg, wParam, lParam);
@@ -1861,6 +1872,7 @@ wxWindow& Grid::getCornerWin () { return *cornerWin_; }
wxWindow& Grid::getRowLabelWin() { return *rowLabelWin_; }
wxWindow& Grid::getColLabelWin() { return *colLabelWin_; }
wxWindow& Grid::getMainWin () { return *mainWin_; }
+const wxWindow& Grid::getMainWin() const { return *mainWin_; }
wxRect Grid::getColumnLabelArea(ColumnType colType, size_t compPos) const
@@ -2018,16 +2030,6 @@ wxRect Grid::getCellArea(size_t row, ColumnType colType, size_t compPos) const
}
-void Grid::clearSelection(size_t compPos)
-{
- if (compPos < comp.size())
- {
- comp[compPos].selection.clear();
- mainWin_->Refresh();
- }
-}
-
-
void Grid::setGridCursor(size_t row, size_t compPos)
{
if (compPos < comp.size())
@@ -2036,7 +2038,7 @@ void Grid::setGridCursor(size_t row, size_t compPos)
mainWin_->makeRowVisible(row);
std::for_each(comp.begin(), comp.end(), [](Grid::Component& c) { c.selection.clear(); }); //clear selection, do NOT fire event
- selectRange(row, row, compPos); //set new selection + fire event
+ selectRangeAndNotify(row, row, compPos); //set new selection + fire event
mainWin_->Refresh();
rowLabelWin_->Refresh(); //row labels! (Kubuntu)
@@ -2044,14 +2046,22 @@ void Grid::setGridCursor(size_t row, size_t compPos)
}
-void Grid::selectRange(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool positive)
+void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool positive)
{
if (compPos < comp.size())
{
- comp[compPos].selection.selectRange(rowFrom, rowTo, positive);
+ //sort + convert to half-open range
+ auto rowFirst = std::min(rowFrom, rowTo);
+ auto rowLast = std::max(rowFrom, rowTo) + 1;
+
+ const size_t rowCount = getRowCount();
+ numeric::confine<ptrdiff_t>(rowFirst, 0, rowCount);
+ numeric::confine<ptrdiff_t>(rowLast, 0, rowCount);
+
+ comp[compPos].selection.selectRange(rowFirst, rowLast, positive);
//notify event
- GridRangeSelectEvent selectionEvent(rowFrom, rowTo, compPos, positive);
+ GridRangeSelectEvent selectionEvent(rowFirst, rowLast, compPos, positive);
if (wxEvtHandler* evtHandler = GetEventHandler())
evtHandler->ProcessEvent(selectionEvent);
@@ -2060,22 +2070,31 @@ void Grid::selectRange(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool
}
-void Grid::clearSelectionAll()
+void Grid::clearSelection(bool emitSelectRangeEvent, size_t compPos)
{
- for (auto iter = comp.begin(); iter != comp.end(); ++iter)
+ if (compPos < comp.size())
{
- Grid::Component& c = *iter;
- c.selection.clear();
+ comp[compPos].selection.clear();
+ mainWin_->Refresh();
- //notify event
- const size_t compPos = iter - comp.begin();
- GridRangeSelectEvent unselectionEvent(-1, -1, compPos, false);
- if (wxEvtHandler* evtHandler = GetEventHandler())
- evtHandler->ProcessEvent(unselectionEvent);
+ if (emitSelectRangeEvent)
+ {
+ //notify event, even if we're not triggered by user interaction
+ GridRangeSelectEvent unselectionEvent(0, 0, compPos, false);
+ if (wxEvtHandler* evtHandler = GetEventHandler())
+ evtHandler->ProcessEvent(unselectionEvent);
+ }
}
}
+void Grid::clearSelectionAllAndNotify()
+{
+ for (size_t compPos = 0; compPos < comp.size(); ++compPos)
+ clearSelection(true, compPos);
+}
+
+
void Grid::scrollTo(size_t row)
{
const wxRect labelRect = rowLabelWin_->getRowLabelArea(row); //returns empty rect if column not found
@@ -2111,7 +2130,7 @@ ptrdiff_t Grid::getBestColumnSize(size_t col, size_t compPos) const
{
if (compPos < comp.size())
{
- auto& visibleCols = comp[compPos].visibleCols;
+ const auto& visibleCols = comp[compPos].visibleCols;
auto dataView = comp[compPos].dataView_;
if (dataView && col < visibleCols.size())
{
@@ -2140,26 +2159,35 @@ void Grid::setColWidthAndNotify(ptrdiff_t width, size_t col, size_t compPos, boo
VisibleColumn& vcRs = comp[compPos].visibleCols[col];
const int mainWinWidth = mainWin_->GetClientSize().GetWidth();
const ptrdiff_t stretchTotal = getStretchTotal();
-
- const ptrdiff_t offset = width - getColStretchedWidth(vcRs.stretch_, stretchTotal, mainWinWidth); //width := stretchedWidth + (normalized) offset
- vcRs.offset_ = offset;
-
- //CAVEAT:
- //I. width may be < COLUMN_MIN_WIDTH: for non-stretched columns this doesn't matter, since it's normalized in getColWidths() anyway,
- // for stretched columns on the other hand negative width would be evaluated *before* normalization! => need to normalize here!
- //II. worse: resizing any column should normalize *all* other stretched columns' offsets considering current mainWinWidth!
- // Testcase: 1. make main window so small in width that horizontal scrollbars are shown despite existing streched column.
- // 2. resize a fixed size column so that scrollbars vanish. 3. verify that the stretched column is resizing immediately while main dialog is enlarged
+ const ptrdiff_t stretchedWithCol = getColStretchedWidth(vcRs.stretch_, stretchTotal, mainWinWidth);
+
+ vcRs.offset_ = width - stretchedWithCol; //width := stretchedWidth + offset
+
+ //CAVEATS:
+ //I. fixed-size columns: normalize offset so that resulting width is at least COLUMN_MIN_WIDTH: this is NOT enforced by getColWidths()!
+ //II. stretched columns: do not allow user to set offsets so small that they result in negative (non-normalized) widths: this gives an
+ //unusual delay when enlarging the column again later
+ vcRs.offset_ = std::max(vcRs.offset_, COLUMN_MIN_WIDTH - stretchedWithCol);
+
+ //III. resizing any column should normalize *all* other stretched columns' offsets considering current mainWinWidth!
+ // test case:
+ //1. have columns, both fixed-size and stretched, fit whole window width
+ //2. shrink main window width so that horizontal scrollbars are shown despite the streched column
+ //3. shrink a fixed-size column so that the scrollbars vanish and columns cover full width again
+ //4. now verify that the stretched column is resizing immediately if main window is enlarged again
std::for_each(comp.begin(), comp.end(), [&](Component& c)
{
std::for_each(c.visibleCols.begin(), c.visibleCols.end(), [&](VisibleColumn& vc)
{
- const ptrdiff_t stretchedWidth = Grid::getColStretchedWidth(vc.stretch_, stretchTotal, mainWinWidth);
- vc.offset_ = std::max(vc.offset_, COLUMN_MIN_WIDTH - stretchedWidth); //it would suffice to normalize stretched columns only
+ if (vc.stretch_ > 0) //normalize stretched columns only
+ {
+ const ptrdiff_t stretchedWidth = Grid::getColStretchedWidth(vc.stretch_, stretchTotal, mainWinWidth);
+ vc.offset_ = std::max(vc.offset_, COLUMN_MIN_WIDTH - stretchedWidth);
+ }
});
});
- GridColumnResizeEvent sizeEvent(offset, vcRs.type_, compPos);
+ GridColumnResizeEvent sizeEvent(vcRs.offset_, vcRs.type_, compPos);
if (wxEvtHandler* evtHandler = GetEventHandler())
{
if (notifyAsync)
@@ -2168,6 +2196,8 @@ void Grid::setColWidthAndNotify(ptrdiff_t width, size_t col, size_t compPos, boo
evtHandler->ProcessEvent(sizeEvent);
}
}
+ else
+ assert(false);
}
@@ -2200,7 +2230,7 @@ ptrdiff_t Grid::getStretchTotal() const //sum of all stretch factors
}
-ptrdiff_t Grid::getColStretchedWidth(ptrdiff_t stretch, ptrdiff_t stretchTotal, int mainWinWidth) //final width = stretchedWidth + (normalized) offset
+ptrdiff_t Grid::getColStretchedWidth(ptrdiff_t stretch, ptrdiff_t stretchTotal, int mainWinWidth) //final width := stretchedWidth + (normalized) offset
{
return stretchTotal > 0 ? mainWinWidth * stretch / stretchTotal : 0; //rounds down! => not all of clientWidth is correctly distributed according to stretch factors
}
@@ -2225,8 +2255,12 @@ std::vector<std::vector<Grid::ColumnWidth>> Grid::getColWidths(int mainWinWidth)
std::for_each(c.visibleCols.begin(), c.visibleCols.end(), [&](const VisibleColumn& vc)
{
- const ptrdiff_t stretchedWidth = Grid::getColStretchedWidth(vc.stretch_, stretchTotal, mainWinWidth);
- const ptrdiff_t widthNormalized = std::max(stretchedWidth + vc.offset_, static_cast<ptrdiff_t>(COLUMN_MIN_WIDTH));
+ ptrdiff_t widthNormalized = Grid::getColStretchedWidth(vc.stretch_, stretchTotal, mainWinWidth) + vc.offset_;
+
+ if (vc.stretch_ > 0)
+ widthNormalized = std::max(widthNormalized, static_cast<ptrdiff_t>(COLUMN_MIN_WIDTH)); //normalization really needed here: e.g. smaller main window would result in negative width
+ else
+ widthNormalized = std::max(widthNormalized, static_cast<ptrdiff_t>(0)); //support smaller width than COLUMN_MIN_WIDTH if set via configuration
compWidths.push_back(Grid::ColumnWidth(vc.type_, widthNormalized));
});
@@ -2245,3 +2279,4 @@ ptrdiff_t Grid::getColWidthsSum(int mainWinWidth) const
[](ptrdiff_t val2, const Grid::ColumnWidth& cw) { return val2 + cw.width_; });
});
};
+
diff --git a/wx+/grid.h b/wx+/grid.h
index 89926e00..11317fd8 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -20,7 +20,7 @@ namespace zen
{
typedef enum { DUMMY_COLUMN_TYPE = static_cast<unsigned int>(-1) } ColumnType;
-//----- Events -----------------------------------------------------------------------------------------------
+//----- events ------------------------------------------------------------------------
extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_LEFT; //generates: GridClickEvent
extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_RIGHT; //
extern const wxEventType EVENT_GRID_COL_RESIZE; //generates: GridColumnResizeEvent
@@ -33,17 +33,14 @@ extern const wxEventType EVENT_GRID_MOUSE_RIGHT_UP; //
extern const wxEventType EVENT_GRID_SELECT_RANGE; //generates: GridRangeSelectEvent
//NOTE: neither first nor second row need to match EVENT_GRID_MOUSE_LEFT_DOWN/EVENT_GRID_MOUSE_LEFT_UP: user holding SHIFT; moving out of window...
-//=> range always specifies *valid* rows
//example: wnd.Connect(EVENT_GRID_COL_LABEL_LEFT_CLICK, GridClickEventHandler(MyDlg::OnLeftClick), nullptr, this);
-
struct GridClickEvent : public wxMouseEvent
{
- GridClickEvent(wxEventType et, const wxMouseEvent& me, int row, ColumnType colType, size_t compPos) : wxMouseEvent(me), row_(row), colType_(colType), compPos_(compPos) { SetEventType(et); }
+ GridClickEvent(wxEventType et, const wxMouseEvent& me, ptrdiff_t row, ColumnType colType, size_t compPos) : wxMouseEvent(me), row_(row), colType_(colType), compPos_(compPos) { SetEventType(et); }
virtual wxEvent* Clone() const { return new GridClickEvent(*this); }
-
- const int row_;
+ const ptrdiff_t row_; //-1 for invalid position, >= rowCount if out of range
const ColumnType colType_;
const size_t compPos_;
};
@@ -60,11 +57,11 @@ struct GridColumnResizeEvent : public wxCommandEvent
struct GridRangeSelectEvent : public wxCommandEvent
{
- GridRangeSelectEvent(int rowFrom, int rowTo, size_t compPos, bool positive) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFrom_(rowFrom), rowTo_(rowTo), compPos_(compPos), positive_(positive) {}
+ GridRangeSelectEvent(size_t rowFirst, size_t rowLast, size_t compPos, bool positive) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFirst_(rowFirst), rowLast_(rowLast), compPos_(compPos), positive_(positive) { assert(rowFirst <= rowLast); }
virtual wxEvent* Clone() const { return new GridRangeSelectEvent(*this); }
- const int rowFrom_;
- const int rowTo_;
+ const size_t rowFirst_; //selected range: [rowFirst_, rowLast_)
+ const size_t rowLast_; //range is empty when clearing selection
const size_t compPos_;
const bool positive_;
};
@@ -168,7 +165,7 @@ public:
void showScrollBars(ScrollBarStatus horizontal, ScrollBarStatus vertical);
std::vector<size_t> getSelectedRows(size_t compPos = 0) const;
- void clearSelection(size_t compPos = 0);
+ void clearSelection(bool emitSelectRangeEvent = true, size_t compPos = 0); //turn off range selection event when calling this function in an event handler to avoid recursion!
void scrollDelta(int deltaX, int deltaY); //in scroll units
@@ -176,8 +173,9 @@ public:
wxWindow& getRowLabelWin();
wxWindow& getColLabelWin();
wxWindow& getMainWin ();
+ const wxWindow& getMainWin() const;
- ptrdiff_t getRowAtPos(int posY) const; //returns < 0 if column not found; absolute coordinates!
+ ptrdiff_t getRowAtPos(int posY) const; //return -1 for invalid position, >= rowCount if out of range; absolute coordinates!
Opt<std::pair<ColumnType, size_t>> getColumnAtPos(int posX) const; //returns (column type, component pos)
wxRect getCellArea(size_t row, ColumnType colType, size_t compPos = 0) const; //returns empty rect if column not found; absolute coordinates!
@@ -185,7 +183,7 @@ public:
void enableColumnMove (bool value, size_t compPos = 0) { if (compPos < comp.size()) comp[compPos].allowColumnMove = value; }
void enableColumnResize(bool value, size_t compPos = 0) { if (compPos < comp.size()) comp[compPos].allowColumnResize = value; }
- void setGridCursor(size_t row, size_t compPos = 0); //set + show + select cursor
+ void setGridCursor(size_t row, size_t compPos = 0); //set + show + select cursor (+ emit range selection event)
std::pair<size_t, size_t> getGridCursor() const; //(row, component pos)
void scrollTo(size_t row);
@@ -193,6 +191,7 @@ public:
virtual void Refresh(bool eraseBackground = true, const wxRect* rect = nullptr);
virtual bool Enable( bool enable = true) { Refresh(); return wxScrolledWindow::Enable(enable); }
void autoSizeColumns(size_t compPos = 0);
+ //############################################################################################################
private:
void onPaintEvent(wxPaintEvent& event);
@@ -235,15 +234,16 @@ private:
bool isSelected(size_t row) const { return row < rowSelectionValue.size() ? rowSelectionValue[row] != 0 : false; }
- void selectRange(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive = true) //select [rowFrom, rowTo], very tolerant: trims and swaps if required!
+ void selectRange(size_t rowFirst, size_t rowLast, bool positive = true) //select [rowFirst, rowLast), trims if required!
{
- auto rowFirst = std::min(rowFrom, rowTo);
- auto rowLast = std::max(rowFrom, rowTo) + 1;
-
- numeric::confine<ptrdiff_t>(rowFirst, 0, rowSelectionValue.size());
- numeric::confine<ptrdiff_t>(rowLast, 0, rowSelectionValue.size());
-
- std::fill(rowSelectionValue.begin() + rowFirst, rowSelectionValue.begin() + rowLast, positive);
+ if (rowFirst <= rowLast)
+ {
+ numeric::confine<size_t>(rowFirst, 0, rowSelectionValue.size());
+ numeric::confine<size_t>(rowLast, 0, rowSelectionValue.size());
+
+ std::fill(rowSelectionValue.begin() + rowFirst, rowSelectionValue.begin() + rowLast, positive);
+ }
+ else assert(false);
}
private:
@@ -294,50 +294,11 @@ private:
void setColWidthAndNotify(ptrdiff_t width, size_t col, size_t compPos, bool notifyAsync = false);
- //ptrdiff_t getNormalizedColOffset(ptrdiff_t offset, ptrdiff_t stretchedWidth) const; //normalize so that "stretchedWidth + offset" gives reasonable width!
-
- //Opt<ptrdiff_t> getColOffsetNorm(size_t col, size_t compPos) const //returns *normalized* offset!
- // {
- // if (compPos < comp.size() && col < comp[compPos].visibleCols.size())
- // {
- // const VisibleColumn& vc = comp[compPos].visibleCols[col];
- // return getNormalizedColOffset(vc.offset_, getColStretchedWidth(vc.stretch_));
- // }
- // return NoValue();
- // }
-
- //Opt<VisibleColumn> getColAttrib(size_t col, size_t compPos) const
- //{
- // if (compPos < comp.size() && col < comp[compPos].visibleCols.size())
- // return comp[compPos].visibleCols[col];
- // return NoValue();
- //}
-
- //Opt<ptrdiff_t> getColStretchedWidth(size_t col, size_t compPos) const
- // {
- // if (compPos < comp.size() && col < comp[compPos].visibleCols.size())
- // {
- // const VisibleColumn& vc = comp[compPos].visibleCols[col];
- // return getColStretchedWidth(vc.stretch_);
- // }
- // return NoValue();
- // }
-
-
- //void setColOffset(size_t col, size_t compPos, ptrdiff_t offset)
- // {
- // if (compPos < comp.size() && col < comp[compPos].visibleCols.size())
- // {
- // VisibleColumn& vc = comp[compPos].visibleCols[col];
- // vc.offset_ = offset;
- // }
- // }
-
wxRect getColumnLabelArea(ColumnType colType, size_t compPos) const; //returns empty rect if column not found
- void selectRange(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool positive = true); //select range + notify event!
+ void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool positive = true); //select inclusive range [rowFrom, rowTo] + notify event!
- void clearSelectionAll(); //clear selection + notify event
+ void clearSelectionAllAndNotify(); //clear selection + notify event
bool isSelected(size_t row, size_t compPos) const { return compPos < comp.size() ? comp[compPos].selection.isSelected(row) : false; }
@@ -359,9 +320,9 @@ private:
/*
Visual layout:
------------------------------------------------
- |CornerWin | RowLabelWin: |
+ |CornerWin | ColLabelWin: |
|-------------------------- Comp1 | Comp2 ... | row label and main window are vertically tiled into one or more "components"
- |ColLabelWin | MainWin: |
+ |RowLabelWin | MainWin: |
------------------------------------------------
*/
CornerWin* cornerWin_;
diff --git a/wx+/zlib_wrap.h b/wx+/zlib_wrap.h
index a5ad2cb1..c6545c9d 100644
--- a/wx+/zlib_wrap.h
+++ b/wx+/zlib_wrap.h
@@ -111,7 +111,7 @@ BinContainer decompress(const BinContainer& stream) //throw ZlibInternalError
const size_t bytesWritten = impl::zlib_decompress(&*stream.begin() + sizeof(uncompressedSize),
stream.size() - sizeof(uncompressedSize),
&*contOut.begin(),
- uncompressedSize); //throw ZlibInternalError
+ static_cast<size_t>(uncompressedSize)); //throw ZlibInternalError
if (bytesWritten != static_cast<size_t>(uncompressedSize))
throw ZlibInternalError();
}
bgstack15