summaryrefslogtreecommitdiff
path: root/wx+/grid.h
diff options
context:
space:
mode:
Diffstat (limited to 'wx+/grid.h')
-rw-r--r--wx+/grid.h312
1 files changed, 252 insertions, 60 deletions
diff --git a/wx+/grid.h b/wx+/grid.h
index e0069e32..8833dd77 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -1,37 +1,116 @@
// **************************************************************************
// * 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) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef WX_TREE_LIST_HEADER_8347021348317
-#define WX_TREE_LIST_HEADER_8347021348317
+#ifndef GENERIC_GRID_HEADER_83470213483173
+#define GENERIC_GRID_HEADER_83470213483173
-#include <set>
#include <memory>
+#include <numeric>
#include <vector>
#include <wx/scrolwin.h>
+#include <zen/basic_math.h>
+#include <zen/optional.h>
+
+//a user-friendly, extensible and high-performance grid control
namespace zen
{
+typedef enum { DUMMY_COLUMN_TYPE = static_cast<size_t>(-1) } ColumnType;
+
+//----- 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
+
+extern const wxEventType EVENT_GRID_MOUSE_LEFT_DOUBLE; //
+extern const wxEventType EVENT_GRID_MOUSE_LEFT_DOWN; //
+extern const wxEventType EVENT_GRID_MOUSE_LEFT_UP; //generates: GridClickEvent
+extern const wxEventType EVENT_GRID_MOUSE_RIGHT_DOWN; //
+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), NULL, 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); }
+ virtual wxEvent* Clone() const { return new GridClickEvent(*this); }
+
+ const int row_;
+ const ColumnType colType_;
+ const size_t compPos_;
+};
+
+struct GridColumnResizeEvent : public wxCommandEvent
+{
+ GridColumnResizeEvent(int width, ColumnType colType, size_t compPos) : wxCommandEvent(EVENT_GRID_COL_RESIZE), colType_(colType), width_(width), compPos_(compPos) {}
+ virtual wxEvent* Clone() const { return new GridColumnResizeEvent(*this); }
+
+ const ColumnType colType_;
+ const int width_;
+ const size_t compPos_;
+};
+
+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) {}
+ virtual wxEvent* Clone() const { return new GridRangeSelectEvent(*this); }
+
+ const int rowFrom_;
+ const int rowTo_;
+ const size_t compPos_;
+ const bool positive_;
+};
+
+typedef void (wxEvtHandler::*GridClickEventFunction )(GridClickEvent&);
+typedef void (wxEvtHandler::*GridColumnResizeEventFunction)(GridColumnResizeEvent&);
+typedef void (wxEvtHandler::*GridRangeSelectEventFunction )(GridRangeSelectEvent&);
+
+#define GridClickEventHandler(func) \
+ (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridClickEventFunction, &func)
+
+#define GridColumnResizeEventHandler(func) \
+ (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridColumnResizeEventFunction, &func)
+
+#define GridRangeSelectEventHandler(func) \
+ (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridRangeSelectEventFunction, &func)
//------------------------------------------------------------------------------------------------------------
class Grid;
+wxColor getColorSelectionGradientFrom();
+wxColor getColorSelectionGradientTo();
-class GridDataView
+void clearArea(wxDC& dc, const wxRect& rect, const wxColor& col);
+
+class GridData
{
public:
- virtual ~GridDataView() {}
+ virtual ~GridData() {}
+
+ virtual size_t getRowCount() const = 0; //if there are multiple grid components, only the first one will be polled for row count!
- virtual wxString getValue(int row, int col) = 0;
- virtual void renderCell(Grid& grid, wxDC& dc, const wxRect& rect, int row, int col, bool selected); //default implementation
+ //grid area
+ virtual wxString getValue(int row, ColumnType colType) const = 0;
+ virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, int row, bool enabled, bool selected, bool hasFocus); //default implementation
+ virtual void renderCell(Grid& grid, wxDC& dc, const wxRect& rect, int row, ColumnType colType); //
+ virtual size_t getBestSize(wxDC& dc, int row, ColumnType colType); //must correspond to renderCell()!
+ virtual wxString getToolTip(int row, ColumnType colType) const { return wxString(); }
- virtual wxString getColumnLabel(int col) = 0;
- virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, int col, bool highlighted); //default implementation
+ //label area
+ virtual wxString getColumnLabel(ColumnType colType) const = 0;
+ virtual void renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted); //default implementation
+ virtual wxString getToolTip(ColumnType colType) const { return wxString(); }
-protected:
+protected: //optional helper routines
static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
- static void drawCellBackground(wxDC& dc, const wxRect& rect, bool selected, bool enabled, bool hasFocus);
- static void drawCellText (wxDC& dc, const wxRect& rect, const wxString& text, bool enabled);
+ static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, bool hasFocus, const wxColor& backgroundColor);
+ static void drawCellText (wxDC& dc, const wxRect& rect, const wxString& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
static wxRect drawColumnLabelBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
static void drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted);
@@ -39,7 +118,6 @@ protected:
};
-
class Grid : public wxScrolledWindow
{
public:
@@ -50,86 +128,200 @@ public:
long style = wxTAB_TRAVERSAL | wxNO_BORDER,
const wxString& name = wxPanelNameStr);
- //------------ set grid data --------------------------------------------------
- void setRowCount(int rowCount);
- int getRowCount() const;
+ size_t getRowCount() const;
void setRowHeight(int height);
- int getRowHeight();
- void setColumnConfig(const std::vector<int>& widths); //set column count + widths
- std::vector<int> getColumnConfig() const;
+ //grid component := a grid is divided into multiple components each of which is essentially a set of connected columns
+ void setComponentCount(size_t count) { comp.resize(count); updateWindowSizes(); }
+ size_t getComponentCount() const { return comp.size(); }
+
+ struct ColumnAttribute
+ {
+ ColumnAttribute(ColumnType type, int width, bool visible = true) : type_(type), width_(width), visible_(visible) {}
+ ColumnType type_;
+ int width_; //if negative, treat as proportional stretch!
+ bool visible_;
+ };
- void setDataProvider(const std::shared_ptr<GridDataView>& dataView) { dataView_ = dataView; }
+ void setColumnConfig(const std::vector<ColumnAttribute>& attr, size_t compPos = 0); //set column count + widths
+ std::vector<ColumnAttribute> getColumnConfig(size_t compPos = 0) const;
+
+ void setDataProvider(const std::shared_ptr<GridData>& dataView, size_t compPos = 0) { if (compPos < comp.size()) comp[compPos].dataView_ = dataView; }
+ /**/ GridData* getDataProvider(size_t compPos = 0) { return compPos < comp.size() ? comp[compPos].dataView_.get() : NULL; }
+ const GridData* getDataProvider(size_t compPos = 0) const { return compPos < comp.size() ? comp[compPos].dataView_.get() : NULL; }
//-----------------------------------------------------------------------------
-
- void setColumnLabelHeight(int height);
- void showRowLabel(bool visible);
- void showScrollBars(bool horizontal, bool vertical);
+ void setColumnLabelHeight(int height);
+ void showRowLabel(bool visible);
- std::vector<int> getSelectedRows() const;
+ void showScrollBars(bool horizontal, bool vertical);
-private:
- void onSizeEvent(wxEvent& evt) { updateWindowSizes(); }
+ std::vector<int> getSelectedRows(size_t compPos = 0) const;
+ void clearSelection(size_t compPos = 0) { if (compPos < comp.size()) comp[compPos].selection.clear(); }
+
+ void scrollDelta(int deltaX, int deltaY); //in scroll units
- void updateWindowSizes();
+ wxWindow& getCornerWin ();
+ wxWindow& getRowLabelWin();
+ wxWindow& getColLabelWin();
+ wxWindow& getMainWin ();
- int getScrollUnitsTotalX() const;
- int getScrollUnitsTotalY() const;
+ int getRowAtPos(int posY) const; //returns < 0 if column not found; absolute coordinates!
+ Opt<std::pair<ColumnType, size_t>> getColumnAtPos(int posX) const; //returns (column type, component pos)
- int getPixelsPerScrollUnit() const;
+ wxRect getCellArea(int row, ColumnType colType, size_t compPos = 0) const; //returns empty rect if column not found; absolute coordinates!
- void scrollDelta(int deltaX, int deltaY); //in scroll units
+ 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(int row, size_t compPos = 0); //set + show + select cursor
+ std::pair<int, size_t> getGridCursor() const; //(row, component pos) row == -1 if none
+
+ void scrollTo(int row);
+
+ virtual void Refresh(bool eraseBackground = true, const wxRect* rect = NULL);
+ virtual bool Enable( bool enable = true) { Refresh(); return wxScrolledWindow::Enable(enable); }
+ void autoSizeColumns(size_t compPos = 0);
+
+private:
+ void onPaintEvent(wxPaintEvent& event);
+ void onEraseBackGround(wxEraseEvent& event) {} //[!]
+ void onSizeEvent(wxEvent& evt) { updateWindowSizes(); }
+
+ void updateWindowSizes(bool updateScrollbar = true);
void redirectRowLabelEvent(wxMouseEvent& event);
#ifdef FFS_WIN
virtual WXLRESULT MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); //support horizontal mouse wheel
-void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh); //get rid of scrollbars, but preserve scrolling behavior!
+ void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh); //get rid of scrollbars, but preserve scrolling behavior!
#endif
- GridDataView* getDataProvider() { return dataView_.get(); }
-
- static const int defaultRowHeight = 20;
- static const int defaultColLabelHeight = 25;
- static const int columnBorderLeft = 4; //for left-aligned text
- static const int columnLabelBorder = columnBorderLeft;
- static const int columnMoveDelay = 5; //unit [pixel] (Explorer)
- static const int columnMinWidth = 30;
- static const int rowLabelBorder = 3;
- static const int columnResizeTolerance = 5; //unit [pixel]
- static const int mouseDragScrollIncrementY = 2; //in scroll units
- static const int mouseDragScrollIncrementX = 1; //
-
- friend class GridDataView;
+
+ int getBestColumnSize(size_t col, size_t compPos) const; //return -1 on error
+
+ friend class GridData;
class SubWindow;
class CornerWin;
class RowLabelWin;
class ColLabelWin;
class MainWin;
+ class Selection
+ {
+ public:
+ void init(size_t rowCount) { rowSelectionValue.resize(rowCount); clear(); }
+
+ std::vector<int> get() const
+ {
+ std::vector<int> selection;
+ for (auto iter = rowSelectionValue.begin(); iter != rowSelectionValue.end(); ++iter)
+ if (*iter != 0)
+ selection.push_back(iter - rowSelectionValue.begin());
+ return selection;
+ }
+
+ void clear() { selectRange(0, rowSelectionValue.size(), false); }
+
+ bool isSelected(size_t row) const { return row < rowSelectionValue.size() ? rowSelectionValue[row] != 0 : false; }
+
+ void selectRange(int rowFrom, int rowTo, bool positive = true) //select [rowFrom, rowTo], very tolerant: trims and swaps if required!
+ {
+ int rowFirst = std::min(rowFrom, rowTo);
+ int rowLast = std::max(rowFrom, rowTo) + 1;
+
+ numeric::restrict(rowFirst, 0, static_cast<int>(rowSelectionValue.size()));
+ numeric::restrict(rowLast, 0, static_cast<int>(rowSelectionValue.size()));
+
+ std::fill(rowSelectionValue.begin() + rowFirst, rowSelectionValue.begin() + rowLast, positive);
+ }
+
+ private:
+ std::vector<char> rowSelectionValue; //effectively a vector<bool> of size "number of rows"
+ };
+
+ struct VisibleColumn
+ {
+ VisibleColumn(ColumnType type, int width) : type_(type), width_(width) {}
+ ColumnType type_;
+ int width_; //may be NEGATIVE => treat as proportional stretch! use getAbsoluteWidths() to evaluate!!!
+ };
+
+ struct Component
+ {
+ Component() : allowColumnMove(true), allowColumnResize(true) {}
+
+ std::shared_ptr<GridData> dataView_;
+ Selection selection;
+ bool allowColumnMove;
+ bool allowColumnResize;
+
+ std::vector<VisibleColumn> visibleCols; //individual widths, type and total column count
+ std::vector<ColumnAttribute> oldColAttributes; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*!
+ };
+
+ int getMinAbsoluteWidthTotal() const; //assigns minimum width to stretched columns
+ std::vector<std::vector<VisibleColumn>> getAbsoluteWidths() const; //evaluate negative widths as stretched absolute values! structure matches "comp"
+
+ Opt<size_t> getAbsoluteWidth(size_t col, size_t compPos) const //resolve stretched columns
+ {
+ const auto& absWidth = getAbsoluteWidths();
+ if (compPos < absWidth.size() && col < absWidth[compPos].size())
+ return absWidth[compPos][col].width_;
+ return NoValue();
+ }
+
+ void setColWidth(size_t col, size_t compPos, int width) //width may be >= 0: absolute, or < 0: stretched
+ {
+ if (compPos < comp.size() && col < comp[compPos].visibleCols.size())
+ comp[compPos].visibleCols[col].width_ = width;
+ }
+
+ wxRect getColumnLabelArea(ColumnType colType, size_t compPos) const; //returns empty rect if column not found
+
+ void selectRange(int rowFrom, int rowTo, size_t compPos, bool positive = true); //select range + notify event!
+ void clearSelectionAll(); //clear selection + notify event
+
+ bool isSelected(int row, size_t compPos) const { return compPos < comp.size() ? comp[compPos].selection.isSelected(row) : false; }
+
+ bool columnMoveAllowed (size_t compPos) const { return compPos < comp.size() ? comp[compPos].allowColumnMove : false; }
+ bool columnResizeAllowed(size_t compPos) const { return compPos < comp.size() ? comp[compPos].allowColumnResize : false; }
+
+ struct ColAction
+ {
+ bool wantResize; //"!wantResize" means "move" or "single click"
+ size_t col;
+ size_t compPos;
+ };
+ Opt<ColAction> clientPosToColumnAction(const wxPoint& pos) const;
+ void moveColumn(size_t colFrom, size_t colTo, size_t compPos);
+ int clientPosToMoveTargetColumn(const wxPoint& pos, size_t compPos) const;
+
+ Opt<ColumnType> colToType(size_t col, size_t compPos) const;
+
+
/*
Visual layout:
- ----------------------------
- |CornerWin | RowLabelWin |
- |---------------------------
- |ColLabelWin | MainWin |
- ----------------------------
+ ------------------------------------------------
+ |CornerWin | RowLabelWin: |
+ |-------------------------- Comp1 | Comp2 ... | row label and main window are vertically tiled into one or more "components"
+ |ColLabelWin | MainWin: |
+ ------------------------------------------------
*/
CornerWin* cornerWin_;
RowLabelWin* rowLabelWin_;
ColLabelWin* colLabelWin_;
MainWin* mainWin_;
- std::shared_ptr<GridDataView> dataView_;
+ bool showScrollbarX;
+ bool showScrollbarY;
- bool showScrollbarX;
- bool showScrollbarY;
+ int colLabelHeight;
+ bool drawRowLabel;
- int colLabelHeight;
- int drawRowLabel;
+ std::vector<Component> comp;
+ size_t colSizeOld; //at the time of last Grid::Refresh()
};
}
-
-#endif //WX_TREE_LIST_HEADER_8347021348317
+#endif //GENERIC_GRID_HEADER_83470213483173
bgstack15