diff options
Diffstat (limited to 'wx+/grid.cpp')
-rw-r--r-- | wx+/grid.cpp | 767 |
1 files changed, 311 insertions, 456 deletions
diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 184302bf..87f81dda 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -297,6 +297,7 @@ protected: SetToolTip(new wxToolTip(L"a b\n\ a b")); //ugly, but is working (on Windows) tt = GetToolTip(); //should be bound by now + assert(tt); if (tt) tt->SetTip(text); } @@ -542,8 +543,8 @@ namespace class ColumnResizing { public: - ColumnResizing(wxWindow& wnd, size_t col, size_t compPos, int startWidth, int clientPosX) : - wnd_(wnd), col_(col), compPos_(compPos), startWidth_(startWidth), clientPosX_(clientPosX) + ColumnResizing(wxWindow& wnd, size_t col, int startWidth, int clientPosX) : + wnd_(wnd), col_(col), startWidth_(startWidth), clientPosX_(clientPosX) { wnd_.CaptureMouse(); } @@ -554,14 +555,12 @@ public: } size_t getColumn () const { return col_; } - size_t getComponentPos() const { return compPos_; } int getStartWidth () const { return startWidth_; } int getStartPosX () const { return clientPosX_; } private: wxWindow& wnd_; const size_t col_; - const size_t compPos_; const int startWidth_; const int clientPosX_; }; @@ -570,10 +569,9 @@ private: class ColumnMove { public: - ColumnMove(wxWindow& wnd, size_t colFrom, size_t compPos, int clientPosX) : + ColumnMove(wxWindow& wnd, size_t colFrom, int clientPosX) : wnd_(wnd), colFrom_(colFrom), - compPos_(compPos), colTo_(colFrom), clientPosX_(clientPosX), singleClick_(true) { wnd_.CaptureMouse(); } @@ -581,7 +579,6 @@ public: size_t getColumnFrom() const { return colFrom_; } size_t& refColumnTo() { return colTo_; } - size_t getComponentPos() const { return compPos_; } int getStartPosX () const { return clientPosX_; } bool isRealMove() const { return !singleClick_; } @@ -590,7 +587,6 @@ public: private: wxWindow& wnd_; const size_t colFrom_; - const size_t compPos_; size_t colTo_; const int clientPosX_; bool singleClick_; @@ -625,36 +621,35 @@ private: wxPoint labelAreaTL(refParent().CalcScrolledPosition(wxPoint(0, 0)).x, 0); //client coordinates - std::vector<std::vector<ColumnWidth>> compAbsWidths = refParent().getColWidths(); //resolve stretched widths - for (auto iterComp = compAbsWidths.begin(); iterComp != compAbsWidths.end(); ++iterComp) - for (auto iterCol = iterComp->begin(); iterCol != iterComp->end(); ++iterCol) - { - const size_t col = iterCol - iterComp->begin(); - const int width = iterCol->width_; //don't use unsigned for calculations! - - if (labelAreaTL.x > rect.GetRight()) - return; //done - if (labelAreaTL.x + width > rect.x) - drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(width, colLabelHeight)), col, iterCol->type_, iterComp - compAbsWidths.begin()); - labelAreaTL.x += width; - } + std::vector<ColumnWidth> absWidths = refParent().getColWidths(); //resolve stretched widths + for (auto it = absWidths.begin(); it != absWidths.end(); ++it) + { + const size_t col = it - absWidths.begin(); + const int width = it->width_; //don't use unsigned for calculations! + + if (labelAreaTL.x > rect.GetRight()) + return; //done + if (labelAreaTL.x + width > rect.x) + drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(width, colLabelHeight)), col, it->type_); + labelAreaTL.x += width; + } } - void drawColumnLabel(wxDC& dc, const wxRect& rect, size_t col, ColumnType colType, size_t compPos) + void drawColumnLabel(wxDC& dc, const wxRect& rect, size_t col, ColumnType colType) { - if (auto dataView = refParent().getDataProvider(compPos)) + if (auto dataView = refParent().getDataProvider()) { - const bool isHighlighted = activeResizing ? col == activeResizing->getColumn () && compPos == activeResizing->getComponentPos() : //highlight column on mouse-over - activeMove ? col == activeMove ->getColumnFrom() && compPos == activeMove ->getComponentPos() : - highlight ? col == highlight->first && compPos == highlight->second : + const bool isHighlighted = activeResizing ? col == activeResizing->getColumn () : //highlight column on mouse-over + activeMove ? col == activeMove ->getColumnFrom() : + highlightCol ? col == *highlightCol : false; RecursiveDcClipper clip(dc, rect); dataView->renderColumnLabel(refParent(), dc, rect, colType, isHighlighted); //draw move target location - if (refParent().columnMoveAllowed(compPos)) - if (activeMove && activeMove->isRealMove() && activeMove->getComponentPos() == compPos) + if (refParent().allowColumnMove) + if (activeMove && activeMove->isRealMove()) { if (col + 1 == activeMove->refColumnTo()) //handle pos 1, 2, .. up to "at end" position dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight() + wxPoint(-2, 0)), COLOR_LABEL_GRADIENT_FROM, *wxBLUE, wxSOUTH); @@ -677,11 +672,11 @@ private: if (action->wantResize) { if (!event.LeftDClick()) //double-clicks never seem to arrive here; why is this checked at all??? - if (Opt<int> colWidth = refParent().getColWidth(action->col, action->compPos)) - activeResizing.reset(new ColumnResizing(*this, action->col, action->compPos, *colWidth, event.GetPosition().x)); + if (Opt<int> colWidth = refParent().getColWidth(action->col)) + activeResizing.reset(new ColumnResizing(*this, action->col, *colWidth, event.GetPosition().x)); } else //a move or single click - activeMove.reset(new ColumnMove(*this, action->col, action->compPos, event.GetPosition().x)); + activeMove.reset(new ColumnMove(*this, action->col, event.GetPosition().x)); } event.Skip(); } @@ -692,10 +687,9 @@ private: if (activeMove) { - const size_t compPos = activeMove->getComponentPos(); if (activeMove->isRealMove()) { - if (refParent().columnMoveAllowed(compPos)) + if (refParent().allowColumnMove) { const auto colFrom = activeMove->getColumnFrom(); auto colTo = activeMove->refColumnTo(); @@ -703,13 +697,13 @@ private: if (colTo > colFrom) //simulate "colFrom" deletion --colTo; - refParent().moveColumn(colFrom, colTo, compPos); + refParent().moveColumn(colFrom, colTo); } } else //notify single label click { - if (const Opt<ColumnType> colType = refParent().colToType(activeMove->getColumnFrom(), compPos)) - sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, -1, *colType, compPos)); + if (const Opt<ColumnType> colType = refParent().colToType(activeMove->getColumnFrom())) + sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, -1, *colType)); } activeMove.reset(); } @@ -733,10 +727,10 @@ private: if (action->wantResize) { //auto-size visible range on double-click - const int bestWidth = refParent().getBestColumnSize(action->col, action->compPos); //return -1 on error + const int bestWidth = refParent().getBestColumnSize(action->col); //return -1 on error if (bestWidth >= 0) { - refParent().setColWidthAndNotify(bestWidth, action->col, action->compPos); + refParent().setColumnWidth(bestWidth, action->col, ALLOW_GRID_EVENT); refParent().Refresh(); //refresh main grid as well! } } @@ -748,16 +742,15 @@ private: if (activeResizing) { const auto col = activeResizing->getColumn(); - const auto compPos = activeResizing->getComponentPos(); const int newWidth = activeResizing->getStartWidth() + event.GetPosition().x - activeResizing->getStartPosX(); //set width tentatively - refParent().setColWidthAndNotify(newWidth, col, compPos); + refParent().setColumnWidth(newWidth, col, ALLOW_GRID_EVENT); //check if there's a small gap after last column, if yes, fill it int gapWidth = GetClientSize().GetWidth() - refParent().getColWidthsSum(GetClientSize().GetWidth()); if (std::abs(gapWidth) < COLUMN_FILL_GAP_TOLERANCE) - refParent().setColWidthAndNotify(newWidth + gapWidth, col, compPos); + refParent().setColumnWidth(newWidth + gapWidth, col, ALLOW_GRID_EVENT); refParent().Refresh(); //refresh columns on main grid as well! } @@ -768,7 +761,7 @@ private: { activeMove->setRealMove(); - const auto col = refParent().clientPosToMoveTargetColumn(event.GetPosition(), activeMove->getComponentPos()); + const ptrdiff_t col = refParent().clientPosToMoveTargetColumn(event.GetPosition()); if (col >= 0) activeMove->refColumnTo() = col; } @@ -777,7 +770,7 @@ private: { if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { - highlight.reset(new std::pair<size_t, size_t>(action->col, action->compPos)); + highlightCol.reset(new size_t(action->col)); if (action->wantResize) SetCursor(wxCURSOR_SIZEWE); //set window-local only! :) @@ -786,7 +779,7 @@ private: } else { - highlight.reset(); + highlightCol.reset(); SetCursor(*wxSTANDARD_CURSOR); } } @@ -795,11 +788,9 @@ private: const wxString toolTip = [&]() -> wxString { const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); - if (const auto colInfo = refParent().getColumnAtPos(absPos.x)) //returns (column type, compPos) - { - if (auto prov = refParent().getDataProvider(colInfo->second)) - return prov->getToolTip(colInfo->first); - } + if (const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x)) + if (auto prov = refParent().getDataProvider()) + return prov->getToolTip(*ct); return wxString(); }(); setToolTip(toolTip); @@ -810,7 +801,7 @@ private: virtual void onLeaveWindow(wxMouseEvent& event) { - highlight.reset(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize! + highlightCol.reset(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize! Refresh(); event.Skip(); } @@ -819,15 +810,15 @@ private: { if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { - if (const Opt<ColumnType> colType = refParent().colToType(action->col, action->compPos)) - sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, -1, *colType, action->compPos)); //notify right click + if (const Opt<ColumnType> colType = refParent().colToType(action->col)) + sendEventNow(GridClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, -1, *colType)); //notify right click } event.Skip(); } std::unique_ptr<ColumnResizing> activeResizing; std::unique_ptr<ColumnMove> activeMove; - std::unique_ptr<std::pair<size_t, size_t>> highlight; //(column, component) mouse-over + std::unique_ptr<size_t> highlightCol; //column during mouse-over }; //---------------------------------------------------------------------------------------------------------------- @@ -893,13 +884,13 @@ public: } } - void setCursor(size_t row, size_t compPos) + void setCursor(size_t row) { - cursor = std::make_pair(row, compPos); + cursorRow = row; activeSelection.reset(); //e.g. user might search with F3 while holding down left mouse button selectionAnchor = row; } - std::pair<size_t, size_t> getCursor() const { return cursor; } // (row, component position) + size_t getCursor() const { return cursorRow; } private: virtual void render(wxDC& dc, const wxRect& rect) @@ -924,27 +915,25 @@ private: wxPoint cellAreaTL(refParent().CalcScrolledPosition(wxPoint(0, 0))); //client coordinates - std::vector<std::vector<ColumnWidth>> compAbsWidths = refParent().getColWidths(); //resolve stretched widths - for (auto iterComp = compAbsWidths.begin(); iterComp != compAbsWidths.end(); ++iterComp) + std::vector<ColumnWidth> absWidths = refParent().getColWidths(); //resolve stretched widths { - int compWidth = 0; - for (const ColumnWidth& cw : *iterComp) - compWidth += cw.width_; + int totalWidth = 0; + for (const ColumnWidth& cw : absWidths) + totalWidth += cw.width_; - const size_t compPos = iterComp - compAbsWidths.begin(); - if (auto prov = refParent().getDataProvider(compPos)) + if (auto prov = refParent().getDataProvider()) { //draw background lines for (int row = rowFirst; row < rowLast; ++row) { - const wxRect rowRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(compWidth, rowHeight)); + const wxRect rowRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(totalWidth, rowHeight)); RecursiveDcClipper dummy2(dc, rowRect); //solve issues with drawBackground() painting in area outside of rect //(which is not also refreshed by renderCell()) -> keep small scope! - prov->renderRowBackgound(dc, rowRect, row, refParent().IsThisEnabled(), drawAsSelected(row, compPos)); + prov->renderRowBackgound(dc, rowRect, row, refParent().IsThisEnabled(), drawAsSelected(row)); } //draw single cells, column by column - for (const ColumnWidth& cw : *iterComp) + for (const ColumnWidth& cw : absWidths) { if (cellAreaTL.x > rect.GetRight()) return; //done @@ -955,27 +944,25 @@ private: const wxRect cellRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width_, rowHeight); RecursiveDcClipper clip(dc, cellRect); - prov->renderCell(dc, cellRect, row, cw.type_, drawAsSelected(row, compPos)); + prov->renderCell(dc, cellRect, row, cw.type_, drawAsSelected(row)); } cellAreaTL.x += cw.width_; } } - else - cellAreaTL.x += compWidth; } } - bool drawAsSelected(size_t row, size_t compPos) const + bool drawAsSelected(size_t row) const { if (activeSelection) //check if user is currently selecting with mouse { 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) + if (rowFrom <= row && row <= rowTo) return activeSelection->isPositiveSelect(); //overwrite default } - return refParent().isSelected(row, compPos); + return refParent().isSelected(row); } virtual void onMouseLeftDown (wxMouseEvent& event) { onMouseDown(event); } @@ -989,12 +976,10 @@ private: 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; + const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x); + const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE; //client is interested in all double-clicks, even those outside of the grid! - sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType, compPos)); + sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType)); } event.Skip(); } @@ -1008,34 +993,28 @@ private: 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; + const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x); + const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE; - if (!event.RightDown() || !refParent().isSelected(row, compPos)) //do NOT start a new selection if user right-clicks on a selected area! + if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area! { if (event.ControlDown()) - { - if (compPos >= 0) - activeSelection.reset(new MouseSelection(*this, row, compPos, !refParent().isSelected(row, compPos))); - } + activeSelection.reset(new MouseSelection(*this, row, !refParent().isSelected(row))); else if (event.ShiftDown()) { - if (compPos >= 0) - activeSelection.reset(new MouseSelection(*this, selectionAnchor, compPos, true)); - refParent().clearSelectionAllAndNotify(); + activeSelection.reset(new MouseSelection(*this, selectionAnchor, true)); + refParent().clearSelection(ALLOW_GRID_EVENT); } else { - if (compPos >= 0) - activeSelection.reset(new MouseSelection(*this, row, compPos, true)); - refParent().clearSelectionAllAndNotify(); + activeSelection.reset(new MouseSelection(*this, row, true)); + refParent().clearSelection(ALLOW_GRID_EVENT); } } - //notify event *after* potential "clearSelectionAllAndNotify()" above: a client should first receive a GridRangeSelectEvent for clearing the grid, if necessary, + //notify event *after* potential "clearSelection(true)" 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); + GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, colType); sendEventNow(mouseEvent); Refresh(); @@ -1052,22 +1031,21 @@ private: { if (activeSelection->getCurrentRow() < rowCount) { - cursor.first = activeSelection->getCurrentRow(); + cursorRow = 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; + cursorRow = rowCount - 1; selectionAnchor = activeSelection->getStartRow(); //allowed to be "out of range" } else //total selection "out of range" - selectionAnchor = cursor.first; + selectionAnchor = cursorRow; } //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(); } @@ -1075,14 +1053,12 @@ private: //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 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; + const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range + const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x); + const ColumnType colType = ct ? *ct : DUMMY_COLUMN_TYPE; //we probably should notify even if colInfo is invalid! //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)); + sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, colType)); Refresh(); event.Skip(); //allow changing focus @@ -1106,13 +1082,11 @@ private: const ptrdiff_t rowCount = refParent().getRowCount(); const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); - 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 && 0 <= row && row < rowCount) - { - if (auto prov = refParent().getDataProvider(colInfo->second)) - return prov->getToolTip(row, colInfo->first); - } + const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range + const Opt<ColumnType> ct = refParent().getColumnAtPos(absPos.x); + if (ct && 0 <= row && row < rowCount) + if (auto prov = refParent().getDataProvider()) + return prov->getToolTip(row, *ct); return wxString(); }(); @@ -1137,17 +1111,16 @@ private: } const ptrdiff_t rowCount = refParent().getRowCount(); - if (rowCount <= 0 || refParent().comp.empty()) + if (rowCount <= 0) { event.Skip(); return; } - auto setSingleSelection = [&](ptrdiff_t row, ptrdiff_t compPos) + auto setSingleSelection = [&](ptrdiff_t row) { - numeric::confine<ptrdiff_t>(row, 0, rowCount - 1); - numeric::confine<ptrdiff_t>(compPos, 0, refParent().comp.size() - 1); - refParent().setGridCursor(row, compPos); //behave like an "external" set cursor! + numeric::confine<ptrdiff_t>(row, 0, rowCount - 1); + refParent().setGridCursor(row); //behave like an "external" set cursor! }; auto setSelectionRange = [&](ptrdiff_t row) @@ -1156,11 +1129,10 @@ private: numeric::confine<ptrdiff_t>(row, 0, rowCount - 1); - for (Grid::Component& c : refParent().comp) - c.selection.clear(); //clear selection, do NOT fire event - refParent().selectRangeAndNotify(selectionAnchor, row, cursor.second); //set new selection + fire event + refParent().selection.clear(); //clear selection, do NOT fire event + refParent().selectRangeAndNotify(selectionAnchor, row); //set new selection + fire event - cursor.first = row; //don't call setCursor() since it writes to "selectionAnchor"! + cursorRow = row; //don't call setCursor() since it writes to "selectionAnchor"! this->makeRowVisible(row); refParent().Refresh(); }; @@ -1170,21 +1142,21 @@ private: case WXK_UP: case WXK_NUMPAD_UP: if (event.ShiftDown()) - setSelectionRange(cursor.first - 1); + setSelectionRange(cursorRow - 1); else if (event.ControlDown()) refParent().scrollDelta(0, -1); else - setSingleSelection(cursor.first - 1, cursor.second); + setSingleSelection(cursorRow - 1); return; //swallow event: wxScrolledWindow, wxWidgets 2.9.3 on Kubuntu x64 processes arrow keys: prevent this! case WXK_DOWN: case WXK_NUMPAD_DOWN: if (event.ShiftDown()) - setSelectionRange(cursor.first + 1); + setSelectionRange(cursorRow + 1); else if (event.ControlDown()) refParent().scrollDelta(0, 1); else - setSingleSelection(cursor.first + 1, cursor.second); + setSingleSelection(cursorRow + 1); return; //swallow event case WXK_LEFT: @@ -1194,7 +1166,7 @@ private: else if (event.ShiftDown()) ; else - setSingleSelection(cursor.first, cursor.second - 1); + setSingleSelection(cursorRow); return; case WXK_RIGHT: @@ -1204,7 +1176,7 @@ private: else if (event.ShiftDown()) ; else - setSingleSelection(cursor.first, cursor.second + 1); + setSingleSelection(cursorRow); return; case WXK_HOME: @@ -1212,9 +1184,9 @@ private: if (event.ShiftDown()) setSelectionRange(0); else if (event.ControlDown()) - setSingleSelection(0, 0); + setSingleSelection(0); else - setSingleSelection(0, cursor.second); + setSingleSelection(0); return; case WXK_END: @@ -1222,39 +1194,39 @@ private: if (event.ShiftDown()) setSelectionRange(rowCount - 1); else if (event.ControlDown()) - setSingleSelection(rowCount - 1, refParent().comp.size() - 1); + setSingleSelection(rowCount - 1); else - setSingleSelection(rowCount - 1, cursor.second); + setSingleSelection(rowCount - 1); return; case WXK_PAGEUP: case WXK_NUMPAD_PAGEUP: if (event.ShiftDown()) - setSelectionRange(cursor.first - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); + setSelectionRange(cursorRow - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); else if (event.ControlDown()) ; else - setSingleSelection(cursor.first - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight(), cursor.second); + setSingleSelection(cursorRow - GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); return; case WXK_PAGEDOWN: case WXK_NUMPAD_PAGEDOWN: if (event.ShiftDown()) - setSelectionRange(cursor.first + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); + setSelectionRange(cursorRow + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); else if (event.ControlDown()) ; else - setSingleSelection(cursor.first + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight(), cursor.second); + setSingleSelection(cursorRow + GetClientSize().GetHeight() / rowLabelWin_.getRowHeight()); return; case 'A': //Ctrl + A - select all if (event.ControlDown()) - refParent().selectRangeAndNotify(0, rowCount, cursor.second); + refParent().selectRangeAndNotify(0, rowCount); break; case WXK_NUMPAD_ADD: //CTRL + '+' - auto-size all if (event.ControlDown()) - refParent().autoSizeColumns(cursor.second); + refParent().autoSizeColumns(ALLOW_GRID_EVENT); return; } @@ -1266,8 +1238,8 @@ private: class MouseSelection : private wxEvtHandler { public: - 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), + MouseSelection(MainWin& wnd, size_t rowStart, bool positiveSelect) : + wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect), toScrollX(0), toScrollY(0), tickCountLast(getTicks()), ticksPerSec_(ticksPerSec()) { @@ -1279,7 +1251,6 @@ private: ~MouseSelection() { if (wnd_.HasCapture()) wnd_.ReleaseMouse(); } 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? @@ -1352,7 +1323,6 @@ private: MainWin& wnd_; const size_t rowStart_; - const size_t compPos_; ptrdiff_t rowCurrent_; const bool positiveSelect_; wxTimer timer; @@ -1397,7 +1367,7 @@ private: std::unique_ptr<MouseSelection> activeSelection; //bound while user is selecting with mouse - std::pair<ptrdiff_t, ptrdiff_t> cursor; //(row, component position), always valid! still unsigned type to facilitate "onKeyDown()" + ptrdiff_t cursorRow; size_t selectionAnchor; bool gridUpdatePending; }; @@ -1415,7 +1385,8 @@ Grid::Grid(wxWindow* parent, showScrollbarY(SB_SHOW_AUTOMATIC), colLabelHeight(0), //dummy init drawRowLabel(true), - comp(1), + allowColumnMove(true), + allowColumnResize(true), rowCountOld(0) { cornerWin_ = new CornerWin (*this); // @@ -1617,23 +1588,31 @@ void Grid::showRowLabel(bool show) } -std::vector<size_t> Grid::getSelectedRows(size_t compPos) const +void Grid::selectAllRows(GridEventPolicy rangeEventPolicy) { - if (compPos < comp.size()) - return comp[compPos].selection.get(); - assert(false); - return std::vector<size_t>(); + selection.selectAll(); + mainWin_->Refresh(); + + if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction + { + GridRangeSelectEvent selEvent(0, getRowCount(), true); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->ProcessEvent(selEvent); + } } -void Grid::setSelectedRows(const std::vector<size_t>& sel, size_t compPos) +void Grid::clearSelection(GridEventPolicy rangeEventPolicy) { - if (compPos < comp.size()) + selection.clear(); + mainWin_->Refresh(); + + if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction { - comp[compPos].selection.set(sel); - Refresh(); + GridRangeSelectEvent unselectionEvent(0, getRowCount(), false); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->ProcessEvent(unselectionEvent); } - else assert(false); } @@ -1667,7 +1646,7 @@ void Grid::redirectRowLabelEvent(wxMouseEvent& event) size_t Grid::getRowCount() const { - return comp.empty() ? 0 : comp.front().dataView_ ? comp.front().dataView_->getRowCount() : 0; + return dataView_ ? dataView_->getRowCount() : 0; } @@ -1680,9 +1659,8 @@ void Grid::Refresh(bool eraseBackground, const wxRect* rect) updateWindowSizes(); } - for (Component& c : comp) - if (c.selection.size() != rowCountNew) //clear selection only when needed (consider setSelectedRows()) - c.selection.init(rowCountNew); + if (selection.size() != rowCountNew) //clear selection only when needed (consider setSelectedRows()) + selection.init(rowCountNew); wxScrolledWindow::Refresh(eraseBackground, rect); } @@ -1696,56 +1674,49 @@ void Grid::setRowHeight(int height) } -void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr, size_t compPos) +void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr) { - if (compPos < comp.size()) - { - //hold ownership of non-visible columns - comp[compPos].oldColAttributes = attr; + //hold ownership of non-visible columns + oldColAttributes = attr; - std::vector<VisibleColumn> visibleCols; - for (const ColumnAttribute& ca : attr) - if (ca.visible_) - visibleCols.push_back(VisibleColumn(ca.type_, ca.offset_, ca.stretch_)); + std::vector<VisibleColumn> visCols; + for (const ColumnAttribute& ca : attr) + if (ca.visible_) + visCols.push_back(VisibleColumn(ca.type_, ca.offset_, ca.stretch_)); - //"ownership" of visible columns is now within Grid - comp[compPos].visibleCols = visibleCols; + //"ownership" of visible columns is now within Grid + visibleCols = visCols; - updateWindowSizes(); - Refresh(); - } + updateWindowSizes(); + Refresh(); } -std::vector<Grid::ColumnAttribute> Grid::getColumnConfig(size_t compPos) const +std::vector<Grid::ColumnAttribute> Grid::getColumnConfig() const { - if (compPos < comp.size()) - { - //get non-visible columns (+ outdated visible ones) - std::vector<ColumnAttribute> output = comp[compPos].oldColAttributes; + //get non-visible columns (+ outdated visible ones) + std::vector<ColumnAttribute> output = oldColAttributes; - auto iterVcols = comp[compPos].visibleCols.begin(); - auto iterVcolsend = comp[compPos].visibleCols.end(); + auto iterVcols = visibleCols.begin(); + auto iterVcolsend = visibleCols.end(); - //update visible columns but keep order of non-visible ones! - for (ColumnAttribute& ca : output) - if (ca.visible_) + //update visible columns but keep order of non-visible ones! + for (ColumnAttribute& ca : output) + if (ca.visible_) + { + if (iterVcols != iterVcolsend) { - if (iterVcols != iterVcolsend) - { - ca.type_ = iterVcols->type_; - ca.stretch_ = iterVcols->stretch_; - ca.offset_ = iterVcols->offset_; - ++iterVcols; - } - else - assert(false); + ca.type_ = iterVcols->type_; + ca.stretch_ = iterVcols->stretch_; + ca.offset_ = iterVcols->offset_; + ++iterVcols; } - assert(iterVcols == iterVcolsend); + else + assert(false); + } + assert(iterVcols == iterVcolsend); - return output; - } - return std::vector<ColumnAttribute>(); + return output; } @@ -1841,34 +1812,6 @@ void Grid::SetScrollbar(int orientation, int position, int thumbSize, int range, } #endif -//get rid of scrollbars, but preserve scrolling behavior! -#ifdef ZEN_WIN -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 - if (nMsg == WM_MOUSEHWHEEL) //horizontal wheel - { - const int distance = GET_WHEEL_DELTA_WPARAM(wParam); - const int delta = WHEEL_DELTA; - int rotations = distance / delta; - - if (GetLayoutDirection() == wxLayout_RightToLeft) - rotations = -rotations; - - static int linesPerRotation = -1; - if (linesPerRotation < 0) - if (!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerRotation, 0)) - linesPerRotation = 3; - - scrollDelta(rotations * linesPerRotation, 0); //in scroll units - return 0; //"If an application processes this message, it should return zero." - } - - return wxScrolledWindow::MSWDefWindowProc(nMsg, wParam, lParam); -} -#endif - wxWindow& Grid::getCornerWin () { return *cornerWin_; } wxWindow& Grid::getRowLabelWin() { return *rowLabelWin_; } @@ -1877,27 +1820,20 @@ wxWindow& Grid::getMainWin () { return *mainWin_; } const wxWindow& Grid::getMainWin() const { return *mainWin_; } -wxRect Grid::getColumnLabelArea(ColumnType colType, size_t compPos) const +wxRect Grid::getColumnLabelArea(ColumnType colType) const { - std::vector<std::vector<ColumnWidth>> compAbsWidths = getColWidths(); //resolve negative/stretched widths - if (compPos < compAbsWidths.size()) - { - auto iterComp = compAbsWidths.begin() + compPos; - - auto iterCol = std::find_if(iterComp->begin(), iterComp->end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; }); - if (iterCol != iterComp->end()) - { - ptrdiff_t posX = 0; - for (auto it = compAbsWidths.begin(); it != iterComp; ++it) - for (const ColumnWidth& cw : *it) - posX += cw.width_; + std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths - for (auto it = iterComp->begin(); it != iterCol; ++it) - posX += it->width_; + auto iterCol = std::find_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; }); + if (iterCol != absWidths.end()) + { + ptrdiff_t posX = 0; + for (auto it = absWidths.begin(); it != iterCol; ++it) + posX += it->width_; - return wxRect(wxPoint(posX, 0), wxSize(iterCol->width_, colLabelHeight)); - } + return wxRect(wxPoint(posX, 0), wxSize(iterCol->width_, colLabelHeight)); } + return wxRect(); } @@ -1907,32 +1843,26 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const const int absPosX = CalcUnscrolledPosition(pos).x; if (absPosX >= 0) { - int accuWidth = 0; + const int resizeTolerance = allowColumnResize ? COLUMN_RESIZE_TOLERANCE : 0; + std::vector<ColumnWidth> absWidths = getColWidths(); //resolve stretched widths - std::vector<std::vector<ColumnWidth>> compAbsWidths = getColWidths(); //resolve stretched widths - for (size_t compPos = 0; compPos < compAbsWidths.size(); ++compPos) + int accuWidth = 0; + for (size_t col = 0; col < absWidths.size(); ++col) { - const int resizeTolerance = columnResizeAllowed(compPos) ? COLUMN_RESIZE_TOLERANCE : 0; - - for (size_t col = 0; col < compAbsWidths[compPos].size(); ++col) + accuWidth += absWidths[col].width_; + if (std::abs(absPosX - accuWidth) < resizeTolerance) { - accuWidth += compAbsWidths[compPos][col].width_; - if (std::abs(absPosX - accuWidth) < resizeTolerance) - { - ColAction out = {}; - out.wantResize = true; - out.col = col; - out.compPos = compPos; - return out; - } - else if (absPosX < accuWidth) - { - ColAction out = {}; - out.wantResize = false; - out.col = col; - out.compPos = compPos; - return out; - } + ColAction out = {}; + out.wantResize = true; + out.col = col; + return out; + } + else if (absPosX < accuWidth) + { + ColAction out = {}; + out.wantResize = false; + out.col = col; + return out; } } } @@ -1940,54 +1870,42 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const } -void Grid::moveColumn(size_t colFrom, size_t colTo, size_t compPos) +void Grid::moveColumn(size_t colFrom, size_t colTo) { - if (compPos < comp.size()) + if (colFrom < visibleCols.size() && + colTo < visibleCols.size() && + colTo != colFrom) { - auto& visibleCols = comp[compPos].visibleCols; - if (colFrom < visibleCols.size() && - colTo < visibleCols.size() && - colTo != colFrom) - { - const auto colAtt = visibleCols[colFrom]; - visibleCols.erase (visibleCols.begin() + colFrom); - visibleCols.insert(visibleCols.begin() + colTo, colAtt); - } + const auto colAtt = visibleCols[colFrom]; + visibleCols.erase (visibleCols.begin() + colFrom); + visibleCols.insert(visibleCols.begin() + colTo, colAtt); } } -ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos, size_t compPos) const +ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos) const { - std::vector<std::vector<ColumnWidth>> compAbsWidths = getColWidths(); //resolve negative/stretched widths - if (compPos < compAbsWidths.size()) - { - auto iterComp = compAbsWidths.begin() + compPos; - const int absPosX = CalcUnscrolledPosition(pos).x; + std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths - int accuWidth = 0; - for (auto it = compAbsWidths.begin(); it != iterComp; ++it) - for (const ColumnWidth& cw : *it) - accuWidth += cw.width_; + const int absPosX = CalcUnscrolledPosition(pos).x; - for (auto iterCol = iterComp->begin(); iterCol != iterComp->end(); ++iterCol) - { - const int width = iterCol->width_; //beware dreaded unsigned conversions! - accuWidth += width; + int accuWidth = 0; + for (auto iterCol = absWidths.begin(); iterCol != absWidths.end(); ++iterCol) + { + const int width = iterCol->width_; //beware dreaded unsigned conversions! + accuWidth += width; - if (absPosX < accuWidth - width / 2) - return iterCol - iterComp->begin(); - } - return iterComp->size(); + if (absPosX < accuWidth - width / 2) + return iterCol - absWidths.begin(); } - return -1; + return absWidths.size(); } -Opt<ColumnType> Grid::colToType(size_t col, size_t compPos) const +Opt<ColumnType> Grid::colToType(size_t col) const { - if (compPos < comp.size() && col < comp[compPos].visibleCols.size()) - return comp[compPos].visibleCols[col].type_; + if (col < visibleCols.size()) + return visibleCols[col].type_; return NoValue(); } @@ -1995,96 +1913,61 @@ Opt<ColumnType> Grid::colToType(size_t col, size_t compPos) const ptrdiff_t Grid::getRowAtPos(int posY) const { return rowLabelWin_->getRowAtPos(posY); } -Opt<std::pair<ColumnType, size_t>> Grid::getColumnAtPos(int posX) const +Opt<ColumnType> Grid::getColumnAtPos(int posX) const { if (posX >= 0) { - std::vector<std::vector<ColumnWidth>> compAbsWidths = getColWidths(); //resolve negative/stretched widths - int accWidth = 0; - for (size_t compPos = 0; compPos < compAbsWidths.size(); ++compPos) - for (const ColumnWidth& cw : compAbsWidths[compPos]) - { - accWidth += cw.width_; - if (posX < accWidth) - return std::make_pair(cw.type_, compPos); - } + for (const ColumnWidth& cw : getColWidths()) + { + accWidth += cw.width_; + if (posX < accWidth) + return cw.type_; + } } return NoValue(); } -wxRect Grid::getCellArea(size_t row, ColumnType colType, size_t compPos) const +wxRect Grid::getCellArea(size_t row, ColumnType colType) const { - const wxRect& colArea = getColumnLabelArea(colType, compPos); + const wxRect& colArea = getColumnLabelArea(colType); const wxRect& rowArea = rowLabelWin_->getRowLabelArea(row); return wxRect(wxPoint(colArea.x, rowArea.y), wxSize(colArea.width, rowArea.height)); } -void Grid::setGridCursor(size_t row, size_t compPos) +void Grid::setGridCursor(size_t row) { - if (compPos < comp.size()) - { - mainWin_->setCursor(row, compPos); - mainWin_->makeRowVisible(row); + mainWin_->setCursor(row); + mainWin_->makeRowVisible(row); - for (Grid::Component& c : comp) - c.selection.clear(); //clear selection, do NOT fire event - selectRangeAndNotify(row, row, compPos); //set new selection + fire event + selection.clear(); //clear selection, do NOT fire event + selectRangeAndNotify(row, row); //set new selection + fire event - mainWin_->Refresh(); - rowLabelWin_->Refresh(); //row labels! (Kubuntu) - } + mainWin_->Refresh(); + rowLabelWin_->Refresh(); //row labels! (Kubuntu) } -void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, size_t compPos, bool positive) +void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive) { - if (compPos < comp.size()) - { - //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(rowFirst, rowLast, compPos, positive); - if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(selectionEvent); - - mainWin_->Refresh(); - } -} + //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); -void Grid::clearSelection(bool emitSelectRangeEvent, size_t compPos) -{ - if (compPos < comp.size()) - { - comp[compPos].selection.clear(); - mainWin_->Refresh(); - - 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); - } - } -} + selection.selectRange(rowFirst, rowLast, positive); + //notify event + GridRangeSelectEvent selectionEvent(rowFirst, rowLast, positive); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->ProcessEvent(selectionEvent); -void Grid::clearSelectionAllAndNotify() -{ - for (size_t compPos = 0; compPos < comp.size(); ++compPos) - clearSelection(true, compPos); + mainWin_->Refresh(); } @@ -2113,46 +1996,41 @@ void Grid::scrollTo(size_t row) } -std::pair<size_t, size_t> Grid::getGridCursor() const +size_t Grid::getGridCursor() const { return mainWin_->getCursor(); } -int Grid::getBestColumnSize(size_t col, size_t compPos) const +int Grid::getBestColumnSize(size_t col) const { - if (compPos < comp.size()) + if (dataView_ && col < visibleCols.size()) { - const auto& visibleCols = comp[compPos].visibleCols; - auto dataView = comp[compPos].dataView_; - if (dataView && col < visibleCols.size()) - { - const ColumnType type = visibleCols[col].type_; + const ColumnType type = visibleCols[col].type_; - wxClientDC dc(mainWin_); - dc.SetFont(mainWin_->GetFont()); //harmonize with MainWin::render() + wxClientDC dc(mainWin_); + dc.SetFont(mainWin_->GetFont()); //harmonize with MainWin::render() - int maxSize = 0; + int maxSize = 0; - auto rowRange = rowLabelWin_->getRowsOnClient(mainWin_->GetClientRect()); //returns range [begin, end) - for (auto row = rowRange.first; row < rowRange.second; ++row) - maxSize = std::max(maxSize, dataView->getBestSize(dc, row, type)); + auto rowRange = rowLabelWin_->getRowsOnClient(mainWin_->GetClientRect()); //returns range [begin, end) + for (auto row = rowRange.first; row < rowRange.second; ++row) + maxSize = std::max(maxSize, dataView_->getBestSize(dc, row, type)); - return maxSize; - } + return maxSize; } return -1; } -void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool notifyAsync) +void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEventPolicy, bool notifyAsync) { - if (compPos < comp.size() && col < comp[compPos].visibleCols.size()) + if (col < visibleCols.size()) { - VisibleColumn& vcRs = comp[compPos].visibleCols[col]; + VisibleColumn& vcRs = visibleCols[col]; - const std::vector<std::vector<int>> stretchedWidths = getColStretchedWidths(mainWin_->GetClientSize().GetWidth()); - if (stretchedWidths.size() != comp.size() || stretchedWidths[compPos].size() != comp[compPos].visibleCols.size()) + const std::vector<int> stretchedWidths = getColStretchedWidths(mainWin_->GetClientSize().GetWidth()); + if (stretchedWidths.size() != visibleCols.size()) { assert(false); return; @@ -2163,7 +2041,7 @@ void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool noti //unusual delay when enlarging the column again later width = std::max(width, COLUMN_MIN_WIDTH); - vcRs.offset_ = width - stretchedWidths[compPos][col]; //width := stretchedWidth + offset + vcRs.offset_ = width - stretchedWidths[col]; //width := stretchedWidth + offset //III. resizing any column should normalize *all* other stretched columns' offsets considering current mainWinWidth! // test case: @@ -2171,21 +2049,20 @@ void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool noti //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 - for (size_t compPos2 = 0; compPos2 < comp.size(); ++compPos2) - { - auto& visibleCols = comp[compPos2].visibleCols; - for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) - if (visibleCols[col2].stretch_ > 0) //normalize stretched columns only - visibleCols[col2].offset_ = std::max(visibleCols[col2].offset_, COLUMN_MIN_WIDTH - stretchedWidths[compPos2][col2]); - } + for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) + if (visibleCols[col2].stretch_ > 0) //normalize stretched columns only + visibleCols[col2].offset_ = std::max(visibleCols[col2].offset_, COLUMN_MIN_WIDTH - stretchedWidths[col2]); - GridColumnResizeEvent sizeEvent(vcRs.offset_, vcRs.type_, compPos); - if (wxEvtHandler* evtHandler = GetEventHandler()) + if (columnResizeEventPolicy == ALLOW_GRID_EVENT) { - if (notifyAsync) - evtHandler->AddPendingEvent(sizeEvent); - else - evtHandler->ProcessEvent(sizeEvent); + GridColumnResizeEvent sizeEvent(vcRs.offset_, vcRs.type_); + if (wxEvtHandler* evtHandler = GetEventHandler()) + { + if (notifyAsync) + evtHandler->AddPendingEvent(sizeEvent); + else + evtHandler->ProcessEvent(sizeEvent); + } } } else @@ -2193,16 +2070,15 @@ void Grid::setColWidthAndNotify(int width, size_t col, size_t compPos, bool noti } -void Grid::autoSizeColumns(size_t compPos) +void Grid::autoSizeColumns(GridEventPolicy columnResizeEventPolicy) { - if (compPos < comp.size() && comp[compPos].allowColumnResize) + if (allowColumnResize) { - auto& visibleCols = comp[compPos].visibleCols; for (size_t col = 0; col < visibleCols.size(); ++col) { - const int bestWidth = getBestColumnSize(col, compPos); //return -1 on error + const int bestWidth = getBestColumnSize(col); //return -1 on error if (bestWidth >= 0) - setColWidthAndNotify(bestWidth, col, compPos, true); + setColumnWidth(bestWidth, col, columnResizeEventPolicy, true); } updateWindowSizes(); Refresh(); @@ -2210,91 +2086,71 @@ void Grid::autoSizeColumns(size_t compPos) } -std::vector<std::vector<int>> Grid::getColStretchedWidths(int clientWidth) const //final width = (normalized) (stretchedWidth + offset) +std::vector<int> Grid::getColStretchedWidths(int clientWidth) const //final width = (normalized) (stretchedWidth + offset) { assert(clientWidth >= 0); clientWidth = std::max(clientWidth, 0); int stretchTotal = 0; - for (const Component& c : comp) - for (const VisibleColumn& vc : c.visibleCols) - { - assert(vc.stretch_ >= 0); - stretchTotal += vc.stretch_; - } + for (const VisibleColumn& vc : visibleCols) + { + assert(vc.stretch_ >= 0); + stretchTotal += vc.stretch_; + } int remainingWidth = clientWidth; - std::vector<std::vector<int>> output; - for (const Component& c : comp) - { - output.push_back(std::vector<int>()); - auto& compWidths = output.back(); + std::vector<int> output; - if (stretchTotal <= 0) - compWidths.resize(c.visibleCols.size()); //fill with zeros - else - for (const VisibleColumn& vc : c.visibleCols) - { - const int width = clientWidth * vc.stretch_ / stretchTotal; //rounds down! - compWidths.push_back(width); - remainingWidth -= width; - } - } + if (stretchTotal <= 0) + output.resize(visibleCols.size()); //fill with zeros + else + for (const VisibleColumn& vc : visibleCols) + { + const int width = clientWidth * vc.stretch_ / stretchTotal; //rounds down! + output.push_back(width); + remainingWidth -= width; + } //distribute *all* of clientWidth: should suffice to enlarge the first few stretched columns; no need to minimize total absolute error of distribution if (stretchTotal > 0) if (remainingWidth > 0) { - for (size_t compPos2 = 0; compPos2 < comp.size(); ++compPos2) - { - auto& visibleCols = comp[compPos2].visibleCols; - for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) - if (visibleCols[col2].stretch_ > 0) - { - ++output[compPos2][col2]; - if (--remainingWidth == 0) - return output; - } - } + for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) + if (visibleCols[col2].stretch_ > 0) + { + ++output[col2]; + if (--remainingWidth == 0) + return output; + } assert(false); } return output; } -std::vector<std::vector<Grid::ColumnWidth>> Grid::getColWidths() const +std::vector<Grid::ColumnWidth> Grid::getColWidths() const { return getColWidths(mainWin_->GetClientSize().GetWidth()); } -std::vector<std::vector<Grid::ColumnWidth>> Grid::getColWidths(int mainWinWidth) const //evaluate stretched columns; structure matches "comp" +std::vector<Grid::ColumnWidth> Grid::getColWidths(int mainWinWidth) const //evaluate stretched columns { - const std::vector<std::vector<int>> stretchedWidths = getColStretchedWidths(mainWinWidth); - assert(stretchedWidths.size() == comp.size()); + const std::vector<int> stretchedWidths = getColStretchedWidths(mainWinWidth); + assert(stretchedWidths.size() == visibleCols.size()); - std::vector<std::vector<ColumnWidth>> output; - - for (size_t compPos2 = 0; compPos2 < comp.size(); ++compPos2) + std::vector<ColumnWidth> output; + for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) { - assert(stretchedWidths[compPos2].size() == comp[compPos2].visibleCols.size()); - - output.push_back(std::vector<ColumnWidth>()); - auto& compWidths = output.back(); - - auto& visibleCols = comp[compPos2].visibleCols; - for (size_t col2 = 0; col2 < visibleCols.size(); ++col2) - { - const auto& vc = visibleCols[col2]; - int width = stretchedWidths[compPos2][col2] + vc.offset_; + const auto& vc = visibleCols[col2]; + int width = stretchedWidths[col2] + vc.offset_; - if (vc.stretch_ > 0) - width = std::max(width, COLUMN_MIN_WIDTH); //normalization really needed here: e.g. smaller main window would result in negative width - else - width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH if set via configuration + if (vc.stretch_ > 0) + width = std::max(width, COLUMN_MIN_WIDTH); //normalization really needed here: e.g. smaller main window would result in negative width + else + width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH if set via configuration - compWidths.push_back(ColumnWidth(vc.type_, width)); - } + output.push_back(ColumnWidth(vc.type_, width)); } return output; } @@ -2303,8 +2159,7 @@ std::vector<std::vector<Grid::ColumnWidth>> Grid::getColWidths(int mainWinWidth) int Grid::getColWidthsSum(int mainWinWidth) const { int sum = 0; - for (const std::vector<ColumnWidth>& cols : getColWidths(mainWinWidth)) - for (const ColumnWidth& cw : cols) - sum += cw.width_; + for (const ColumnWidth& cw : getColWidths(mainWinWidth)) + sum += cw.width_; return sum; }; |