X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ad805b9e0d0966647a633220c0c63cd1a89629a0..1680c3561bc4f44c63a026e0d1a5324a39336156:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index b03c969d06..8326934f7f 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -48,8 +48,12 @@ #include "wx/tokenzr.h" #include "wx/renderer.h" #include "wx/headerctrl.h" +#include "wx/hashset.h" #include "wx/generic/gridsel.h" +#include "wx/generic/gridctrl.h" +#include "wx/generic/grideditors.h" +#include "wx/generic/private/grid.h" const char wxGridNameStr[] = "grid"; @@ -68,62 +72,59 @@ const char wxGridNameStr[] = "grid"; // Required for wxIs... functions #include +WX_DECLARE_HASH_SET_WITH_DECL(int, wxIntegerHash, wxIntegerEqual, + wxGridFixedIndicesSet, class WXDLLIMPEXP_ADV); + + // ---------------------------------------------------------------------------- -// array classes +// globals // ---------------------------------------------------------------------------- -WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridCellAttr *, wxArrayAttrs, - class WXDLLIMPEXP_ADV); +namespace +{ + +//#define DEBUG_ATTR_CACHE +#ifdef DEBUG_ATTR_CACHE + static size_t gs_nAttrCacheHits = 0; + static size_t gs_nAttrCacheMisses = 0; +#endif -struct wxGridCellWithAttr +// this struct simply combines together the default header renderers +// +// as the renderers ctors are trivial, there is no problem with making them +// globals +struct DefaultHeaderRenderers { - wxGridCellWithAttr(int row, int col, wxGridCellAttr *attr_) - : coords(row, col), attr(attr_) - { - wxASSERT( attr ); - } + wxGridColumnHeaderRendererDefault colRenderer; + wxGridRowHeaderRendererDefault rowRenderer; + wxGridCornerHeaderRendererDefault cornerRenderer; +} gs_defaultHeaderRenderers; - wxGridCellWithAttr(const wxGridCellWithAttr& other) - : coords(other.coords), - attr(other.attr) - { - attr->IncRef(); - } +} // anonymous namespace - wxGridCellWithAttr& operator=(const wxGridCellWithAttr& other) - { - coords = other.coords; - if (attr != other.attr) - { - attr->DecRef(); - attr = other.attr; - attr->IncRef(); - } - return *this; - } +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- - void ChangeAttr(wxGridCellAttr* new_attr) - { - if (attr != new_attr) - { - // "Delete" (i.e. DecRef) the old attribute. - attr->DecRef(); - attr = new_attr; - // Take ownership of the new attribute, i.e. no IncRef. - } - } +wxGridCellCoords wxGridNoCellCoords( -1, -1 ); +wxRect wxGridNoCellRect( -1, -1, -1, -1 ); - ~wxGridCellWithAttr() - { - attr->DecRef(); - } +namespace +{ + +// scroll line size +const size_t GRID_SCROLL_LINE_X = 15; +const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; + +// the size of hash tables used a bit everywhere (the max number of elements +// in these hash tables is the number of rows/columns) +const int GRID_HASH_SIZE = 100; - wxGridCellCoords coords; - wxGridCellAttr *attr; -}; +// the minimal distance in pixels the mouse needs to move to start a drag +// operation +const int DRAG_SENSITIVITY = 3; -WX_DECLARE_OBJARRAY_WITH_DECL(wxGridCellWithAttr, wxGridCellWithAttrArray, - class WXDLLIMPEXP_ADV); +} // anonymous namespace #include "wx/arrimpl.cpp" @@ -134,307 +135,47 @@ WX_DEFINE_OBJARRAY(wxGridCellWithAttrArray) // events // ---------------------------------------------------------------------------- -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_LEFT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_RIGHT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_LEFT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_RIGHT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_BEGIN_DRAG) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_LEFT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_LEFT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_ROW_SIZE) -DEFINE_EVENT_TYPE(wxEVT_GRID_COL_SIZE) -DEFINE_EVENT_TYPE(wxEVT_GRID_COL_MOVE) -DEFINE_EVENT_TYPE(wxEVT_GRID_RANGE_SELECT) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGE) -DEFINE_EVENT_TYPE(wxEVT_GRID_SELECT_CELL) -DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_SHOWN) -DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_HIDDEN) -DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_CREATED) +wxDEFINE_EVENT( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_CELL_RIGHT_DCLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_CELL_BEGIN_DRAG, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_LABEL_LEFT_CLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_LABEL_RIGHT_CLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_LABEL_LEFT_DCLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_LABEL_RIGHT_DCLICK, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_ROW_SIZE, wxGridSizeEvent ); +wxDEFINE_EVENT( wxEVT_GRID_COL_SIZE, wxGridSizeEvent ); +wxDEFINE_EVENT( wxEVT_GRID_COL_MOVE, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_COL_SORT, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEvent ); +wxDEFINE_EVENT( wxEVT_GRID_CELL_CHANGING, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_CELL_CHANGED, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_SELECT_CELL, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_EDITOR_SHOWN, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_EDITOR_HIDDEN, wxGridEvent ); +wxDEFINE_EVENT( wxEVT_GRID_EDITOR_CREATED, wxGridEditorCreatedEvent ); // ---------------------------------------------------------------------------- -// private classes +// private helpers // ---------------------------------------------------------------------------- -// header column providing access to the column information stored in wxGrid -// via wxHeaderColumn interface -class wxGridHeaderColumn : public wxHeaderColumn -{ -public: - wxGridHeaderColumn(wxGrid *grid, int col) - : m_grid(grid), - m_col(col) - { - } - - virtual wxString GetTitle() const { return m_grid->GetColLabelValue(m_col); } - virtual wxBitmap GetBitmap() const { return wxNullBitmap; } - virtual int GetWidth() const { return m_grid->GetColSize(m_col); } - virtual int GetMinWidth() const { return 0; } - virtual wxAlignment GetAlignment() const - { - int horz, - vert; - m_grid->GetColLabelAlignment(&horz, &vert); - - return static_cast(horz); - } - - virtual int GetFlags() const - { - int flags = 0; - if ( m_grid->CanDragColSize() ) - flags |= wxCOL_RESIZABLE; - if ( m_grid->CanDragColMove() ) - flags |= wxCOL_REORDERABLE; - - return flags; - } - - // TODO: currently there is no support for sorting - virtual bool IsSortKey() const { return false; } - virtual bool IsSortOrderAscending() const { return false; } - -private: - wxGrid * const m_grid; - const int m_col; - - DECLARE_NO_ASSIGN_CLASS(wxGridHeaderColumn) -}; - -// header control retreiving column information from the grid -class wxGridHeaderCtrl : public wxHeaderCtrl -{ -public: - wxGridHeaderCtrl(wxGrid *owner) - : wxHeaderCtrl(owner, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize, - owner->CanDragColMove() ? wxHD_DRAGDROP : 0) - { - } - -protected: - virtual wxHeaderColumn& GetColumn(unsigned int idx) - { - return m_columns[idx]; - } - -private: - wxGrid *GetOwner() const { return static_cast(GetParent()); } - - // override the base class method to update our m_columns array - virtual void OnColumnCountChanging(unsigned int count) - { - const unsigned countOld = m_columns.size(); - if ( count < countOld ) - { - // just discard the columns which don't exist any more (notice that - // we can't use resize() here as it would require the vector - // value_type, i.e. wxGridHeaderColumn to be default constructible, - // which it is not) - m_columns.erase(m_columns.begin() + count, m_columns.end()); - } - else // new columns added - { - // add columns for the new elements - for ( unsigned n = countOld; n < count; n++ ) - m_columns.push_back(wxGridHeaderColumn(GetOwner(), n)); - } - } - - // override to implement column auto sizing - virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle) - { - GetOwner()->SetColSize(idx, widthTitle); - - return true; - } - - - // event handlers forwarding wxHeaderCtrl events to wxGrid - void OnBeginResize(wxHeaderCtrlEvent& event) - { - GetOwner()->DoStartResizeCol(event.GetColumn()); - - event.Skip(); - } - - void OnResizing(wxHeaderCtrlEvent& event) - { - GetOwner()->DoUpdateResizeColWidth(event.GetWidth()); - } - - void OnEndResize(wxHeaderCtrlEvent& event) - { - GetOwner()->DoEndDragResizeCol(); - - event.Skip(); - } - - void OnEndReorder(wxHeaderCtrlEvent& event) - { - event.Skip(); // TODO: position it at event.GetNewOrder() - } - - wxVector m_columns; - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridHeaderCtrl) -}; - -BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl) - EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize) - EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing) - EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize) - - EVT_HEADER_END_REORDER(wxID_ANY, wxGridHeaderCtrl::OnEndReorder) -END_EVENT_TABLE() - -// common base class for various grid subwindows -class WXDLLIMPEXP_ADV wxGridSubwindow : public wxWindow -{ -public: - wxGridSubwindow(wxGrid *owner, - int additionalStyle = 0, - const wxString& name = wxPanelNameStr) - : wxWindow(owner, wxID_ANY, - wxDefaultPosition, wxDefaultSize, - wxBORDER_NONE | additionalStyle, - name) - { - m_owner = owner; - } - - virtual bool AcceptsFocus() const { return false; } - - wxGrid *GetOwner() { return m_owner; } - -protected: - void OnMouseCaptureLost(wxMouseCaptureLostEvent& event); - - wxGrid *m_owner; - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridSubwindow) -}; - -class WXDLLIMPEXP_ADV wxGridRowLabelWindow : public wxGridSubwindow -{ -public: - wxGridRowLabelWindow(wxGrid *parent) - : wxGridSubwindow(parent) - { - } - - -private: - void OnPaint( wxPaintEvent& event ); - void OnMouseEvent( wxMouseEvent& event ); - void OnMouseWheel( wxMouseEvent& event ); - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridRowLabelWindow) -}; - - -class WXDLLIMPEXP_ADV wxGridColLabelWindow : public wxGridSubwindow -{ -public: - wxGridColLabelWindow(wxGrid *parent) - : wxGridSubwindow(parent) - { - } - - -private: - void OnPaint( wxPaintEvent& event ); - void OnMouseEvent( wxMouseEvent& event ); - void OnMouseWheel( wxMouseEvent& event ); - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridColLabelWindow) -}; - - -class WXDLLIMPEXP_ADV wxGridCornerLabelWindow : public wxGridSubwindow -{ -public: - wxGridCornerLabelWindow(wxGrid *parent) - : wxGridSubwindow(parent) - { - } - -private: - void OnMouseEvent( wxMouseEvent& event ); - void OnMouseWheel( wxMouseEvent& event ); - void OnPaint( wxPaintEvent& event ); - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridCornerLabelWindow) -}; - -class WXDLLIMPEXP_ADV wxGridWindow : public wxGridSubwindow -{ -public: - wxGridWindow(wxGrid *parent) - : wxGridSubwindow(parent, - wxWANTS_CHARS | wxCLIP_CHILDREN, - "GridWindow") - { - } - - - virtual void ScrollWindow( int dx, int dy, const wxRect *rect ); - - virtual bool AcceptsFocus() const { return true; } - -private: - void OnPaint( wxPaintEvent &event ); - void OnMouseWheel( wxMouseEvent& event ); - void OnMouseEvent( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); - void OnEraseBackground( wxEraseEvent& ); - void OnFocus( wxFocusEvent& ); - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridWindow) -}; - - -class wxGridCellEditorEvtHandler : public wxEvtHandler +namespace { -public: - wxGridCellEditorEvtHandler(wxGrid* grid, wxGridCellEditor* editor) - : m_grid(grid), - m_editor(editor), - m_inSetFocus(false) + + // ensure that first is less or equal to second, swapping the values if + // necessary + void EnsureFirstLessThanSecond(int& first, int& second) { + if ( first > second ) + wxSwap(first, second); } + +} // anonymous namespace - void OnKillFocus(wxFocusEvent& event); - void OnKeyDown(wxKeyEvent& event); - void OnChar(wxKeyEvent& event); - - void SetInSetFocus(bool inSetFocus) { m_inSetFocus = inSetFocus; } - -private: - wxGrid *m_grid; - wxGridCellEditor *m_editor; - - // Work around the fact that a focus kill event can be sent to - // a combobox within a set focus event. - bool m_inSetFocus; - - DECLARE_EVENT_TABLE() - DECLARE_DYNAMIC_CLASS(wxGridCellEditorEvtHandler) - DECLARE_NO_COPY_CLASS(wxGridCellEditorEvtHandler) -}; - +// ============================================================================ +// implementation +// ============================================================================ IMPLEMENT_ABSTRACT_CLASS(wxGridCellEditorEvtHandler, wxEvtHandler) @@ -444,2374 +185,129 @@ BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler ) EVT_CHAR( wxGridCellEditorEvtHandler::OnChar ) END_EVENT_TABLE() +BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl) + EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick) + EVT_HEADER_DCLICK(wxID_ANY, wxGridHeaderCtrl::OnDoubleClick) + EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxGridHeaderCtrl::OnRightClick) -// ---------------------------------------------------------------------------- -// the internal data representation used by wxGridCellAttrProvider -// ---------------------------------------------------------------------------- - -// this class stores attributes set for cells -class WXDLLIMPEXP_ADV wxGridCellAttrData -{ -public: - void SetAttr(wxGridCellAttr *attr, int row, int col); - wxGridCellAttr *GetAttr(int row, int col) const; - void UpdateAttrRows( size_t pos, int numRows ); - void UpdateAttrCols( size_t pos, int numCols ); - -private: - // searches for the attr for given cell, returns wxNOT_FOUND if not found - int FindIndex(int row, int col) const; - - wxGridCellWithAttrArray m_attrs; -}; - -// this class stores attributes set for rows or columns -class WXDLLIMPEXP_ADV wxGridRowOrColAttrData -{ -public: - // empty ctor to suppress warnings - wxGridRowOrColAttrData() {} - ~wxGridRowOrColAttrData(); - - void SetAttr(wxGridCellAttr *attr, int rowOrCol); - wxGridCellAttr *GetAttr(int rowOrCol) const; - void UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols ); - -private: - wxArrayInt m_rowsOrCols; - wxArrayAttrs m_attrs; -}; - -// NB: this is just a wrapper around 3 objects: one which stores cell -// attributes, and 2 others for row/col ones -class WXDLLIMPEXP_ADV wxGridCellAttrProviderData -{ -public: - wxGridCellAttrData m_cellAttrs; - wxGridRowOrColAttrData m_rowAttrs, - m_colAttrs; -}; - - -// ---------------------------------------------------------------------------- -// data structures used for the data type registry -// ---------------------------------------------------------------------------- - -struct wxGridDataTypeInfo -{ - wxGridDataTypeInfo(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor) - : m_typeName(typeName), m_renderer(renderer), m_editor(editor) - {} - - ~wxGridDataTypeInfo() - { - wxSafeDecRef(m_renderer); - wxSafeDecRef(m_editor); - } - - wxString m_typeName; - wxGridCellRenderer* m_renderer; - wxGridCellEditor* m_editor; - - DECLARE_NO_COPY_CLASS(wxGridDataTypeInfo) -}; - - -WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridDataTypeInfo*, wxGridDataTypeInfoArray, - class WXDLLIMPEXP_ADV); - - -class WXDLLIMPEXP_ADV wxGridTypeRegistry -{ -public: - wxGridTypeRegistry() {} - ~wxGridTypeRegistry(); - - void RegisterDataType(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor); - - // find one of already registered data types - int FindRegisteredDataType(const wxString& typeName); - - // try to FindRegisteredDataType(), if this fails and typeName is one of - // standard typenames, register it and return its index - int FindDataType(const wxString& typeName); - - // try to FindDataType(), if it fails see if it is not one of already - // registered data types with some params in which case clone the - // registered data type and set params for it - int FindOrCloneDataType(const wxString& typeName); - - wxGridCellRenderer* GetRenderer(int index); - wxGridCellEditor* GetEditor(int index); - -private: - wxGridDataTypeInfoArray m_typeinfo; -}; - -// ---------------------------------------------------------------------------- -// operations classes abstracting the difference between operating on rows and -// columns -// ---------------------------------------------------------------------------- - -// This class allows to write a function only once because by using its methods -// it will apply to both columns and rows. -// -// This is an abstract interface definition, the two concrete implementations -// below should be used when working with rows and columns respectively. -class wxGridOperations -{ -public: - // Returns the operations in the other direction, i.e. wxGridRowOperations - // if this object is a wxGridColumnOperations and vice versa. - virtual wxGridOperations& Dual() const = 0; - - // Return the number of rows or columns. - virtual int GetNumberOfLines(const wxGrid *grid) const = 0; - - // Return the selection mode which allows selecting rows or columns. - virtual wxGrid::wxGridSelectionModes GetSelectionMode() const = 0; - - // Make a wxGridCellCoords from the given components: thisDir is row or - // column and otherDir is column or row - virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const = 0; - - // Calculate the scrolled position of the given abscissa or ordinate. - virtual int CalcScrolledPosition(wxGrid *grid, int pos) const = 0; - - // Selects the horizontal or vertical component from the given object. - virtual int Select(const wxGridCellCoords& coords) const = 0; - virtual int Select(const wxPoint& pt) const = 0; - virtual int Select(const wxSize& sz) const = 0; - virtual int Select(const wxRect& r) const = 0; - virtual int& Select(wxRect& r) const = 0; - - // Returns width or height of the rectangle - virtual int& SelectSize(wxRect& r) const = 0; - - // Make a wxSize such that Select() applied to it returns first component - virtual wxSize MakeSize(int first, int second) const = 0; - - // Sets the row or column component of the given cell coordinates - virtual void Set(wxGridCellCoords& coords, int line) const = 0; - - - // Draws a line parallel to the row or column, i.e. horizontal or vertical: - // pos is the horizontal or vertical position of the line and start and end - // are the coordinates of the line extremities in the other direction - virtual void - DrawParallelLine(wxDC& dc, int start, int end, int pos) const = 0; - - // Draw a horizontal or vertical line across the given rectangle - // (this is implemented in terms of above and uses Select() to extract - // start and end from the given rectangle) - void DrawParallelLineInRect(wxDC& dc, const wxRect& rect, int pos) const - { - const int posStart = Select(rect.GetPosition()); - DrawParallelLine(dc, posStart, posStart + Select(rect.GetSize()), pos); - } - - - // Return the row or column at the given pixel coordinate. - virtual int - PosToLine(const wxGrid *grid, int pos, bool clip = false) const = 0; - - // Get the top/left position, in pixels, of the given row or column - virtual int GetLineStartPos(const wxGrid *grid, int line) const = 0; - - // Get the bottom/right position, in pixels, of the given row or column - virtual int GetLineEndPos(const wxGrid *grid, int line) const = 0; - - // Get the height/width of the given row/column - virtual int GetLineSize(const wxGrid *grid, int line) const = 0; - - // Get wxGrid::m_rowBottoms/m_colRights array - virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const = 0; - - // Get default height row height or column width - virtual int GetDefaultLineSize(const wxGrid *grid) const = 0; - - // Return the minimal acceptable row height or column width - virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const = 0; - - // Return the minimal row height or column width - virtual int GetMinimalLineSize(const wxGrid *grid, int line) const = 0; - - // Set the row height or column width - virtual void SetLineSize(wxGrid *grid, int line, int size) const = 0; - - // True if rows/columns can be resized by user - virtual bool CanResizeLines(const wxGrid *grid) const = 0; - - - // Return the index of the line at the given position - // - // NB: currently this is always identity for the rows as reordering is only - // implemented for the lines - virtual int GetLineAt(const wxGrid *grid, int line) const = 0; - - - // Get the row or column label window - virtual wxWindow *GetHeaderWindow(wxGrid *grid) const = 0; - - // Get the width or height of the row or column label window - virtual int GetHeaderWindowSize(wxGrid *grid) const = 0; - - - // This class is never used polymorphically but give it a virtual dtor - // anyhow to suppress g++ complaints about it - virtual ~wxGridOperations() { } -}; - -class wxGridRowOperations : public wxGridOperations -{ -public: - virtual wxGridOperations& Dual() const; - - virtual int GetNumberOfLines(const wxGrid *grid) const - { return grid->GetNumberRows(); } - - virtual wxGrid::wxGridSelectionModes GetSelectionMode() const - { return wxGrid::wxGridSelectRows; } - - virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const - { return wxGridCellCoords(thisDir, otherDir); } - - virtual int CalcScrolledPosition(wxGrid *grid, int pos) const - { return grid->CalcScrolledPosition(wxPoint(pos, 0)).x; } - - virtual int Select(const wxGridCellCoords& c) const { return c.GetRow(); } - virtual int Select(const wxPoint& pt) const { return pt.x; } - virtual int Select(const wxSize& sz) const { return sz.x; } - virtual int Select(const wxRect& r) const { return r.x; } - virtual int& Select(wxRect& r) const { return r.x; } - virtual int& SelectSize(wxRect& r) const { return r.width; } - virtual wxSize MakeSize(int first, int second) const - { return wxSize(first, second); } - virtual void Set(wxGridCellCoords& coords, int line) const - { coords.SetRow(line); } - - virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const - { dc.DrawLine(start, pos, end, pos); } - - virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const - { return grid->YToRow(pos, clip); } - virtual int GetLineStartPos(const wxGrid *grid, int line) const - { return grid->GetRowTop(line); } - virtual int GetLineEndPos(const wxGrid *grid, int line) const - { return grid->GetRowBottom(line); } - virtual int GetLineSize(const wxGrid *grid, int line) const - { return grid->GetRowHeight(line); } - virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const - { return grid->m_rowBottoms; } - virtual int GetDefaultLineSize(const wxGrid *grid) const - { return grid->GetDefaultRowSize(); } - virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const - { return grid->GetRowMinimalAcceptableHeight(); } - virtual int GetMinimalLineSize(const wxGrid *grid, int line) const - { return grid->GetRowMinimalHeight(line); } - virtual void SetLineSize(wxGrid *grid, int line, int size) const - { grid->SetRowSize(line, size); } - virtual bool CanResizeLines(const wxGrid *grid) const - { return grid->CanDragRowSize(); } - - virtual int GetLineAt(const wxGrid * WXUNUSED(grid), int line) const - { return line; } // TODO: implement row reordering - - virtual wxWindow *GetHeaderWindow(wxGrid *grid) const - { return grid->GetGridRowLabelWindow(); } - virtual int GetHeaderWindowSize(wxGrid *grid) const - { return grid->GetRowLabelSize(); } -}; - -class wxGridColumnOperations : public wxGridOperations -{ -public: - virtual wxGridOperations& Dual() const; - - virtual int GetNumberOfLines(const wxGrid *grid) const - { return grid->GetNumberCols(); } - - virtual wxGrid::wxGridSelectionModes GetSelectionMode() const - { return wxGrid::wxGridSelectColumns; } - - virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const - { return wxGridCellCoords(otherDir, thisDir); } - - virtual int CalcScrolledPosition(wxGrid *grid, int pos) const - { return grid->CalcScrolledPosition(wxPoint(0, pos)).y; } - - virtual int Select(const wxGridCellCoords& c) const { return c.GetCol(); } - virtual int Select(const wxPoint& pt) const { return pt.y; } - virtual int Select(const wxSize& sz) const { return sz.y; } - virtual int Select(const wxRect& r) const { return r.y; } - virtual int& Select(wxRect& r) const { return r.y; } - virtual int& SelectSize(wxRect& r) const { return r.height; } - virtual wxSize MakeSize(int first, int second) const - { return wxSize(second, first); } - virtual void Set(wxGridCellCoords& coords, int line) const - { coords.SetCol(line); } - - virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const - { dc.DrawLine(pos, start, pos, end); } - - virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const - { return grid->XToCol(pos, clip); } - virtual int GetLineStartPos(const wxGrid *grid, int line) const - { return grid->GetColLeft(line); } - virtual int GetLineEndPos(const wxGrid *grid, int line) const - { return grid->GetColRight(line); } - virtual int GetLineSize(const wxGrid *grid, int line) const - { return grid->GetColWidth(line); } - virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const - { return grid->m_colRights; } - virtual int GetDefaultLineSize(const wxGrid *grid) const - { return grid->GetDefaultColSize(); } - virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const - { return grid->GetColMinimalAcceptableWidth(); } - virtual int GetMinimalLineSize(const wxGrid *grid, int line) const - { return grid->GetColMinimalWidth(line); } - virtual void SetLineSize(wxGrid *grid, int line, int size) const - { grid->SetColSize(line, size); } - virtual bool CanResizeLines(const wxGrid *grid) const - { return grid->CanDragColSize(); } - - virtual int GetLineAt(const wxGrid *grid, int line) const - { return grid->GetColAt(line); } - - virtual wxWindow *GetHeaderWindow(wxGrid *grid) const - { return grid->GetGridColLabelWindow(); } - virtual int GetHeaderWindowSize(wxGrid *grid) const - { return grid->GetColLabelSize(); } -}; - -wxGridOperations& wxGridRowOperations::Dual() const -{ - static wxGridColumnOperations s_colOper; - - return s_colOper; -} - -wxGridOperations& wxGridColumnOperations::Dual() const -{ - static wxGridRowOperations s_rowOper; - - return s_rowOper; -} - -// This class abstracts the difference between operations going forward -// (down/right) and backward (up/left) and allows to use the same code for -// functions which differ only in the direction of grid traversal -// -// Like wxGridOperations it's an ABC with two concrete subclasses below. Unlike -// it, this is a normal object and not just a function dispatch table and has a -// non-default ctor. -// -// Note: the explanation of this discrepancy is the existence of (very useful) -// Dual() method in wxGridOperations which forces us to make wxGridOperations a -// function dispatcher only. -class wxGridDirectionOperations -{ -public: - // The oper parameter to ctor selects whether we work with rows or columns - wxGridDirectionOperations(wxGrid *grid, const wxGridOperations& oper) - : m_grid(grid), - m_oper(oper) - { - } - - // Check if the component of this point in our direction is at the - // boundary, i.e. is the first/last row/column - virtual bool IsAtBoundary(const wxGridCellCoords& coords) const = 0; - - // Increment the component of this point in our direction - virtual void Advance(wxGridCellCoords& coords) const = 0; - - // Find the line at the given distance, in pixels, away from this one - // (this uses clipping, i.e. anything after the last line is counted as the - // last one and anything before the first one as 0) - virtual int MoveByPixelDistance(int line, int distance) const = 0; - - // This class is never used polymorphically but give it a virtual dtor - // anyhow to suppress g++ complaints about it - virtual ~wxGridDirectionOperations() { } - -protected: - wxGrid * const m_grid; - const wxGridOperations& m_oper; -}; - -class wxGridBackwardOperations : public wxGridDirectionOperations -{ -public: - wxGridBackwardOperations(wxGrid *grid, const wxGridOperations& oper) - : wxGridDirectionOperations(grid, oper) - { - } - - virtual bool IsAtBoundary(const wxGridCellCoords& coords) const - { - wxASSERT_MSG( m_oper.Select(coords) >= 0, "invalid row/column" ); - - return m_oper.Select(coords) == 0; - } - - virtual void Advance(wxGridCellCoords& coords) const - { - wxASSERT( !IsAtBoundary(coords) ); - - m_oper.Set(coords, m_oper.Select(coords) - 1); - } - - virtual int MoveByPixelDistance(int line, int distance) const - { - int pos = m_oper.GetLineStartPos(m_grid, line); - return m_oper.PosToLine(m_grid, pos - distance + 1, true); - } -}; - -class wxGridForwardOperations : public wxGridDirectionOperations -{ -public: - wxGridForwardOperations(wxGrid *grid, const wxGridOperations& oper) - : wxGridDirectionOperations(grid, oper), - m_numLines(oper.GetNumberOfLines(grid)) - { - } - - virtual bool IsAtBoundary(const wxGridCellCoords& coords) const - { - wxASSERT_MSG( m_oper.Select(coords) < m_numLines, "invalid row/column" ); - - return m_oper.Select(coords) == m_numLines - 1; - } - - virtual void Advance(wxGridCellCoords& coords) const - { - wxASSERT( !IsAtBoundary(coords) ); - - m_oper.Set(coords, m_oper.Select(coords) + 1); - } - - virtual int MoveByPixelDistance(int line, int distance) const - { - int pos = m_oper.GetLineStartPos(m_grid, line); - return m_oper.PosToLine(m_grid, pos + distance, true); - } - -private: - const int m_numLines; -}; - -// ---------------------------------------------------------------------------- -// globals -// ---------------------------------------------------------------------------- - -//#define DEBUG_ATTR_CACHE -#ifdef DEBUG_ATTR_CACHE - static size_t gs_nAttrCacheHits = 0; - static size_t gs_nAttrCacheMisses = 0; -#endif - -// ---------------------------------------------------------------------------- -// constants -// ---------------------------------------------------------------------------- - -wxGridCellCoords wxGridNoCellCoords( -1, -1 ); -wxRect wxGridNoCellRect( -1, -1, -1, -1 ); - -namespace -{ - -// scroll line size -const size_t GRID_SCROLL_LINE_X = 15; -const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; - -// the size of hash tables used a bit everywhere (the max number of elements -// in these hash tables is the number of rows/columns) -const int GRID_HASH_SIZE = 100; - -// the minimal distance in pixels the mouse needs to move to start a drag -// operation -const int DRAG_SENSITIVITY = 3; - -} // anonymous namespace - -// ---------------------------------------------------------------------------- -// private helpers -// ---------------------------------------------------------------------------- - -namespace -{ - -// ensure that first is less or equal to second, swapping the values if -// necessary -void EnsureFirstLessThanSecond(int& first, int& second) -{ - if ( first > second ) - wxSwap(first, second); -} - -} // anonymous namespace - -// ============================================================================ -// implementation -// ============================================================================ - -// ---------------------------------------------------------------------------- -// wxGridCellEditor -// ---------------------------------------------------------------------------- - -wxGridCellEditor::wxGridCellEditor() -{ - m_control = NULL; - m_attr = NULL; -} - -wxGridCellEditor::~wxGridCellEditor() -{ - Destroy(); -} - -void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent), - wxWindowID WXUNUSED(id), - wxEvtHandler* evtHandler) -{ - if ( evtHandler ) - m_control->PushEventHandler(evtHandler); -} - -void wxGridCellEditor::PaintBackground(const wxRect& rectCell, - wxGridCellAttr *attr) -{ - // erase the background because we might not fill the cell - wxClientDC dc(m_control->GetParent()); - wxGridWindow* gridWindow = wxDynamicCast(m_control->GetParent(), wxGridWindow); - if (gridWindow) - gridWindow->GetOwner()->PrepareDC(dc); - - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(wxBrush(attr->GetBackgroundColour())); - dc.DrawRectangle(rectCell); - - // redraw the control we just painted over - m_control->Refresh(); -} - -void wxGridCellEditor::Destroy() -{ - if (m_control) - { - m_control->PopEventHandler( true /* delete it*/ ); - - m_control->Destroy(); - m_control = NULL; - } -} - -void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - m_control->Show(show); - - if ( show ) - { - // set the colours/fonts if we have any - if ( attr ) - { - m_colFgOld = m_control->GetForegroundColour(); - m_control->SetForegroundColour(attr->GetTextColour()); - - m_colBgOld = m_control->GetBackgroundColour(); - m_control->SetBackgroundColour(attr->GetBackgroundColour()); - -// Workaround for GTK+1 font setting problem on some platforms -#if !defined(__WXGTK__) || defined(__WXGTK20__) - m_fontOld = m_control->GetFont(); - m_control->SetFont(attr->GetFont()); -#endif - - // can't do anything more in the base class version, the other - // attributes may only be used by the derived classes - } - } - else - { - // restore the standard colours fonts - if ( m_colFgOld.Ok() ) - { - m_control->SetForegroundColour(m_colFgOld); - m_colFgOld = wxNullColour; - } - - if ( m_colBgOld.Ok() ) - { - m_control->SetBackgroundColour(m_colBgOld); - m_colBgOld = wxNullColour; - } - -// Workaround for GTK+1 font setting problem on some platforms -#if !defined(__WXGTK__) || defined(__WXGTK20__) - if ( m_fontOld.Ok() ) - { - m_control->SetFont(m_fontOld); - m_fontOld = wxNullFont; - } -#endif - } -} - -void wxGridCellEditor::SetSize(const wxRect& rect) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - m_control->SetSize(rect, wxSIZE_ALLOW_MINUS_ONE); -} - -void wxGridCellEditor::HandleReturn(wxKeyEvent& event) -{ - event.Skip(); -} - -bool wxGridCellEditor::IsAcceptedKey(wxKeyEvent& event) -{ - bool ctrl = event.ControlDown(); - bool alt = event.AltDown(); - -#ifdef __WXMAC__ - // On the Mac the Alt key is more like shift and is used for entry of - // valid characters, so check for Ctrl and Meta instead. - alt = event.MetaDown(); -#endif - - // Assume it's not a valid char if ctrl or alt is down, but if both are - // down then it may be because of an AltGr key combination, so let them - // through in that case. - if ((ctrl || alt) && !(ctrl && alt)) - return false; - -#if wxUSE_UNICODE - // if the unicode key code is not really a unicode character (it may - // be a function key or etc., the platforms appear to always give us a - // small value in this case) then fallback to the ASCII key code but - // don't do anything for function keys or etc. - if ( event.GetUnicodeKey() > 127 && event.GetKeyCode() > 127 ) - return false; -#else - if ( event.GetKeyCode() > 255 ) - return false; -#endif - - return true; -} - -void wxGridCellEditor::StartingKey(wxKeyEvent& event) -{ - event.Skip(); -} - -void wxGridCellEditor::StartingClick() -{ -} - -#if wxUSE_TEXTCTRL - -// ---------------------------------------------------------------------------- -// wxGridCellTextEditor -// ---------------------------------------------------------------------------- - -wxGridCellTextEditor::wxGridCellTextEditor() -{ - m_maxChars = 0; -} - -void wxGridCellTextEditor::Create(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler) -{ - DoCreate(parent, id, evtHandler); -} - -void wxGridCellTextEditor::DoCreate(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler, - long style) -{ - style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER; - - m_control = new wxTextCtrl(parent, id, wxEmptyString, - wxDefaultPosition, wxDefaultSize, - style); - - // set max length allowed in the textctrl, if the parameter was set - if ( m_maxChars != 0 ) - { - Text()->SetMaxLength(m_maxChars); - } - - wxGridCellEditor::Create(parent, id, evtHandler); -} - -void wxGridCellTextEditor::PaintBackground(const wxRect& WXUNUSED(rectCell), - wxGridCellAttr * WXUNUSED(attr)) -{ - // as we fill the entire client area, - // don't do anything here to minimize flicker -} - -void wxGridCellTextEditor::SetSize(const wxRect& rectOrig) -{ - wxRect rect(rectOrig); - - // Make the edit control large enough to allow for internal margins - // - // TODO: remove this if the text ctrl sizing is improved esp. for unix - // -#if defined(__WXGTK__) - if (rect.x != 0) - { - rect.x += 1; - rect.y += 1; - rect.width -= 1; - rect.height -= 1; - } -#elif defined(__WXMSW__) - if ( rect.x == 0 ) - rect.x += 2; - else - rect.x += 3; - - if ( rect.y == 0 ) - rect.y += 2; - else - rect.y += 3; - - rect.width -= 2; - rect.height -= 2; -#else - int extra_x = ( rect.x > 2 ) ? 2 : 1; - int extra_y = ( rect.y > 2 ) ? 2 : 1; - - #if defined(__WXMOTIF__) - extra_x *= 2; - extra_y *= 2; - #endif - - rect.SetLeft( wxMax(0, rect.x - extra_x) ); - rect.SetTop( wxMax(0, rect.y - extra_y) ); - rect.SetRight( rect.GetRight() + 2 * extra_x ); - rect.SetBottom( rect.GetBottom() + 2 * extra_y ); -#endif - - wxGridCellEditor::SetSize(rect); -} - -void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - m_startValue = grid->GetTable()->GetValue(row, col); - - DoBeginEdit(m_startValue); -} - -void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue) -{ - Text()->SetValue(startValue); - Text()->SetInsertionPointEnd(); - Text()->SetSelection(-1, -1); - Text()->SetFocus(); -} - -bool wxGridCellTextEditor::EndEdit(int row, int col, wxGrid* grid) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - bool changed = false; - wxString value = Text()->GetValue(); - if (value != m_startValue) - changed = true; - - if (changed) - grid->GetTable()->SetValue(row, col, value); - - m_startValue = wxEmptyString; - - // No point in setting the text of the hidden control - //Text()->SetValue(m_startValue); - - return changed; -} - -void wxGridCellTextEditor::Reset() -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - DoReset(m_startValue); -} - -void wxGridCellTextEditor::DoReset(const wxString& startValue) -{ - Text()->SetValue(startValue); - Text()->SetInsertionPointEnd(); -} - -bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event) -{ - return wxGridCellEditor::IsAcceptedKey(event); -} - -void wxGridCellTextEditor::StartingKey(wxKeyEvent& event) -{ - // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no - // longer an appropriate way to get the character into the text control. - // Do it ourselves instead. We know that if we get this far that we have - // a valid character, so not a whole lot of testing needs to be done. - - wxTextCtrl* tc = Text(); - wxChar ch; - long pos; - -#if wxUSE_UNICODE - ch = event.GetUnicodeKey(); - if (ch <= 127) - ch = (wxChar)event.GetKeyCode(); -#else - ch = (wxChar)event.GetKeyCode(); -#endif - - switch (ch) - { - case WXK_DELETE: - // delete the character at the cursor - pos = tc->GetInsertionPoint(); - if (pos < tc->GetLastPosition()) - tc->Remove(pos, pos + 1); - break; - - case WXK_BACK: - // delete the character before the cursor - pos = tc->GetInsertionPoint(); - if (pos > 0) - tc->Remove(pos - 1, pos); - break; - - default: - tc->WriteText(ch); - break; - } -} - -void wxGridCellTextEditor::HandleReturn( wxKeyEvent& - WXUNUSED_GTK(WXUNUSED_MOTIF(event)) ) -{ -#if defined(__WXMOTIF__) || defined(__WXGTK__) - // wxMotif needs a little extra help... - size_t pos = (size_t)( Text()->GetInsertionPoint() ); - wxString s( Text()->GetValue() ); - s = s.Left(pos) + wxT("\n") + s.Mid(pos); - Text()->SetValue(s); - Text()->SetInsertionPoint( pos ); -#else - // the other ports can handle a Return key press - // - event.Skip(); -#endif -} - -void wxGridCellTextEditor::SetParameters(const wxString& params) -{ - if ( !params ) - { - // reset to default - m_maxChars = 0; - } - else - { - long tmp; - if ( params.ToLong(&tmp) ) - { - m_maxChars = (size_t)tmp; - } - else - { - wxLogDebug( _T("Invalid wxGridCellTextEditor parameter string '%s' ignored"), params.c_str() ); - } - } -} - -// return the value in the text control -wxString wxGridCellTextEditor::GetValue() const -{ - return Text()->GetValue(); -} - -// ---------------------------------------------------------------------------- -// wxGridCellNumberEditor -// ---------------------------------------------------------------------------- - -wxGridCellNumberEditor::wxGridCellNumberEditor(int min, int max) -{ - m_min = min; - m_max = max; -} - -void wxGridCellNumberEditor::Create(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler) -{ -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - // create a spin ctrl - m_control = new wxSpinCtrl(parent, wxID_ANY, wxEmptyString, - wxDefaultPosition, wxDefaultSize, - wxSP_ARROW_KEYS, - m_min, m_max); - - wxGridCellEditor::Create(parent, id, evtHandler); - } - else -#endif - { - // just a text control - wxGridCellTextEditor::Create(parent, id, evtHandler); - -#if wxUSE_VALIDATORS - Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); -#endif - } -} - -void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid) -{ - // first get the value - wxGridTableBase *table = grid->GetTable(); - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) ) - { - m_valueOld = table->GetValueAsLong(row, col); - } - else - { - m_valueOld = 0; - wxString sValue = table->GetValue(row, col); - if (! sValue.ToLong(&m_valueOld) && ! sValue.empty()) - { - wxFAIL_MSG( _T("this cell doesn't have numeric value") ); - return; - } - } - -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - Spin()->SetValue((int)m_valueOld); - Spin()->SetFocus(); - } - else -#endif - { - DoBeginEdit(GetString()); - } -} - -bool wxGridCellNumberEditor::EndEdit(int row, int col, - wxGrid* grid) -{ - long value = 0; - wxString text; - -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - value = Spin()->GetValue(); - if ( value == m_valueOld ) - return false; - - text.Printf(wxT("%ld"), value); - } - else // using unconstrained input -#endif // wxUSE_SPINCTRL - { - const wxString textOld(grid->GetCellValue(row, col)); - text = Text()->GetValue(); - if ( text.empty() ) - { - if ( textOld.empty() ) - return false; - } - else // non-empty text now (maybe 0) - { - if ( !text.ToLong(&value) ) - return false; - - // if value == m_valueOld == 0 but old text was "" and new one is - // "0" something still did change - if ( value == m_valueOld && (value || !textOld.empty()) ) - return false; - } - } - - wxGridTableBase * const table = grid->GetTable(); - if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) ) - table->SetValueAsLong(row, col, value); - else - table->SetValue(row, col, text); - - return true; -} - -void wxGridCellNumberEditor::Reset() -{ -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - Spin()->SetValue((int)m_valueOld); - } - else -#endif - { - DoReset(GetString()); - } -} - -bool wxGridCellNumberEditor::IsAcceptedKey(wxKeyEvent& event) -{ - if ( wxGridCellEditor::IsAcceptedKey(event) ) - { - int keycode = event.GetKeyCode(); - if ( (keycode < 128) && - (wxIsdigit(keycode) || keycode == '+' || keycode == '-')) - { - return true; - } - } - - return false; -} - -void wxGridCellNumberEditor::StartingKey(wxKeyEvent& event) -{ - int keycode = event.GetKeyCode(); - if ( !HasRange() ) - { - if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-') - { - wxGridCellTextEditor::StartingKey(event); - - // skip Skip() below - return; - } - } -#if wxUSE_SPINCTRL - else - { - if ( wxIsdigit(keycode) ) - { - wxSpinCtrl* spin = (wxSpinCtrl*)m_control; - spin->SetValue(keycode - '0'); - spin->SetSelection(1,1); - return; - } - } -#endif - - event.Skip(); -} - -void wxGridCellNumberEditor::SetParameters(const wxString& params) -{ - if ( !params ) - { - // reset to default - m_min = - m_max = -1; - } - else - { - long tmp; - if ( params.BeforeFirst(_T(',')).ToLong(&tmp) ) - { - m_min = (int)tmp; - - if ( params.AfterFirst(_T(',')).ToLong(&tmp) ) - { - m_max = (int)tmp; - - // skip the error message below - return; - } - } - - wxLogDebug(_T("Invalid wxGridCellNumberEditor parameter string '%s' ignored"), params.c_str()); - } -} - -// return the value in the spin control if it is there (the text control otherwise) -wxString wxGridCellNumberEditor::GetValue() const -{ - wxString s; - -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - long value = Spin()->GetValue(); - s.Printf(wxT("%ld"), value); - } - else -#endif - { - s = Text()->GetValue(); - } - - return s; -} - -// ---------------------------------------------------------------------------- -// wxGridCellFloatEditor -// ---------------------------------------------------------------------------- - -wxGridCellFloatEditor::wxGridCellFloatEditor(int width, int precision) -{ - m_width = width; - m_precision = precision; -} - -void wxGridCellFloatEditor::Create(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler) -{ - wxGridCellTextEditor::Create(parent, id, evtHandler); - -#if wxUSE_VALIDATORS - Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); -#endif -} - -void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid) -{ - // first get the value - wxGridTableBase * const table = grid->GetTable(); - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) - { - m_valueOld = table->GetValueAsDouble(row, col); - } - else - { - m_valueOld = 0.0; - - const wxString value = table->GetValue(row, col); - if ( !value.empty() ) - { - if ( !value.ToDouble(&m_valueOld) ) - { - wxFAIL_MSG( _T("this cell doesn't have float value") ); - return; - } - } - } - - DoBeginEdit(GetString()); -} - -bool wxGridCellFloatEditor::EndEdit(int row, int col, wxGrid* grid) -{ - const wxString text(Text()->GetValue()), - textOld(grid->GetCellValue(row, col)); - - double value; - if ( !text.empty() ) - { - if ( !text.ToDouble(&value) ) - return false; - } - else // new value is empty string - { - if ( textOld.empty() ) - return false; // nothing changed - - value = 0.; - } - - // the test for empty strings ensures that we don't skip the value setting - // when "" is replaced by "0" or vice versa as "" numeric value is also 0. - if ( wxIsSameDouble(value, m_valueOld) && !text.empty() && !textOld.empty() ) - return false; // nothing changed - - wxGridTableBase * const table = grid->GetTable(); - - if ( table->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT) ) - table->SetValueAsDouble(row, col, value); - else - table->SetValue(row, col, text); - - return true; -} - -void wxGridCellFloatEditor::Reset() -{ - DoReset(GetString()); -} - -void wxGridCellFloatEditor::StartingKey(wxKeyEvent& event) -{ - int keycode = event.GetKeyCode(); - char tmpbuf[2]; - tmpbuf[0] = (char) keycode; - tmpbuf[1] = '\0'; - wxString strbuf(tmpbuf, *wxConvCurrent); - -#if wxUSE_INTL - bool is_decimal_point = ( strbuf == - wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER) ); -#else - bool is_decimal_point = ( strbuf == _T(".") ); -#endif - - if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-' - || is_decimal_point ) - { - wxGridCellTextEditor::StartingKey(event); - - // skip Skip() below - return; - } - - event.Skip(); -} - -void wxGridCellFloatEditor::SetParameters(const wxString& params) -{ - if ( !params ) - { - // reset to default - m_width = - m_precision = -1; - } - else - { - long tmp; - if ( params.BeforeFirst(_T(',')).ToLong(&tmp) ) - { - m_width = (int)tmp; - - if ( params.AfterFirst(_T(',')).ToLong(&tmp) ) - { - m_precision = (int)tmp; - - // skip the error message below - return; - } - } - - wxLogDebug(_T("Invalid wxGridCellFloatEditor parameter string '%s' ignored"), params.c_str()); - } -} - -wxString wxGridCellFloatEditor::GetString() const -{ - wxString fmt; - if ( m_precision == -1 && m_width != -1) - { - // default precision - fmt.Printf(_T("%%%d.f"), m_width); - } - else if ( m_precision != -1 && m_width == -1) - { - // default width - fmt.Printf(_T("%%.%df"), m_precision); - } - else if ( m_precision != -1 && m_width != -1 ) - { - fmt.Printf(_T("%%%d.%df"), m_width, m_precision); - } - else - { - // default width/precision - fmt = _T("%f"); - } - - return wxString::Format(fmt, m_valueOld); -} - -bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event) -{ - if ( wxGridCellEditor::IsAcceptedKey(event) ) - { - const int keycode = event.GetKeyCode(); - if ( isascii(keycode) ) - { - char tmpbuf[2]; - tmpbuf[0] = (char) keycode; - tmpbuf[1] = '\0'; - wxString strbuf(tmpbuf, *wxConvCurrent); - -#if wxUSE_INTL - const wxString decimalPoint = - wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER); -#else - const wxString decimalPoint(_T('.')); -#endif - - // accept digits, 'e' as in '1e+6', also '-', '+', and '.' - if ( wxIsdigit(keycode) || - tolower(keycode) == 'e' || - keycode == decimalPoint || - keycode == '+' || - keycode == '-' ) - { - return true; - } - } - } - - return false; -} - -#endif // wxUSE_TEXTCTRL - -#if wxUSE_CHECKBOX - -// ---------------------------------------------------------------------------- -// wxGridCellBoolEditor -// ---------------------------------------------------------------------------- - -// the default values for GetValue() -wxString wxGridCellBoolEditor::ms_stringValues[2] = { _T(""), _T("1") }; - -void wxGridCellBoolEditor::Create(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler) -{ - m_control = new wxCheckBox(parent, id, wxEmptyString, - wxDefaultPosition, wxDefaultSize, - wxNO_BORDER); - - wxGridCellEditor::Create(parent, id, evtHandler); -} - -void wxGridCellBoolEditor::SetSize(const wxRect& r) -{ - bool resize = false; - wxSize size = m_control->GetSize(); - wxCoord minSize = wxMin(r.width, r.height); - - // check if the checkbox is not too big/small for this cell - wxSize sizeBest = m_control->GetBestSize(); - if ( !(size == sizeBest) ) - { - // reset to default size if it had been made smaller - size = sizeBest; - - resize = true; - } - - if ( size.x >= minSize || size.y >= minSize ) - { - // leave 1 pixel margin - size.x = size.y = minSize - 2; - - resize = true; - } - - if ( resize ) - { - m_control->SetSize(size); - } - - // position it in the centre of the rectangle (TODO: support alignment?) - -#if defined(__WXGTK__) || defined (__WXMOTIF__) - // the checkbox without label still has some space to the right in wxGTK, - // so shift it to the right - size.x -= 8; -#elif defined(__WXMSW__) - // here too, but in other way - size.x += 1; - size.y -= 2; -#endif - - int hAlign = wxALIGN_CENTRE; - int vAlign = wxALIGN_CENTRE; - if (GetCellAttr()) - GetCellAttr()->GetAlignment(& hAlign, & vAlign); - - int x = 0, y = 0; - if (hAlign == wxALIGN_LEFT) - { - x = r.x + 2; - -#ifdef __WXMSW__ - x += 2; -#endif - - y = r.y + r.height / 2 - size.y / 2; - } - else if (hAlign == wxALIGN_RIGHT) - { - x = r.x + r.width - size.x - 2; - y = r.y + r.height / 2 - size.y / 2; - } - else if (hAlign == wxALIGN_CENTRE) - { - x = r.x + r.width / 2 - size.x / 2; - y = r.y + r.height / 2 - size.y / 2; - } - - m_control->Move(x, y); -} - -void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr) -{ - m_control->Show(show); - - if ( show ) - { - wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY; - CBox()->SetBackgroundColour(colBg); - } -} - -void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid) -{ - wxASSERT_MSG(m_control, - wxT("The wxGridCellEditor must be created first!")); - - if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL)) - { - m_startValue = grid->GetTable()->GetValueAsBool(row, col); - } - else - { - wxString cellval( grid->GetTable()->GetValue(row, col) ); - - if ( cellval == ms_stringValues[false] ) - m_startValue = false; - else if ( cellval == ms_stringValues[true] ) - m_startValue = true; - else - { - // do not try to be smart here and convert it to true or false - // because we'll still overwrite it with something different and - // this risks to be very surprising for the user code, let them - // know about it - wxFAIL_MSG( _T("invalid value for a cell with bool editor!") ); - } - } - - CBox()->SetValue(m_startValue); - CBox()->SetFocus(); -} - -bool wxGridCellBoolEditor::EndEdit(int row, int col, - wxGrid* grid) -{ - wxASSERT_MSG(m_control, - wxT("The wxGridCellEditor must be created first!")); - - bool changed = false; - bool value = CBox()->GetValue(); - if ( value != m_startValue ) - changed = true; - - if ( changed ) - { - wxGridTableBase * const table = grid->GetTable(); - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) - table->SetValueAsBool(row, col, value); - else - table->SetValue(row, col, GetValue()); - } - - return changed; -} - -void wxGridCellBoolEditor::Reset() -{ - wxASSERT_MSG(m_control, - wxT("The wxGridCellEditor must be created first!")); - - CBox()->SetValue(m_startValue); -} - -void wxGridCellBoolEditor::StartingClick() -{ - CBox()->SetValue(!CBox()->GetValue()); -} - -bool wxGridCellBoolEditor::IsAcceptedKey(wxKeyEvent& event) -{ - if ( wxGridCellEditor::IsAcceptedKey(event) ) - { - int keycode = event.GetKeyCode(); - switch ( keycode ) - { - case WXK_SPACE: - case '+': - case '-': - return true; - } - } - - return false; -} - -void wxGridCellBoolEditor::StartingKey(wxKeyEvent& event) -{ - int keycode = event.GetKeyCode(); - switch ( keycode ) - { - case WXK_SPACE: - CBox()->SetValue(!CBox()->GetValue()); - break; - - case '+': - CBox()->SetValue(true); - break; - - case '-': - CBox()->SetValue(false); - break; - } -} - -wxString wxGridCellBoolEditor::GetValue() const -{ - return ms_stringValues[CBox()->GetValue()]; -} - -/* static */ void -wxGridCellBoolEditor::UseStringValues(const wxString& valueTrue, - const wxString& valueFalse) -{ - ms_stringValues[false] = valueFalse; - ms_stringValues[true] = valueTrue; -} - -/* static */ bool -wxGridCellBoolEditor::IsTrueValue(const wxString& value) -{ - return value == ms_stringValues[true]; -} - -#endif // wxUSE_CHECKBOX - -#if wxUSE_COMBOBOX - -// ---------------------------------------------------------------------------- -// wxGridCellChoiceEditor -// ---------------------------------------------------------------------------- - -wxGridCellChoiceEditor::wxGridCellChoiceEditor(const wxArrayString& choices, - bool allowOthers) - : m_choices(choices), - m_allowOthers(allowOthers) { } - -wxGridCellChoiceEditor::wxGridCellChoiceEditor(size_t count, - const wxString choices[], - bool allowOthers) - : m_allowOthers(allowOthers) -{ - if ( count ) - { - m_choices.Alloc(count); - for ( size_t n = 0; n < count; n++ ) - { - m_choices.Add(choices[n]); - } - } -} - -wxGridCellEditor *wxGridCellChoiceEditor::Clone() const -{ - wxGridCellChoiceEditor *editor = new wxGridCellChoiceEditor; - editor->m_allowOthers = m_allowOthers; - editor->m_choices = m_choices; - - return editor; -} - -void wxGridCellChoiceEditor::Create(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler) -{ - int style = wxTE_PROCESS_ENTER | - wxTE_PROCESS_TAB | - wxBORDER_NONE; - - if ( !m_allowOthers ) - style |= wxCB_READONLY; - m_control = new wxComboBox(parent, id, wxEmptyString, - wxDefaultPosition, wxDefaultSize, - m_choices, - style); - - wxGridCellEditor::Create(parent, id, evtHandler); -} - -void wxGridCellChoiceEditor::PaintBackground(const wxRect& rectCell, - wxGridCellAttr * attr) -{ - // as we fill the entire client area, don't do anything here to minimize - // flicker - - // TODO: It doesn't actually fill the client area since the height of a - // combo always defaults to the standard. Until someone has time to - // figure out the right rectangle to paint, just do it the normal way. - wxGridCellEditor::PaintBackground(rectCell, attr); -} - -void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid) -{ - wxASSERT_MSG(m_control, - wxT("The wxGridCellEditor must be created first!")); - - wxGridCellEditorEvtHandler* evtHandler = NULL; - if (m_control) - evtHandler = wxDynamicCast(m_control->GetEventHandler(), wxGridCellEditorEvtHandler); - - // Don't immediately end if we get a kill focus event within BeginEdit - if (evtHandler) - evtHandler->SetInSetFocus(true); - - m_startValue = grid->GetTable()->GetValue(row, col); - - Reset(); // this updates combo box to correspond to m_startValue - - Combo()->SetFocus(); - - if (evtHandler) - { - // When dropping down the menu, a kill focus event - // happens after this point, so we can't reset the flag yet. -#if !defined(__WXGTK20__) - evtHandler->SetInSetFocus(false); -#endif - } -} - -bool wxGridCellChoiceEditor::EndEdit(int row, int col, - wxGrid* grid) -{ - wxString value = Combo()->GetValue(); - if ( value == m_startValue ) - return false; - - grid->GetTable()->SetValue(row, col, value); - - return true; -} - -void wxGridCellChoiceEditor::Reset() -{ - if (m_allowOthers) - { - Combo()->SetValue(m_startValue); - Combo()->SetInsertionPointEnd(); - } - else // the combobox is read-only - { - // find the right position, or default to the first if not found - int pos = Combo()->FindString(m_startValue); - if (pos == wxNOT_FOUND) - pos = 0; - Combo()->SetSelection(pos); - } -} - -void wxGridCellChoiceEditor::SetParameters(const wxString& params) -{ - if ( !params ) - { - // what can we do? - return; - } - - m_choices.Empty(); - - wxStringTokenizer tk(params, _T(',')); - while ( tk.HasMoreTokens() ) - { - m_choices.Add(tk.GetNextToken()); - } -} - -// return the value in the text control -wxString wxGridCellChoiceEditor::GetValue() const -{ - return Combo()->GetValue(); -} - -#endif // wxUSE_COMBOBOX - -// ---------------------------------------------------------------------------- -// wxGridCellEditorEvtHandler -// ---------------------------------------------------------------------------- - -void wxGridCellEditorEvtHandler::OnKillFocus(wxFocusEvent& event) -{ - // Don't disable the cell if we're just starting to edit it - if (m_inSetFocus) - return; - - // accept changes - m_grid->DisableCellEditControl(); - - event.Skip(); -} - -void wxGridCellEditorEvtHandler::OnKeyDown(wxKeyEvent& event) -{ - switch ( event.GetKeyCode() ) - { - case WXK_ESCAPE: - m_editor->Reset(); - m_grid->DisableCellEditControl(); - break; - - case WXK_TAB: - m_grid->GetEventHandler()->ProcessEvent( event ); - break; - - case WXK_RETURN: - case WXK_NUMPAD_ENTER: - if (!m_grid->GetEventHandler()->ProcessEvent(event)) - m_editor->HandleReturn(event); - break; - - default: - event.Skip(); - break; - } -} - -void wxGridCellEditorEvtHandler::OnChar(wxKeyEvent& event) -{ - int row = m_grid->GetGridCursorRow(); - int col = m_grid->GetGridCursorCol(); - wxRect rect = m_grid->CellToRect( row, col ); - int cw, ch; - m_grid->GetGridWindow()->GetClientSize( &cw, &ch ); - - // if cell width is smaller than grid client area, cell is wholly visible - bool wholeCellVisible = (rect.GetWidth() < cw); - - switch ( event.GetKeyCode() ) - { - case WXK_ESCAPE: - case WXK_TAB: - case WXK_RETURN: - case WXK_NUMPAD_ENTER: - break; - - case WXK_HOME: - { - if ( wholeCellVisible ) - { - // no special processing needed... - event.Skip(); - break; - } - - // do special processing for partly visible cell... - - // get the widths of all cells previous to this one - int colXPos = 0; - for ( int i = 0; i < col; i++ ) - { - colXPos += m_grid->GetColSize(i); - } - - int xUnit = 1, yUnit = 1; - m_grid->GetScrollPixelsPerUnit(&xUnit, &yUnit); - if (col != 0) - { - m_grid->Scroll(colXPos / xUnit - 1, m_grid->GetScrollPos(wxVERTICAL)); - } - else - { - m_grid->Scroll(colXPos / xUnit, m_grid->GetScrollPos(wxVERTICAL)); - } - event.Skip(); - break; - } - - case WXK_END: - { - if ( wholeCellVisible ) - { - // no special processing needed... - event.Skip(); - break; - } - - // do special processing for partly visible cell... - - int textWidth = 0; - wxString value = m_grid->GetCellValue(row, col); - if ( wxEmptyString != value ) - { - // get width of cell CONTENTS (text) - int y; - wxFont font = m_grid->GetCellFont(row, col); - m_grid->GetTextExtent(value, &textWidth, &y, NULL, NULL, &font); - - // try to RIGHT align the text by scrolling - int client_right = m_grid->GetGridWindow()->GetClientSize().GetWidth(); - - // (m_grid->GetScrollLineX()*2) is a factor for not scrolling to far, - // otherwise the last part of the cell content might be hidden below the scroll bar - // FIXME: maybe there is a more suitable correction? - textWidth -= (client_right - (m_grid->GetScrollLineX() * 2)); - if ( textWidth < 0 ) - { - textWidth = 0; - } - } - - // get the widths of all cells previous to this one - int colXPos = 0; - for ( int i = 0; i < col; i++ ) - { - colXPos += m_grid->GetColSize(i); - } - - // and add the (modified) text width of the cell contents - // as we'd like to see the last part of the cell contents - colXPos += textWidth; - - int xUnit = 1, yUnit = 1; - m_grid->GetScrollPixelsPerUnit(&xUnit, &yUnit); - m_grid->Scroll(colXPos / xUnit - 1, m_grid->GetScrollPos(wxVERTICAL)); - event.Skip(); - break; - } - - default: - event.Skip(); - break; - } -} - -// ---------------------------------------------------------------------------- -// wxGridCellWorker is an (almost) empty common base class for -// wxGridCellRenderer and wxGridCellEditor managing ref counting -// ---------------------------------------------------------------------------- - -void wxGridCellWorker::SetParameters(const wxString& WXUNUSED(params)) -{ - // nothing to do -} - -wxGridCellWorker::~wxGridCellWorker() -{ -} - -// ============================================================================ -// renderer classes -// ============================================================================ - -// ---------------------------------------------------------------------------- -// wxGridCellRenderer -// ---------------------------------------------------------------------------- - -void wxGridCellRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int WXUNUSED(row), int WXUNUSED(col), - bool isSelected) -{ - dc.SetBackgroundMode( wxBRUSHSTYLE_SOLID ); - - wxColour clr; - if ( grid.IsEnabled() ) - { - if ( isSelected ) - { - if ( grid.HasFocus() ) - clr = grid.GetSelectionBackground(); - else - clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); - } - else - { - clr = attr.GetBackgroundColour(); - } - } - else // grey out fields if the grid is disabled - { - clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); - } - - dc.SetBrush(clr); - dc.SetPen( *wxTRANSPARENT_PEN ); - dc.DrawRectangle(rect); -} - -// ---------------------------------------------------------------------------- -// wxGridCellStringRenderer -// ---------------------------------------------------------------------------- - -void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid& grid, - const wxGridCellAttr& attr, - wxDC& dc, - bool isSelected) -{ - dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); - - // TODO some special colours for attr.IsReadOnly() case? - - // different coloured text when the grid is disabled - if ( grid.IsEnabled() ) - { - if ( isSelected ) - { - wxColour clr; - if ( grid.HasFocus() ) - clr = grid.GetSelectionBackground(); - else - clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); - dc.SetTextBackground( clr ); - dc.SetTextForeground( grid.GetSelectionForeground() ); - } - else - { - dc.SetTextBackground( attr.GetBackgroundColour() ); - dc.SetTextForeground( attr.GetTextColour() ); - } - } - else - { - dc.SetTextBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - } - - dc.SetFont( attr.GetFont() ); -} - -wxSize wxGridCellStringRenderer::DoGetBestSize(const wxGridCellAttr& attr, - wxDC& dc, - const wxString& text) -{ - wxCoord x = 0, y = 0, max_x = 0; - dc.SetFont(attr.GetFont()); - wxStringTokenizer tk(text, _T('\n')); - while ( tk.HasMoreTokens() ) - { - dc.GetTextExtent(tk.GetNextToken(), &x, &y); - max_x = wxMax(max_x, x); - } - - y *= 1 + text.Freq(wxT('\n')); // multiply by the number of lines. - - return wxSize(max_x, y); -} - -wxSize wxGridCellStringRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - int row, int col) -{ - return DoGetBestSize(attr, dc, grid.GetCellValue(row, col)); -} - -void wxGridCellStringRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rectCell, - int row, int col, - bool isSelected) -{ - wxRect rect = rectCell; - rect.Inflate(-1); - - // erase only this cells background, overflow cells should have been erased - wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); - - int hAlign, vAlign; - attr.GetAlignment(&hAlign, &vAlign); - - int overflowCols = 0; - - if (attr.GetOverflow()) - { - int cols = grid.GetNumberCols(); - int best_width = GetBestSize(grid,attr,dc,row,col).GetWidth(); - int cell_rows, cell_cols; - attr.GetSize( &cell_rows, &cell_cols ); // shouldn't get here if <= 0 - if ((best_width > rectCell.width) && (col < cols) && grid.GetTable()) - { - int i, c_cols, c_rows; - for (i = col+cell_cols; i < cols; i++) - { - bool is_empty = true; - for (int j=row; j < row + cell_rows; j++) - { - // check w/ anchor cell for multicell block - grid.GetCellSize(j, i, &c_rows, &c_cols); - if (c_rows > 0) - c_rows = 0; - if (!grid.GetTable()->IsEmptyCell(j + c_rows, i)) - { - is_empty = false; - break; - } - } - - if (is_empty) - { - rect.width += grid.GetColSize(i); - } - else - { - i--; - break; - } - - if (rect.width >= best_width) - break; - } - - overflowCols = i - col - cell_cols + 1; - if (overflowCols >= cols) - overflowCols = cols - 1; - } - - if (overflowCols > 0) // redraw overflow cells w/ proper hilight - { - hAlign = wxALIGN_LEFT; // if oveflowed then it's left aligned - wxRect clip = rect; - clip.x += rectCell.width; - // draw each overflow cell individually - int col_end = col + cell_cols + overflowCols; - if (col_end >= grid.GetNumberCols()) - col_end = grid.GetNumberCols() - 1; - for (int i = col + cell_cols; i <= col_end; i++) - { - clip.width = grid.GetColSize(i) - 1; - dc.DestroyClippingRegion(); - dc.SetClippingRegion(clip); - - SetTextColoursAndFont(grid, attr, dc, - grid.IsInSelection(row,i)); - - grid.DrawTextRectangle(dc, grid.GetCellValue(row, col), - rect, hAlign, vAlign); - clip.x += grid.GetColSize(i) - 1; - } - - rect = rectCell; - rect.Inflate(-1); - rect.width++; - dc.DestroyClippingRegion(); - } - } - - // now we only have to draw the text - SetTextColoursAndFont(grid, attr, dc, isSelected); - - grid.DrawTextRectangle(dc, grid.GetCellValue(row, col), - rect, hAlign, vAlign); -} - -// ---------------------------------------------------------------------------- -// wxGridCellNumberRenderer -// ---------------------------------------------------------------------------- - -wxString wxGridCellNumberRenderer::GetString(const wxGrid& grid, int row, int col) -{ - wxGridTableBase *table = grid.GetTable(); - wxString text; - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) ) - { - text.Printf(_T("%ld"), table->GetValueAsLong(row, col)); - } - else - { - text = table->GetValue(row, col); - } - - return text; -} - -void wxGridCellNumberRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rectCell, - int row, int col, - bool isSelected) -{ - wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); - - SetTextColoursAndFont(grid, attr, dc, isSelected); - - // draw the text right aligned by default - int hAlign, vAlign; - attr.GetAlignment(&hAlign, &vAlign); - hAlign = wxALIGN_RIGHT; - - wxRect rect = rectCell; - rect.Inflate(-1); - - grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); -} - -wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - int row, int col) -{ - return DoGetBestSize(attr, dc, GetString(grid, row, col)); -} - -// ---------------------------------------------------------------------------- -// wxGridCellFloatRenderer -// ---------------------------------------------------------------------------- - -wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width, int precision) -{ - SetWidth(width); - SetPrecision(precision); -} - -wxGridCellRenderer *wxGridCellFloatRenderer::Clone() const -{ - wxGridCellFloatRenderer *renderer = new wxGridCellFloatRenderer; - renderer->m_width = m_width; - renderer->m_precision = m_precision; - renderer->m_format = m_format; - - return renderer; -} - -wxString wxGridCellFloatRenderer::GetString(const wxGrid& grid, int row, int col) -{ - wxGridTableBase *table = grid.GetTable(); - - bool hasDouble; - double val; - wxString text; - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) - { - val = table->GetValueAsDouble(row, col); - hasDouble = true; - } - else - { - text = table->GetValue(row, col); - hasDouble = text.ToDouble(&val); - } - - if ( hasDouble ) - { - if ( !m_format ) - { - if ( m_width == -1 ) - { - if ( m_precision == -1 ) - { - // default width/precision - m_format = _T("%f"); - } - else - { - m_format.Printf(_T("%%.%df"), m_precision); - } - } - else if ( m_precision == -1 ) - { - // default precision - m_format.Printf(_T("%%%d.f"), m_width); - } - else - { - m_format.Printf(_T("%%%d.%df"), m_width, m_precision); - } - } + EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize) + EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing) + EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize) - text.Printf(m_format, val); + EVT_HEADER_BEGIN_REORDER(wxID_ANY, wxGridHeaderCtrl::OnBeginReorder) + EVT_HEADER_END_REORDER(wxID_ANY, wxGridHeaderCtrl::OnEndReorder) +END_EVENT_TABLE() - } - //else: text already contains the string +wxGridOperations& wxGridRowOperations::Dual() const +{ + static wxGridColumnOperations s_colOper; - return text; + return s_colOper; } -void wxGridCellFloatRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rectCell, - int row, int col, - bool isSelected) +wxGridOperations& wxGridColumnOperations::Dual() const { - wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); - - SetTextColoursAndFont(grid, attr, dc, isSelected); - - // draw the text right aligned by default - int hAlign, vAlign; - attr.GetAlignment(&hAlign, &vAlign); - hAlign = wxALIGN_RIGHT; - - wxRect rect = rectCell; - rect.Inflate(-1); + static wxGridRowOperations s_rowOper; - grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); + return s_rowOper; } -wxSize wxGridCellFloatRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - int row, int col) +// ---------------------------------------------------------------------------- +// wxGridCellWorker is an (almost) empty common base class for +// wxGridCellRenderer and wxGridCellEditor managing ref counting +// ---------------------------------------------------------------------------- + +void wxGridCellWorker::SetParameters(const wxString& WXUNUSED(params)) { - return DoGetBestSize(attr, dc, GetString(grid, row, col)); + // nothing to do } -void wxGridCellFloatRenderer::SetParameters(const wxString& params) +wxGridCellWorker::~wxGridCellWorker() { - if ( !params ) - { - // reset to defaults - SetWidth(-1); - SetPrecision(-1); - } - else - { - wxString tmp = params.BeforeFirst(_T(',')); - if ( !tmp.empty() ) - { - long width; - if ( tmp.ToLong(&width) ) - { - SetWidth((int)width); - } - else - { - wxLogDebug(_T("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str()); - } - } - - tmp = params.AfterFirst(_T(',')); - if ( !tmp.empty() ) - { - long precision; - if ( tmp.ToLong(&precision) ) - { - SetPrecision((int)precision); - } - else - { - wxLogDebug(_T("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params.c_str()); - } - } - } } // ---------------------------------------------------------------------------- -// wxGridCellBoolRenderer +// wxGridHeaderLabelsRenderer and related classes // ---------------------------------------------------------------------------- -wxSize wxGridCellBoolRenderer::ms_sizeCheckMark; - -// FIXME these checkbox size calculations are really ugly... - -// between checkmark and box -static const wxCoord wxGRID_CHECKMARK_MARGIN = 2; - -wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& WXUNUSED(attr), - wxDC& WXUNUSED(dc), - int WXUNUSED(row), - int WXUNUSED(col)) +void wxGridHeaderLabelsRenderer::DrawLabel(const wxGrid& grid, + wxDC& dc, + const wxString& value, + const wxRect& rect, + int horizAlign, + int vertAlign, + int textOrientation) const { - // compute it only once (no locks for MT safeness in GUI thread...) - if ( !ms_sizeCheckMark.x ) - { - // get checkbox size - wxCheckBox *checkbox = new wxCheckBox(&grid, wxID_ANY, wxEmptyString); - wxSize size = checkbox->GetBestSize(); - wxCoord checkSize = size.y + 2 * wxGRID_CHECKMARK_MARGIN; + dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT); + dc.SetTextForeground(grid.GetLabelTextColour()); + dc.SetFont(grid.GetLabelFont()); + grid.DrawTextRectangle(dc, value, rect, horizAlign, vertAlign, textOrientation); +} -#if defined(__WXMOTIF__) - checkSize -= size.y / 2; -#endif - delete checkbox; +void wxGridRowHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid), + wxDC& dc, + wxRect& rect) const +{ + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine(rect.GetRight(), rect.GetTop(), + rect.GetRight(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetLeft(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetBottom(), + rect.GetRight() + 1, rect.GetBottom()); - ms_sizeCheckMark.x = ms_sizeCheckMark.y = checkSize; - } + dc.SetPen(*wxWHITE_PEN); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), + rect.GetLeft() + 1, rect.GetBottom()); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), + rect.GetRight(), rect.GetTop()); - return ms_sizeCheckMark; + rect.Deflate(2); } -void wxGridCellBoolRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected) +void wxGridColumnHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid), + wxDC& dc, + wxRect& rect) const { - wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); - - // draw a check mark in the centre (ignoring alignment - TODO) - wxSize size = GetBestSize(grid, attr, dc, row, col); - - // don't draw outside the cell - wxCoord minSize = wxMin(rect.width, rect.height); - if ( size.x >= minSize || size.y >= minSize ) - { - // and even leave (at least) 1 pixel margin - size.x = size.y = minSize; - } + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine(rect.GetRight(), rect.GetTop(), + rect.GetRight(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetRight(), rect.GetTop()); + dc.DrawLine(rect.GetLeft(), rect.GetBottom(), + rect.GetRight() + 1, rect.GetBottom()); - // draw a border around checkmark - int vAlign, hAlign; - attr.GetAlignment(&hAlign, &vAlign); + dc.SetPen(*wxWHITE_PEN); + dc.DrawLine(rect.GetLeft(), rect.GetTop() + 1, + rect.GetLeft(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetTop() + 1, + rect.GetRight(), rect.GetTop() + 1); - wxRect rectBorder; - if (hAlign == wxALIGN_CENTRE) - { - rectBorder.x = rect.x + rect.width / 2 - size.x / 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } - else if (hAlign == wxALIGN_LEFT) - { - rectBorder.x = rect.x + 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } - else if (hAlign == wxALIGN_RIGHT) - { - rectBorder.x = rect.x + rect.width - size.x - 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } + rect.Deflate(2); +} - bool value; - if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) - { - value = grid.GetTable()->GetValueAsBool(row, col); - } - else - { - wxString cellval( grid.GetTable()->GetValue(row, col) ); - value = wxGridCellBoolEditor::IsTrueValue(cellval); - } +void wxGridCornerHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid), + wxDC& dc, + wxRect& rect) const +{ + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine(rect.GetRight() - 1, rect.GetBottom() - 1, + rect.GetRight() - 1, rect.GetTop()); + dc.DrawLine(rect.GetRight() - 1, rect.GetBottom() - 1, + rect.GetLeft(), rect.GetBottom() - 1); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetRight(), rect.GetTop()); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetLeft(), rect.GetBottom()); - int flags = 0; - if (value) - flags |= wxCONTROL_CHECKED; + dc.SetPen(*wxWHITE_PEN); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop() + 1, + rect.GetRight() - 1, rect.GetTop() + 1); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop() + 1, + rect.GetLeft() + 1, rect.GetBottom() - 1); - wxRendererNative::Get().DrawCheckBox( &grid, dc, rectBorder, flags ); + rect.Deflate(2); } // ---------------------------------------------------------------------------- @@ -2820,8 +316,6 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, void wxGridCellAttr::Init(wxGridCellAttr *attrDefault) { - m_nRef = 1; - m_isReadOnly = Unset; m_renderer = NULL; @@ -3001,6 +495,15 @@ void wxGridCellAttr::GetAlignment(int *hAlign, int *vAlign) const } } +void wxGridCellAttr::GetNonDefaultAlignment(int *hAlign, int *vAlign) const +{ + if ( hAlign && m_hAlign != wxALIGN_INVALID ) + *hAlign = m_hAlign; + + if ( vAlign && m_vAlign != wxALIGN_INVALID ) + *vAlign = m_vAlign; +} + void wxGridCellAttr::GetSize( int *num_rows, int *num_cols ) const { if ( num_rows ) @@ -3268,30 +771,30 @@ void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol) { if ( attr ) { - // add the attribute - no need to do anything to reference count - // since we take ownership of the attribute. + // store the new attribute, taking its ownership m_rowsOrCols.Add(rowOrCol); m_attrs.Add(attr); } // nothing to remove } - else + else // we have an attribute for this row or column { size_t n = (size_t)i; - if ( m_attrs[n] == attr ) - // nothing to do - return; + + // notice that this code works correctly even when the old attribute is + // the same as the new one: as we own of it, we must call DecRef() on + // it in any case and this won't result in destruction of the new + // attribute if it's the same as old one because it must have ref count + // of at least 2 to be passed to us while we keep a reference to it too + m_attrs[n]->DecRef(); + if ( attr ) { - // change the attribute, handling reference count manually, - // taking ownership of the new attribute. - m_attrs[n]->DecRef(); + // replace the attribute with the new one m_attrs[n] = attr; } - else + else // remove the attribute { - // remove this attribute, handling reference count manually - m_attrs[n]->DecRef(); m_rowsOrCols.RemoveAt(n); m_attrs.RemoveAt(n); } @@ -3484,165 +987,21 @@ void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols ) } } -// ---------------------------------------------------------------------------- -// wxGridTypeRegistry -// ---------------------------------------------------------------------------- - -wxGridTypeRegistry::~wxGridTypeRegistry() -{ - size_t count = m_typeinfo.GetCount(); - for ( size_t i = 0; i < count; i++ ) - delete m_typeinfo[i]; -} - -void wxGridTypeRegistry::RegisterDataType(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor) -{ - wxGridDataTypeInfo* info = new wxGridDataTypeInfo(typeName, renderer, editor); - - // is it already registered? - int loc = FindRegisteredDataType(typeName); - if ( loc != wxNOT_FOUND ) - { - delete m_typeinfo[loc]; - m_typeinfo[loc] = info; - } - else - { - m_typeinfo.Add(info); - } -} - -int wxGridTypeRegistry::FindRegisteredDataType(const wxString& typeName) -{ - size_t count = m_typeinfo.GetCount(); - for ( size_t i = 0; i < count; i++ ) - { - if ( typeName == m_typeinfo[i]->m_typeName ) - { - return i; - } - } - - return wxNOT_FOUND; -} - -int wxGridTypeRegistry::FindDataType(const wxString& typeName) -{ - int index = FindRegisteredDataType(typeName); - if ( index == wxNOT_FOUND ) - { - // check whether this is one of the standard ones, in which case - // register it "on the fly" -#if wxUSE_TEXTCTRL - if ( typeName == wxGRID_VALUE_STRING ) - { - RegisterDataType(wxGRID_VALUE_STRING, - new wxGridCellStringRenderer, - new wxGridCellTextEditor); - } - else -#endif // wxUSE_TEXTCTRL -#if wxUSE_CHECKBOX - if ( typeName == wxGRID_VALUE_BOOL ) - { - RegisterDataType(wxGRID_VALUE_BOOL, - new wxGridCellBoolRenderer, - new wxGridCellBoolEditor); - } - else -#endif // wxUSE_CHECKBOX -#if wxUSE_TEXTCTRL - if ( typeName == wxGRID_VALUE_NUMBER ) - { - RegisterDataType(wxGRID_VALUE_NUMBER, - new wxGridCellNumberRenderer, - new wxGridCellNumberEditor); - } - else if ( typeName == wxGRID_VALUE_FLOAT ) - { - RegisterDataType(wxGRID_VALUE_FLOAT, - new wxGridCellFloatRenderer, - new wxGridCellFloatEditor); - } - else -#endif // wxUSE_TEXTCTRL -#if wxUSE_COMBOBOX - if ( typeName == wxGRID_VALUE_CHOICE ) - { - RegisterDataType(wxGRID_VALUE_CHOICE, - new wxGridCellStringRenderer, - new wxGridCellChoiceEditor); - } - else -#endif // wxUSE_COMBOBOX - { - return wxNOT_FOUND; - } - - // we get here only if just added the entry for this type, so return - // the last index - index = m_typeinfo.GetCount() - 1; - } - - return index; -} - -int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName) +const wxGridColumnHeaderRenderer& +wxGridCellAttrProvider::GetColumnHeaderRenderer(int WXUNUSED(col)) { - int index = FindDataType(typeName); - if ( index == wxNOT_FOUND ) - { - // the first part of the typename is the "real" type, anything after ':' - // are the parameters for the renderer - index = FindDataType(typeName.BeforeFirst(_T(':'))); - if ( index == wxNOT_FOUND ) - { - return wxNOT_FOUND; - } - - wxGridCellRenderer *renderer = GetRenderer(index); - wxGridCellRenderer *rendererOld = renderer; - renderer = renderer->Clone(); - rendererOld->DecRef(); - - wxGridCellEditor *editor = GetEditor(index); - wxGridCellEditor *editorOld = editor; - editor = editor->Clone(); - editorOld->DecRef(); - - // do it even if there are no parameters to reset them to defaults - wxString params = typeName.AfterFirst(_T(':')); - renderer->SetParameters(params); - editor->SetParameters(params); - - // register the new typename - RegisterDataType(typeName, renderer, editor); - - // we just registered it, it's the last one - index = m_typeinfo.GetCount() - 1; - } - - return index; + return gs_defaultHeaderRenderers.colRenderer; } -wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index) +const wxGridRowHeaderRenderer& +wxGridCellAttrProvider::GetRowHeaderRenderer(int WXUNUSED(row)) { - wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer; - if (renderer) - renderer->IncRef(); - - return renderer; + return gs_defaultHeaderRenderers.rowRenderer; } -wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) +const wxGridCornerHeaderRenderer& wxGridCellAttrProvider::GetCornerRenderer() { - wxGridCellEditor* editor = m_typeinfo[index]->m_editor; - if (editor) - editor->IncRef(); - - return editor; + return gs_defaultHeaderRenderers.cornerRenderer; } // ---------------------------------------------------------------------------- @@ -3801,7 +1160,7 @@ wxString wxGridTableBase::GetColLabelValue( int col ) unsigned int i, n; for ( n = 1; ; n++ ) { - s += (wxChar) (_T('A') + (wxChar)(col % 26)); + s += (wxChar) (wxT('A') + (wxChar)(col % 26)); col = col / 26 - 1; if ( col < 0 ) break; @@ -3911,11 +1270,14 @@ IMPLEMENT_DYNAMIC_CLASS( wxGridStringTable, wxGridTableBase ) wxGridStringTable::wxGridStringTable() : wxGridTableBase() { + m_numCols = 0; } wxGridStringTable::wxGridStringTable( int numRows, int numCols ) : wxGridTableBase() { + m_numCols = numCols; + m_data.Alloc( numRows ); wxArrayString sa; @@ -3925,36 +1287,21 @@ wxGridStringTable::wxGridStringTable( int numRows, int numCols ) m_data.Add( sa, numRows ); } -wxGridStringTable::~wxGridStringTable() -{ -} - -int wxGridStringTable::GetNumberRows() -{ - return m_data.GetCount(); -} - -int wxGridStringTable::GetNumberCols() -{ - if ( m_data.GetCount() > 0 ) - return m_data[0].GetCount(); - else - return 0; -} - wxString wxGridStringTable::GetValue( int row, int col ) { - wxCHECK_MSG( (row < GetNumberRows()) && (col < GetNumberCols()), + wxCHECK_MSG( (row >= 0 && row < GetNumberRows()) && + (col >= 0 && col < GetNumberCols()), wxEmptyString, - _T("invalid row or column index in wxGridStringTable") ); + wxT("invalid row or column index in wxGridStringTable") ); return m_data[row][col]; } void wxGridStringTable::SetValue( int row, int col, const wxString& value ) { - wxCHECK_RET( (row < GetNumberRows()) && (col < GetNumberCols()), - _T("invalid row or column index in wxGridStringTable") ); + wxCHECK_RET( (row >= 0 && row < GetNumberRows()) && + (col >= 0 && col < GetNumberCols()), + wxT("invalid row or column index in wxGridStringTable") ); m_data[row][col] = value; } @@ -4111,6 +1458,8 @@ bool wxGridStringTable::InsertCols( size_t pos, size_t numCols ) } } + m_numCols += numCols; + if ( GetView() ) { wxGridTableMessage msg( this, @@ -4135,6 +1484,8 @@ bool wxGridStringTable::AppendCols( size_t numCols ) m_data[row].Add( wxEmptyString, numCols ); } + m_numCols += numCols; + if ( GetView() ) { wxGridTableMessage msg( this, @@ -4183,21 +1534,27 @@ bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols ) // m_colLabels stores just as many elements as it needs, e.g. if only // the label of the first column had been set it would have only one // element and not numCols, so account for it - int nToRm = m_colLabels.size() - colID; - if ( nToRm > 0 ) - m_colLabels.RemoveAt( colID, nToRm ); + int numRemaining = m_colLabels.size() - colID; + m_colLabels.RemoveAt( colID, wxMin(numCols, numRemaining) ); } - for ( row = 0; row < curNumRows; row++ ) + if ( numCols >= curNumCols ) { - if ( numCols >= curNumCols ) + for ( row = 0; row < curNumRows; row++ ) { m_data[row].Clear(); } - else + + m_numCols = 0; + } + else // something will be left + { + for ( row = 0; row < curNumRows; row++ ) { m_data[row].RemoveAt( colID, numCols ); } + + m_numCols -= numCols; } if ( GetView() ) @@ -4498,58 +1855,6 @@ void wxGridWindow::OnFocus(wxFocusEvent& event) ///////////////////////////////////////////////////////////////////// -#if wxUSE_EXTENDED_RTTI -WX_DEFINE_FLAGS( wxGridStyle ) - -wxBEGIN_FLAGS( wxGridStyle ) - // new style border flags, we put them first to - // use them for streaming out - wxFLAGS_MEMBER(wxBORDER_SIMPLE) - wxFLAGS_MEMBER(wxBORDER_SUNKEN) - wxFLAGS_MEMBER(wxBORDER_DOUBLE) - wxFLAGS_MEMBER(wxBORDER_RAISED) - wxFLAGS_MEMBER(wxBORDER_STATIC) - wxFLAGS_MEMBER(wxBORDER_NONE) - - // old style border flags - wxFLAGS_MEMBER(wxSIMPLE_BORDER) - wxFLAGS_MEMBER(wxSUNKEN_BORDER) - wxFLAGS_MEMBER(wxDOUBLE_BORDER) - wxFLAGS_MEMBER(wxRAISED_BORDER) - wxFLAGS_MEMBER(wxSTATIC_BORDER) - wxFLAGS_MEMBER(wxBORDER) - - // standard window styles - wxFLAGS_MEMBER(wxTAB_TRAVERSAL) - wxFLAGS_MEMBER(wxCLIP_CHILDREN) - wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) - wxFLAGS_MEMBER(wxWANTS_CHARS) - wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) - wxFLAGS_MEMBER(wxALWAYS_SHOW_SB) - wxFLAGS_MEMBER(wxVSCROLL) - wxFLAGS_MEMBER(wxHSCROLL) - -wxEND_FLAGS( wxGridStyle ) - -IMPLEMENT_DYNAMIC_CLASS_XTI(wxGrid, wxScrolledWindow,"wx/grid.h") - -wxBEGIN_PROPERTIES_TABLE(wxGrid) - wxHIDE_PROPERTY( Children ) - wxPROPERTY_FLAGS( WindowStyle , wxGridStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style -wxEND_PROPERTIES_TABLE() - -wxBEGIN_HANDLERS_TABLE(wxGrid) -wxEND_HANDLERS_TABLE() - -wxCONSTRUCTOR_5( wxGrid , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle ) - -/* - TODO : Expose more information of a list's layout, etc. via appropriate objects (e.g., NotebookPageInfo) -*/ -#else -IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow ) -#endif - BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow ) EVT_PAINT( wxGrid::OnPaint ) EVT_SIZE( wxGrid::OnSize ) @@ -4572,7 +1877,6 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, Create(); SetInitialSize(size); - SetScrollRate(m_scrollLineX, m_scrollLineY); CalcDimensions(); return true; @@ -4595,7 +1899,7 @@ wxGrid::~wxGrid() #ifdef DEBUG_ATTR_CACHE size_t total = gs_nAttrCacheHits + gs_nAttrCacheMisses; - wxPrintf(_T("wxGrid attribute cache statistics: " + wxPrintf(wxT("wxGrid attribute cache statistics: " "total: %u, hits: %u (%u%%)\n"), total, gs_nAttrCacheHits, total ? (gs_nAttrCacheHits*100) / total : 0); @@ -4610,6 +1914,9 @@ wxGrid::~wxGrid() delete m_typeRegistry; delete m_selection; + + delete m_setFixedRows; + delete m_setFixedCols; } // @@ -4758,8 +2065,7 @@ wxGrid::SetTable(wxGridTableBase *table, m_table = NULL; } - delete m_selection; - m_selection = NULL; + wxDELETE(m_selection); m_ownTable = false; m_numRows = 0; @@ -4779,7 +2085,7 @@ wxGrid::SetTable(wxGridTableBase *table, m_numCols = table->GetNumberCols(); if ( m_useNativeHeader ) - GetColHeader()->SetColumnCount(m_numCols); + GetGridColHeader()->SetColumnCount(m_numCols); m_table = table; m_table->SetView( this ); @@ -4835,6 +2141,9 @@ void wxGrid::Init() m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; + m_setFixedRows = + m_setFixedCols = NULL; + // init attr cache m_attrCache.row = -1; m_attrCache.col = -1; @@ -4877,6 +2186,9 @@ void wxGrid::Init() m_isDragging = false; m_startDragPos = wxDefaultPosition; + m_sortCol = wxNOT_FOUND; + m_sortIsAscending = true; + m_useNativeHeader = m_nativeColumnLabels = false; @@ -4902,8 +2214,11 @@ void wxGrid::Init() m_extraWidth = m_extraHeight = 0; - m_scrollLineX = GRID_SCROLL_LINE_X; - m_scrollLineY = GRID_SCROLL_LINE_Y; + // we can't call SetScrollRate() as the window isn't created yet but OTOH + // we don't need to call it neither as the scroll position is (0, 0) right + // now anyhow, so just set the parameters directly + m_xScrollPixelsPerLine = GRID_SCROLL_LINE_X; + m_yScrollPixelsPerLine = GRID_SCROLL_LINE_Y; } // ---------------------------------------------------------------------------- @@ -5098,20 +2413,6 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) // cell it might want to save that stuff to might no longer exist. HideCellEditControl(); -#if 0 - // if we were using the default widths/heights so far, we must change them - // now - if ( m_colWidths.IsEmpty() ) - { - InitColWidths(); - } - - if ( m_rowHeights.IsEmpty() ) - { - InitRowHeights(); - } -#endif - switch ( msg.GetId() ) { case wxGRIDTABLE_NOTIFY_ROWS_INSERTED: @@ -5263,7 +2564,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) m_numCols += numCols; if ( m_useNativeHeader ) - GetColHeader()->SetColumnCount(m_numCols); + GetGridColHeader()->SetColumnCount(m_numCols); if ( !m_colAt.IsEmpty() ) { @@ -5331,7 +2632,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) int oldNumCols = m_numCols; m_numCols += numCols; if ( m_useNativeHeader ) - GetColHeader()->SetColumnCount(m_numCols); + GetGridColHeader()->SetColumnCount(m_numCols); if ( !m_colAt.IsEmpty() ) { @@ -5386,7 +2687,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) int numCols = msg.GetCommandInt2(); m_numCols -= numCols; if ( m_useNativeHeader ) - GetColHeader()->SetColumnCount(m_numCols); + GetGridColHeader()->SetColumnCount(m_numCols); if ( !m_colAt.IsEmpty() ) { @@ -5601,9 +2902,8 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom ); // find the cells within these bounds - // - int row, col; - for ( row = internalYToRow(top); row < m_numRows; row++ ) + wxArrayInt cols; + for ( int row = internalYToRow(top); row < m_numRows; row++ ) { if ( GetRowBottom(row) <= top ) continue; @@ -5611,19 +2911,23 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const if ( GetRowTop(row) > bottom ) break; - int colPos; - for ( colPos = GetColPos( internalXToCol(left) ); colPos < m_numCols; colPos++ ) + // add all dirty cells in this row: notice that the columns which + // are dirty don't depend on the row so we compute them only once + // for the first dirty row and then reuse for all the next ones + if ( cols.empty() ) { - col = GetColAt( colPos ); + // do determine the dirty columns + for ( int pos = XToPos(left); pos <= XToPos(right); pos++ ) + cols.push_back(GetColAt(pos)); - if ( GetColRight(col) <= left ) - continue; - - if ( GetColLeft(col) > right ) + // if there are no dirty columns at all, nothing to do + if ( cols.empty() ) break; - - cellsExposed.Add( wxGridCellCoords( row, col ) ); } + + const size_t count = cols.size(); + for ( size_t n = 0; n < count; n++ ) + cellsExposed.Add(wxGridCellCoords(row, cols[n])); } ++iter; @@ -5712,11 +3016,12 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // else if ( event.LeftDown() ) { - // don't send a label click event for a hit on the - // edge of the row label - this is probably the user - // wanting to resize the row - // - if ( YToEdgeOfRow(y) < 0 ) + row = YToEdgeOfRow(y); + if ( row != wxNOT_FOUND && CanDragRowSize(row) ) + { + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin); + } + else // not a request to start resizing { row = YToRow(y); if ( row >= 0 && @@ -5744,12 +3049,6 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin); } } - else - { - // starting to drag-resize a row - if ( CanDragRowSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin); - } } // ------------ Left double click @@ -5757,7 +3056,19 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) else if (event.LeftDClick() ) { row = YToEdgeOfRow(y); - if ( row < 0 ) + if ( row != wxNOT_FOUND && CanDragRowSize(row) ) + { + // adjust row height depending on label text + // + // TODO: generate RESIZING event, see #10754 + AutoSizeRowLabelSize( row ); + + SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, row, -1, event); + + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); + m_dragLastPos = -1; + } + else // not on row separator or it's not resizable { row = YToRow(y); if ( row >=0 && @@ -5766,14 +3077,6 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // no default action at the moment } } - else - { - // adjust row height depending on label text - AutoSizeRowLabelSize( row ); - - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); - m_dragLastPos = -1; - } } // ------------ Left button released @@ -5781,14 +3084,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) else if ( event.LeftUp() ) { if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) - { - DoEndDragResizeRow(); - - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); - } + DoEndDragResizeRow(event); ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin); m_dragLastPos = -1; @@ -5823,12 +3119,11 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) else if ( event.Moving() ) { m_dragRowOrCol = YToEdgeOfRow( y ); - if ( m_dragRowOrCol >= 0 ) + if ( m_dragRowOrCol != wxNOT_FOUND ) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - // don't capture the mouse yet - if ( CanDragRowSize() ) + if ( CanDragRowSize(m_dragRowOrCol) ) ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false); } } @@ -5839,6 +3134,60 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) } } +void wxGrid::UpdateColumnSortingIndicator(int col) +{ + wxCHECK_RET( col != wxNOT_FOUND, "invalid column index" ); + + if ( m_useNativeHeader ) + GetGridColHeader()->UpdateColumn(col); + else if ( m_nativeColumnLabels ) + m_colWindow->Refresh(); + //else: sorting indicator display not yet implemented in grid version +} + +void wxGrid::SetSortingColumn(int col, bool ascending) +{ + if ( col == m_sortCol ) + { + // we are already using this column for sorting (or not sorting at all) + // but we might still change the sorting order, check for it + if ( m_sortCol != wxNOT_FOUND && ascending != m_sortIsAscending ) + { + m_sortIsAscending = ascending; + + UpdateColumnSortingIndicator(m_sortCol); + } + } + else // we're changing the column used for sorting + { + const int sortColOld = m_sortCol; + + // change it before updating the column as we want GetSortingColumn() + // to return the correct new value + m_sortCol = col; + + if ( sortColOld != wxNOT_FOUND ) + UpdateColumnSortingIndicator(sortColOld); + + if ( m_sortCol != wxNOT_FOUND ) + { + m_sortIsAscending = ascending; + UpdateColumnSortingIndicator(m_sortCol); + } + } +} + +void wxGrid::DoColHeaderClick(int col) +{ + // we consider that the grid was resorted if this event is processed and + // not vetoed + if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == 1 ) + { + SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true); + Refresh(); + } +} + void wxGrid::DoStartResizeCol(int col) { m_dragRowOrCol = col; @@ -5872,10 +3221,11 @@ void wxGrid::DoUpdateResizeColWidth(int w) void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { - int x, y, col; + int x, y; wxPoint pos( event.GetPosition() ); CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); + int col = XToCol(x); if ( event.Dragging() ) { if (!m_isDragging) @@ -5883,8 +3233,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) m_isDragging = true; GetColLabelWindow()->CaptureMouse(); - if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL ) - m_dragRowOrCol = XToCol( x ); + if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL && col != -1 ) + DoStartMoveCol(col); } if ( event.LeftIsDown() ) @@ -5897,7 +3247,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) case WXGRID_CURSOR_SELECT_COL: { - if ( (col = XToCol( x )) >= 0 ) + if ( col != -1 ) { if ( m_selection ) m_selection->SelectCol(col, event); @@ -5907,25 +3257,15 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) case WXGRID_CURSOR_MOVE_COL: { - if ( x < 0 ) - m_moveToCol = GetColAt( 0 ); - else - m_moveToCol = XToCol( x ); + int posNew = XToPos(x); + int colNew = GetColAt(posNew); + // determine the position of the drop marker int markerX; - - if ( m_moveToCol < 0 ) - markerX = GetColRight( GetColAt( m_numCols - 1 ) ); - else if ( x >= (GetColLeft( m_moveToCol ) + (GetColWidth(m_moveToCol) / 2)) ) - { - m_moveToCol = GetColAt( GetColPos( m_moveToCol ) + 1 ); - if ( m_moveToCol < 0 ) - markerX = GetColRight( GetColAt( m_numCols - 1 ) ); - else - markerX = GetColLeft( m_moveToCol ); - } + if ( x >= GetColLeft(colNew) + (GetColWidth(colNew) / 2) ) + markerX = GetColRight(colNew); else - markerX = GetColLeft( m_moveToCol ); + markerX = GetColLeft(colNew); if ( markerX != m_dragLastPos ) { @@ -5951,9 +3291,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) const wxColour *color; //Moving to the same place? Don't draw a marker - if ( (m_moveToCol == m_dragRowOrCol) - || (GetColPos( m_moveToCol ) == GetColPos( m_dragRowOrCol ) + 1) - || (m_moveToCol < 0 && m_dragRowOrCol == GetColAt( m_numCols - 1 ))) + if ( colNew == m_dragRowOrCol ) color = wxLIGHT_GREY; else color = wxBLUE; @@ -6001,11 +3339,12 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // else if ( event.LeftDown() ) { - // don't send a label click event for a hit on the - // edge of the col label - this is probably the user - // wanting to resize the col - // - if ( XToEdgeOfCol(x) < 0 ) + int col = XToEdgeOfCol(x); + if ( col != wxNOT_FOUND && CanDragColSize(col) ) + { + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow()); + } + else // not a request to start resizing { col = XToCol(x); if ( col >= 0 && @@ -6048,23 +3387,15 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) } } } - else - { - // starting to drag-resize a col - // - if ( CanDragColSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow()); - } } // ------------ Left double click // if ( event.LeftDClick() ) { - col = XToEdgeOfCol(x); - if ( col < 0 ) + const int colEdge = XToEdgeOfCol(x); + if ( colEdge == -1 ) { - col = XToCol(x); if ( col >= 0 && ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) ) { @@ -6074,7 +3405,11 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) else { // adjust column width depending on label text - AutoSizeColLabelSize( col ); + // + // TODO: generate RESIZING event, see #10754 + AutoSizeColLabelSize( colEdge ); + + SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, colEdge, event); ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; @@ -6088,20 +3423,56 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) switch ( m_cursorMode ) { case WXGRID_CURSOR_RESIZE_COL: - DoEndDragResizeCol(); + DoEndDragResizeCol(event); break; case WXGRID_CURSOR_MOVE_COL: - DoEndDragMoveCol(); + if ( m_dragLastPos == -1 || col == m_dragRowOrCol ) + { + // the column didn't actually move anywhere + if ( col != -1 ) + DoColHeaderClick(col); + m_colWindow->Refresh(); // "unpress" the column + } + else + { + // get the position of the column we're over + int pos = XToPos(x); + + // we may need to adjust the drop position but don't bother + // checking for it if we can't anyhow + if ( pos > 1 ) + { + // also find the index of the column we're over: notice + // that the existing "col" variable may be invalid but + // we need a valid one here + const int colValid = GetColAt(pos); + + // if we're on the "near" (usually left but right in + // RTL case) part of the column, the actual position we + // should be placed in is actually the one before it + bool onNearPart; + const int middle = GetColLeft(colValid) + + GetColWidth(colValid)/2; + if ( GetLayoutDirection() == wxLayout_LeftToRight ) + onNearPart = (x <= middle); + else // wxLayout_RightToLeft + onNearPart = (x > middle); + + if ( onNearPart ) + pos--; + } - SendEvent( wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol, event ); + DoEndMoveCol(pos); + } break; case WXGRID_CURSOR_SELECT_COL: case WXGRID_CURSOR_SELECT_CELL: case WXGRID_CURSOR_RESIZE_ROW: case WXGRID_CURSOR_SELECT_ROW: - // nothing to do (?) + if ( col != -1 ) + DoColHeaderClick(col); break; } @@ -6113,7 +3484,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDown() ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) ) { @@ -6125,7 +3495,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDClick() ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) ) { @@ -6142,8 +3511,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - // don't capture the cursor yet - if ( CanDragColSize() ) + if ( CanDragColSize(m_dragRowOrCol) ) ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow(), false); } } @@ -6207,24 +3575,24 @@ void wxGrid::ChangeCursorMode(CursorMode mode, wxWindow *win, bool captureMouse) { -#ifdef __WXDEBUG__ - static const wxChar *cursorModes[] = +#if wxUSE_LOG_TRACE + static const wxChar *const cursorModes[] = { - _T("SELECT_CELL"), - _T("RESIZE_ROW"), - _T("RESIZE_COL"), - _T("SELECT_ROW"), - _T("SELECT_COL"), - _T("MOVE_COL"), + wxT("SELECT_CELL"), + wxT("RESIZE_ROW"), + wxT("RESIZE_COL"), + wxT("SELECT_ROW"), + wxT("SELECT_COL"), + wxT("MOVE_COL"), }; - wxLogTrace(_T("grid"), - _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"), - win == m_colWindow ? _T("colLabelWin") - : win ? _T("rowLabelWin") - : _T("gridWin"), + wxLogTrace(wxT("grid"), + wxT("wxGrid cursor mode (mouse capture for %s): %s -> %s"), + win == m_colWindow ? wxT("colLabelWin") + : win ? wxT("rowLabelWin") + : wxT("gridWin"), cursorModes[m_cursorMode], cursorModes[mode]); -#endif +#endif // wxUSE_LOG_TRACE if ( mode == m_cursorMode && win == m_winCapture && @@ -6391,6 +3759,8 @@ void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords if ( isFirstDrag ) { + wxASSERT_MSG( !m_winCapture, "shouldn't capture the mouse twice" ); + m_winCapture = m_gridWin; m_winCapture->CaptureMouse(); } @@ -6436,6 +3806,29 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event, } else { + if ( m_selection ) + { + // In row or column selection mode just clicking on the cell + // should select the row or column containing it: this is more + // convenient for the kinds of controls that use such selection + // mode and is compatible with 2.8 behaviour (see #12062). + switch ( m_selection->GetSelectionMode() ) + { + case wxGridSelectCells: + case wxGridSelectRowsOrColumns: + // nothing to do in these cases + break; + + case wxGridSelectRows: + m_selection->SelectRow(coords.GetRow()); + break; + + case wxGridSelectColumns: + m_selection->SelectCol(coords.GetCol()); + break; + } + } + m_waitForSlowClick = m_currentCellCoords == coords && coords != wxGridNoCellCoords; SetCurrentCell( coords ); @@ -6504,17 +3897,12 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) { ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeRow(); - - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); + DoEndDragResizeRow(event); } else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) { ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeCol(); + DoEndDragResizeCol(event); } m_dragLastPos = -1; @@ -6544,27 +3932,24 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), return; } - if ( dragRow >= 0 ) + if ( dragRow >= 0 && CanDragGridSize() && CanDragRowSize(dragRow) ) { - m_dragRowOrCol = dragRow; - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - if ( CanDragRowSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); + m_dragRowOrCol = dragRow; + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); } } // When using the native header window we can only resize the columns by // dragging the dividers in it because we can't make it enter into the // column resizing mode programmatically - else if ( dragCol >= 0 && !m_useNativeHeader ) + else if ( dragCol >= 0 && !m_useNativeHeader && + CanDragGridSize() && CanDragColSize(dragCol) ) { - m_dragRowOrCol = dragCol; - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - if ( CanDragColSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); + m_dragRowOrCol = dragCol; + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); } } else // Neither on a row or col edge @@ -6578,6 +3963,14 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) { + if ( event.Entering() || event.Leaving() ) + { + // we don't care about these events but we must not reset m_isDragging + // if they happen so return before anything else is done + event.Skip(); + return; + } + const wxPoint pos = CalcUnscrolledPosition(event.GetPosition()); // coordinates of the cell under mouse @@ -6603,17 +3996,6 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) m_isDragging = false; m_startDragPos = wxDefaultPosition; - // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL - // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under - // wxGTK -#if 0 - if ( event.Entering() || event.Leaving() ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - m_gridWin->SetCursor( *wxSTANDARD_CURSOR ); - } -#endif // 0 - // deal with various button presses if ( event.IsButton() ) { @@ -6647,10 +4029,11 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) } } -void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) +// this function returns true only if the size really changed +bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) { if ( m_dragLastPos == -1 ) - return; + return false; const wxGridOperations& doper = oper.Dual(); @@ -6674,9 +4057,12 @@ void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) // do resize the line const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol); + const int lineSizeOld = oper.GetLineSize(this, m_dragRowOrCol); oper.SetLineSize(this, m_dragRowOrCol, wxMax(m_dragLastPos - lineStart, oper.GetMinimalLineSize(this, m_dragRowOrCol))); + const bool + sizeChanged = oper.GetLineSize(this, m_dragRowOrCol) != lineSizeOld; m_dragLastPos = -1; @@ -6710,13 +4096,13 @@ void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) oper.SelectSize(rect) = oper.Select(size); int subtractLines = 0; - const int lineStart = oper.PosToLine(this, posLineStart); + const int lineStart = doper.PosToLine(this, posLineStart); if ( lineStart >= 0 ) { // ensure that if we have a multi-cell block we redraw all of // it by increasing the refresh area to cover it entirely if a // part of it is affected - const int lineEnd = oper.PosToLine(this, posLineEnd, true); + const int lineEnd = doper.PosToLine(this, posLineEnd, true); for ( int line = lineStart; line < lineEnd; line++ ) { int cellLines = oper.Select( @@ -6739,129 +4125,120 @@ void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) // show the edit control back again ShowCellEditControl(); + + return sizeChanged; } -void wxGrid::DoEndDragResizeRow() +void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event) { - DoEndDragResizeLine(wxGridRowOperations()); + // TODO: generate RESIZING event, see #10754 + + if ( DoEndDragResizeLine(wxGridRowOperations()) ) + SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event); } -void wxGrid::DoEndDragResizeCol(wxMouseEvent *event) +void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event) { - DoEndDragResizeLine(wxGridColumnOperations()); + // TODO: generate RESIZING event, see #10754 - // Note: we are ending the event *after* doing - // default processing in this case - // - if ( event ) - SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, *event ); - else - SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol ); + if ( DoEndDragResizeLine(wxGridColumnOperations()) ) + SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event); } -void wxGrid::DoEndDragMoveCol() +void wxGrid::DoStartMoveCol(int col) { - //The user clicked on the column but didn't actually drag - if ( m_dragLastPos < 0 ) - { - m_colWindow->Refresh(); //Do this to "unpress" the column - return; - } + m_dragRowOrCol = col; +} - int newPos; - if ( m_moveToCol == -1 ) - newPos = m_numCols - 1; - else - { - newPos = GetColPos( m_moveToCol ); - if ( newPos > GetColPos( m_dragRowOrCol ) ) - newPos--; - } +void wxGrid::DoEndMoveCol(int pos) +{ + wxASSERT_MSG( m_dragRowOrCol != -1, "no matching DoStartMoveCol?" ); - SetColPos( m_dragRowOrCol, newPos ); + if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol) != -1 ) + SetColPos(m_dragRowOrCol, pos); + //else: vetoed by user + + m_dragRowOrCol = -1; } -void wxGrid::SetColPos( int colID, int newPos ) +void wxGrid::RefreshAfterColPosChange() { - if ( m_colAt.IsEmpty() ) + // recalculate the column rights as the column positions have changed, + // unless we calculate them dynamically because all columns widths are the + // same and it's easy to do + if ( !m_colWidths.empty() ) { - m_colAt.Alloc( m_numCols ); - - int i; - for ( i = 0; i < m_numCols; i++ ) + int colRight = 0; + for ( int colPos = 0; colPos < m_numCols; colPos++ ) { - m_colAt.Add( i ); + int colID = GetColAt( colPos ); + + colRight += m_colWidths[colID]; + m_colRights[colID] = colRight; } } - int oldPos = GetColPos( colID ); - - //Reshuffle the m_colAt array - if ( newPos > oldPos ) + // and make the changes visible + if ( m_useNativeHeader ) { - int i; - for ( i = oldPos; i < newPos; i++ ) - { - m_colAt[i] = m_colAt[i+1]; - } + if ( m_colAt.empty() ) + GetGridColHeader()->ResetColumnsOrder(); + else + GetGridColHeader()->SetColumnsOrder(m_colAt); } else { - int i; - for ( i = oldPos; i > newPos; i-- ) - { - m_colAt[i] = m_colAt[i-1]; - } + m_colWindow->Refresh(); } + m_gridWin->Refresh(); +} - m_colAt[newPos] = colID; +void wxGrid::SetColumnsOrder(const wxArrayInt& order) +{ + m_colAt = order; - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) - { - int colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) - { - int colID = GetColAt( colPos ); + RefreshAfterColPosChange(); +} - colRight += m_colWidths[colID]; - m_colRights[colID] = colRight; - } +void wxGrid::SetColPos(int idx, int pos) +{ + // we're going to need m_colAt now, initialize it if needed + if ( m_colAt.empty() ) + { + m_colAt.reserve(m_numCols); + for ( int i = 0; i < m_numCols; i++ ) + m_colAt.push_back(i); } - m_colWindow->Refresh(); - m_gridWin->Refresh(); + wxHeaderCtrl::MoveColumnInOrderArray(m_colAt, idx, pos); + + RefreshAfterColPosChange(); } +void wxGrid::ResetColPos() +{ + m_colAt.clear(); + RefreshAfterColPosChange(); +} void wxGrid::EnableDragColMove( bool enable ) { if ( m_canDragColMove == enable ) return; - m_canDragColMove = enable; - - if ( !m_canDragColMove ) + if ( m_useNativeHeader ) { - m_colAt.Clear(); + // update all columns to make them [not] reorderable + GetGridColHeader()->SetColumnCount(m_numCols); + } - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) - { - int colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) - { - colRight += m_colWidths[colPos]; - m_colRights[colPos] = colRight; - } - } + m_canDragColMove = enable; - m_colWindow->Refresh(); - m_gridWin->Refresh(); - } + // we use to call ResetColPos() from here if !enable but this doesn't seem + // right as it would mean there would be no way to "freeze" the current + // columns order by disabling moving them after putting them in the desired + // order, whereas now you can always call ResetColPos() manually if needed } @@ -6939,9 +4316,27 @@ wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t), return (m_table->*funcAppend)(num); } -// -// ----- event handlers -// +// ---------------------------------------------------------------------------- +// event generation helpers +// ---------------------------------------------------------------------------- + +void +wxGrid::SendGridSizeEvent(wxEventType type, + int row, int col, + const wxMouseEvent& mouseEv) +{ + int rowOrCol = row == -1 ? col : row; + + wxGridSizeEvent gridEvt( GetId(), + type, + this, + rowOrCol, + mouseEv.GetX() + GetRowLabelSize(), + mouseEv.GetY() + GetColLabelSize(), + mouseEv); + + GetEventHandler()->ProcessEvent(gridEvt); +} // Generate a grid event based on a mouse event and return: // -1 if the event was vetoed @@ -6950,26 +4345,11 @@ wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t), int wxGrid::SendEvent(const wxEventType type, int row, int col, - wxMouseEvent& mouseEv) + const wxMouseEvent& mouseEv) { bool claimed, vetoed; - if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE ) - { - int rowOrCol = (row == -1 ? col : row); - - wxGridSizeEvent gridEvt( GetId(), - type, - this, - rowOrCol, - mouseEv.GetX() + GetRowLabelSize(), - mouseEv.GetY() + GetColLabelSize(), - mouseEv); - - claimed = GetEventHandler()->ProcessEvent(gridEvt); - vetoed = !gridEvt.IsAllowed(); - } - else if ( type == wxEVT_GRID_RANGE_SELECT ) + if ( type == wxEVT_GRID_RANGE_SELECT ) { // Right now, it should _never_ end up here! wxGridRangeSelectEvent gridEvt( GetId(), @@ -7029,29 +4409,16 @@ wxGrid::SendEvent(const wxEventType type, // Generate a grid event of specified type, return value same as above // -int wxGrid::SendEvent(const wxEventType type, int row, int col) +int +wxGrid::SendEvent(const wxEventType type, int row, int col, const wxString& s) { - bool claimed, vetoed; + wxGridEvent gridEvt( GetId(), type, this, row, col ); + gridEvt.SetString(s); - if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE ) - { - int rowOrCol = (row == -1 ? col : row); - - wxGridSizeEvent gridEvt( GetId(), type, this, rowOrCol ); - - claimed = GetEventHandler()->ProcessEvent(gridEvt); - vetoed = !gridEvt.IsAllowed(); - } - else - { - wxGridEvent gridEvt( GetId(), type, this, row, col ); - - claimed = GetEventHandler()->ProcessEvent(gridEvt); - vetoed = !gridEvt.IsAllowed(); - } + const bool claimed = GetEventHandler()->ProcessEvent(gridEvt); // A Veto'd event may not be `claimed' so test this first - if (vetoed) + if ( !gridEvt.IsAllowed() ) return -1; return claimed ? 1 : 0; @@ -7270,25 +4637,15 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) break; case WXK_HOME: - if ( event.ControlDown() ) - { - GoToCell(0, 0); - } - else - { - event.Skip(); - } + GoToCell(event.ControlDown() ? 0 + : m_currentCellCoords.GetRow(), + 0); break; case WXK_END: - if ( event.ControlDown() ) - { - GoToCell(m_numRows - 1, m_numCols - 1); - } - else - { - event.Skip(); - } + GoToCell(event.ControlDown() ? m_numRows - 1 + : m_currentCellCoords.GetRow(), + m_numCols - 1); break; case WXK_PAGEUP: @@ -7466,6 +4823,9 @@ void wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol, int bottomRow, int rightCol) { + MakeCellVisible(m_selectedBlockCorner); + m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol); + if ( m_selection ) { switch ( m_selection->GetSelectionMode() ) @@ -7502,9 +4862,6 @@ wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol, } } - m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol); - MakeCellVisible(m_selectedBlockCorner); - EnsureFirstLessThanSecond(topRow, bottomRow); EnsureFirstLessThanSecond(leftCol, rightCol); @@ -8099,32 +5456,25 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 ) return; - wxRect rect; - - int rowTop = GetRowTop(row), - rowBottom = GetRowBottom(row) - 1; - - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); - dc.DrawLine( m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom ); - dc.DrawLine( 0, rowTop, 0, rowBottom ); - dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom ); + wxGridCellAttrProvider * const + attrProvider = m_table ? m_table->GetAttrProvider() : NULL; - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( 1, rowTop, 1, rowBottom ); - dc.DrawLine( 1, rowTop, m_rowLabelWidth - 1, rowTop ); + // notice that an explicit static_cast is needed to avoid a compilation + // error with VC7.1 which, for some reason, tries to instantiate (abstract) + // wxGridRowHeaderRenderer class without it + const wxGridRowHeaderRenderer& + rend = attrProvider ? attrProvider->GetRowHeaderRenderer(row) + : static_cast + (gs_defaultHeaderRenderers.rowRenderer); - dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); - dc.SetTextForeground( GetLabelTextColour() ); - dc.SetFont( GetLabelFont() ); + wxRect rect(0, GetRowTop(row), m_rowLabelWidth, GetRowHeight(row)); + rend.DrawBorder(*this, dc, rect); int hAlign, vAlign; - GetRowLabelAlignment( &hAlign, &vAlign ); + GetRowLabelAlignment(&hAlign, &vAlign); - rect.SetX( 2 ); - rect.SetY( GetRowTop(row) + 2 ); - rect.SetWidth( m_rowLabelWidth - 4 ); - rect.SetHeight( GetRowHeight(row) - 4 ); - DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign ); + rend.DrawLabel(*this, dc, GetRowLabelValue(row), + rect, hAlign, vAlign, wxHORIZONTAL); } void wxGrid::UseNativeColHeader(bool native) @@ -8138,7 +5488,7 @@ void wxGrid::UseNativeColHeader(bool native) CreateColumnWindow(); if ( m_useNativeHeader ) - GetColHeader()->SetColumnCount(m_numCols); + GetGridColHeader()->SetColumnCount(m_numCols); CalcWindowSizes(); } @@ -8172,26 +5522,27 @@ void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols ) void wxGrid::DrawCornerLabel(wxDC& dc) { + wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight)); + if ( m_nativeColumnLabels ) { - wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight)); rect.Deflate(1); wxRendererNative::Get().DrawHeaderButton(m_cornerLabelWin, dc, rect, 0); } else { - dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); - dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1, - m_rowLabelWidth - 1, 0 ); - dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1, - 0, m_colLabelHeight - 1 ); - dc.DrawLine( 0, 0, m_rowLabelWidth, 0 ); - dc.DrawLine( 0, 0, 0, m_colLabelHeight ); + rect.width++; + rect.height++; - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( 1, 1, m_rowLabelWidth - 1, 1 ); - dc.DrawLine( 1, 1, 1, m_colLabelHeight - 1 ); + wxGridCellAttrProvider * const + attrProvider = m_table ? m_table->GetAttrProvider() : NULL; + const wxGridCornerHeaderRenderer& + rend = attrProvider ? attrProvider->GetCornerRenderer() + : static_cast + (gs_defaultHeaderRenderers.cornerRenderer); + + rend.DrawBorder(*this, dc, rect); } } @@ -8203,38 +5554,44 @@ void wxGrid::DrawColLabel(wxDC& dc, int col) int colLeft = GetColLeft(col); wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight); + wxGridCellAttrProvider * const + attrProvider = m_table ? m_table->GetAttrProvider() : NULL; + const wxGridColumnHeaderRenderer& + rend = attrProvider ? attrProvider->GetColumnHeaderRenderer(col) + : static_cast + (gs_defaultHeaderRenderers.colRenderer); if ( m_nativeColumnLabels ) { - wxRendererNative::Get().DrawHeaderButton(GetColLabelWindow(), dc, rect, 0); + wxRendererNative::Get().DrawHeaderButton + ( + GetColLabelWindow(), + dc, + rect, + 0, + IsSortingBy(col) + ? IsSortOrderAscending() + ? wxHDR_SORT_ICON_UP + : wxHDR_SORT_ICON_DOWN + : wxHDR_SORT_ICON_NONE + ); + rect.Deflate(2); } else { - int colRight = GetColRight(col) - 1; - - dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); - dc.DrawLine( colRight, 0, - colRight, m_colLabelHeight - 1 ); - dc.DrawLine( colLeft, 0, - colRight, 0 ); - dc.DrawLine( colLeft, m_colLabelHeight - 1, - colRight + 1, m_colLabelHeight - 1 ); + // It is reported that we need to erase the background to avoid display + // artefacts, see #12055. + wxDCBrushChanger setBrush(dc, m_colWindow->GetBackgroundColour()); + dc.DrawRectangle(rect); - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight - 1 ); - dc.DrawLine( colLeft, 1, colRight, 1 ); + rend.DrawBorder(*this, dc, rect); } - dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); - dc.SetTextForeground( GetLabelTextColour() ); - dc.SetFont( GetLabelFont() ); - int hAlign, vAlign; - GetColLabelAlignment( &hAlign, &vAlign ); + GetColLabelAlignment(&hAlign, &vAlign); const int orient = GetColLabelTextOrientation(); - rect.Deflate(2); - DrawTextRectangle(dc, GetColLabelValue(col), rect, hAlign, vAlign, orient); + rend.DrawLabel(*this, dc, GetColLabelValue(col), rect, hAlign, vAlign, orient); } // TODO: these 2 functions should be replaced with wxDC::DrawLabel() to which @@ -8244,7 +5601,7 @@ void wxGrid::DrawTextRectangle( wxDC& dc, const wxRect& rect, int horizAlign, int vertAlign, - int textOrientation ) + int textOrientation ) const { wxArrayString lines; @@ -8258,7 +5615,7 @@ void wxGrid::DrawTextRectangle(wxDC& dc, const wxRect& rect, int horizAlign, int vertAlign, - int textOrientation) + int textOrientation) const { if ( lines.empty() ) return; @@ -8477,7 +5834,7 @@ void wxGrid::EnableCellEditControl( bool enable ) return; // this should be checked by the caller! - wxASSERT_MSG( CanEnableCellControl(), _T("can't enable editing for this cell!") ); + wxASSERT_MSG( CanEnableCellControl(), wxT("can't enable editing for this cell!") ); // do it before ShowCellEditControl() m_cellEditCtrlEnabled = enable; @@ -8486,7 +5843,6 @@ void wxGrid::EnableCellEditControl( bool enable ) } else { - //FIXME:add veto support SendEvent(wxEVT_GRID_EDITOR_HIDDEN); HideCellEditControl(); @@ -8500,8 +5856,8 @@ void wxGrid::EnableCellEditControl( bool enable ) bool wxGrid::IsCurrentCellReadOnly() const { - // const_cast - wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords); + wxGridCellAttr* + attr = const_cast(this)->GetCellAttr(m_currentCellCoords); bool readonly = attr->IsReadOnly(); attr->DecRef(); @@ -8719,19 +6075,26 @@ void wxGrid::SaveEditControlValue() wxGridCellAttr* attr = GetCellAttr(row, col); wxGridCellEditor* editor = attr->GetEditor(this, row, col); - bool changed = editor->EndEdit(row, col, this); - editor->DecRef(); - attr->DecRef(); + wxString newval; + bool changed = editor->EndEdit(row, col, this, oldval, &newval); - if (changed) + if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 ) { - if ( SendEvent(wxEVT_GRID_CELL_CHANGE) == -1 ) + editor->ApplyEdit(row, col, this); + + // for compatibility reasons dating back to wx 2.8 when this event + // was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING + // didn't exist we allow vetoing this one too + if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 ) { // Event has been vetoed, set the data back. SetCellValue(row, col, oldval); } } + + editor->DecRef(); + attr->DecRef(); } } @@ -8755,15 +6118,14 @@ wxGridCellCoords wxGrid::XYToCell(int x, int y) const // m_defaultRowHeight/m_defaultColWidth or binary search on array of // m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used // for large grids) -int -wxGrid::PosToLine(int coord, - bool clipToMinMax, - const wxGridOperations& oper) const +int wxGrid::PosToLinePos(int coord, + bool clipToMinMax, + const wxGridOperations& oper) const { const int numLines = oper.GetNumberOfLines(this); if ( coord < 0 ) - return clipToMinMax && numLines > 0 ? oper.GetLineAt(this, 0) : -1; + return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND; const int defaultLineSize = oper.GetDefaultLineSize(this); wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" ); @@ -8807,12 +6169,12 @@ wxGrid::PosToLine(int coord, // check if the position is beyond the last column const int lineAtMaxPos = oper.GetLineAt(this, maxPos); if ( coord >= lineEnds[lineAtMaxPos] ) - return clipToMinMax ? lineAtMaxPos : -1; + return clipToMinMax ? maxPos : -1; // or before the first one const int lineAt0 = oper.GetLineAt(this, 0); if ( coord < lineEnds[lineAt0] ) - return lineAt0; + return 0; // finally do perform the binary search @@ -8821,10 +6183,10 @@ wxGrid::PosToLine(int coord, wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord && coord < lineEnds[oper.GetLineAt(this, maxPos)], -1, - "wxGrid: internal error in PosToLine()" ); + "wxGrid: internal error in PosToLinePos()" ); if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] ) - return oper.GetLineAt(this, maxPos); + return maxPos; else maxPos--; @@ -8835,7 +6197,17 @@ wxGrid::PosToLine(int coord, minPos = median; } - return oper.GetLineAt(this, maxPos); + return maxPos; +} + +int +wxGrid::PosToLine(int coord, + bool clipToMinMax, + const wxGridOperations& oper) const +{ + int pos = PosToLinePos(coord, clipToMinMax, oper); + + return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos); } int wxGrid::YToRow(int y, bool clipToMinMax) const @@ -8848,23 +6220,20 @@ int wxGrid::XToCol(int x, bool clipToMinMax) const return PosToLine(x, clipToMinMax, wxGridColumnOperations()); } -// return the row number that that the y coord is near the edge of, or -1 if +int wxGrid::XToPos(int x) const +{ + return PosToLinePos(x, true /* clip */, wxGridColumnOperations()); +} + +// return the row number such that the y coord is near the edge of, or -1 if // not near an edge. // -// coords can only possibly be near an edge if -// (a) the row/column is large enough to still allow for an "inner" area -// that is _not_ near the edge (i.e., if the height/width is smaller -// than WXGRID_LABEL_EDGE_ZONE, coords are _never_ considered to be -// near the edge). -// and -// (b) resizing rows/columns (the thing for which edge detection is -// relevant at all) is enabled. -// +// notice that position can only possibly be near an edge if the row/column is +// large enough to still allow for an "inner" area that is _not_ near the edge +// (i.e., if the height/width is smaller than WXGRID_LABEL_EDGE_ZONE, pos will +// _never_ be considered to be near the edge). int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const { - if ( !oper.CanResizeLines(this) ) - return -1; - const int line = oper.PosToLine(this, pos, true); if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE ) @@ -8876,7 +6245,9 @@ int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const else if ( line > 0 && pos - oper.GetLineStartPos(this, line) < WXGRID_LABEL_EDGE_ZONE ) - return line - 1; + { + return oper.GetLineBefore(this, line); + } } return -1; @@ -8915,13 +6286,13 @@ wxRect wxGrid::CellToRect( int row, int col ) const rect.width += GetColWidth(i); for (i=row; i < row + cell_rows; i++) rect.height += GetRowHeight(i); - } - // if grid lines are enabled, then the area of the cell is a bit smaller - if (m_gridLinesEnabled) - { - rect.width -= 1; - rect.height -= 1; + // if grid lines are enabled, then the area of the cell is a bit smaller + if (m_gridLinesEnabled) + { + rect.width -= 1; + rect.height -= 1; + } } return rect; @@ -9004,7 +6375,7 @@ void wxGrid::MakeCellVisible( int row, int col ) // // Sometimes GRID_SCROLL_LINE / 2 is not enough, // so just add a full scroll unit... - ypos += m_scrollLineY; + ypos += m_yScrollPixelsPerLine; } // special handling for wide cells - show always left part of the cell! @@ -9023,15 +6394,15 @@ void wxGrid::MakeCellVisible( int row, int col ) xpos = x0 + (right - cw); // see comment for ypos above - xpos += m_scrollLineX; + xpos += m_xScrollPixelsPerLine; } if ( xpos != -1 || ypos != -1 ) { if ( xpos != -1 ) - xpos /= m_scrollLineX; + xpos /= m_xScrollPixelsPerLine; if ( ypos != -1 ) - ypos /= m_scrollLineY; + ypos /= m_yScrollPixelsPerLine; Scroll( xpos, ypos ); AdjustScrollbars(); } @@ -9497,7 +6868,7 @@ void wxGrid::SetColLabelValue( int col, const wxString& s ) { if ( m_useNativeHeader ) { - GetColHeader()->UpdateColumn(col); + GetGridColHeader()->UpdateColumn(col); } else { @@ -9546,7 +6917,7 @@ void wxGrid::SetCellHighlightPenWidth(int width) m_cellHighlightPenWidth = width; // Just redrawing the cell highlight is not enough since that won't - // make any visible change if the the thickness is getting smaller. + // make any visible change if the thickness is getting smaller. int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); if ( row == -1 || col == -1 || GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) @@ -9564,7 +6935,7 @@ void wxGrid::SetCellHighlightROPenWidth(int width) m_cellHighlightROPenWidth = width; // Just redrawing the cell highlight is not enough since that won't - // make any visible change if the the thickness is getting smaller. + // make any visible change if the thickness is getting smaller. int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); if ( row == -1 || col == -1 || @@ -9622,7 +6993,7 @@ int wxGrid::GetDefaultRowSize() const int wxGrid::GetRowSize( int row ) const { - wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") ); + wxCHECK_MSG( row >= 0 && row < m_numRows, 0, wxT("invalid row index") ); return GetRowHeight(row); } @@ -9634,7 +7005,7 @@ int wxGrid::GetDefaultColSize() const int wxGrid::GetColSize( int col ) const { - wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") ); + wxCHECK_MSG( col >= 0 && col < m_numCols, 0, wxT("invalid column index") ); return GetColWidth(col); } @@ -9781,11 +7152,21 @@ bool wxGrid::GetCellOverflow( int row, int col ) const return allow; } -void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const +wxGrid::CellSpan +wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const { wxGridCellAttr *attr = GetCellAttr(row, col); attr->GetSize( num_rows, num_cols ); attr->DecRef(); + + if ( *num_rows == 1 && *num_cols == 1 ) + return CellSpan_None; // just a normal cell + + if ( *num_rows < 0 || *num_cols < 0 ) + return CellSpan_Inside; // covered by a multi-span cell + + // this cell spans multiple cells to its right/bottom + return CellSpan_Main; } wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) const @@ -9844,11 +7225,18 @@ void wxGrid::ClearAttrCache() } } +void wxGrid::RefreshAttr(int row, int col) +{ + if ( m_attrCache.row == row && m_attrCache.col == col ) + ClearAttrCache(); +} + + void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const { if ( attr != NULL ) { - wxGrid *self = (wxGrid *)this; // const_cast + wxGrid * const self = const_cast(this); self->ClearAttrCache(); self->m_attrCache.row = row; @@ -9914,8 +7302,8 @@ wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const wxGridCellAttr *attr = NULL; bool canHave = ((wxGrid*)this)->CanHaveAttributes(); - wxCHECK_MSG( canHave, attr, _T("Cell attributes not allowed")); - wxCHECK_MSG( m_table, attr, _T("must have a table") ); + wxCHECK_MSG( canHave, attr, wxT("Cell attributes not allowed")); + wxCHECK_MSG( m_table, attr, wxT("must have a table") ); attr = m_table->GetAttr(row, col, wxGridCellAttr::Cell); if ( !attr ) @@ -9949,7 +7337,7 @@ void wxGrid::SetColFormatFloat(int col, int width, int precision) wxString typeName = wxGRID_VALUE_FLOAT; if ( (width != -1) || (precision != -1) ) { - typeName << _T(':') << width << _T(',') << precision; + typeName << wxT(':') << width << wxT(',') << precision; } SetColFormatCustom(col, typeName); @@ -10205,6 +7593,22 @@ wxGridCellRenderer * wxGrid::GetDefaultRendererForType(const wxString& typeName) // row/col size // ---------------------------------------------------------------------------- +void wxGrid::DoDisableLineResize(int line, wxGridFixedIndicesSet *& setFixed) +{ + if ( !setFixed ) + { + setFixed = new wxGridFixedIndicesSet; + } + + setFixed->insert(line); +} + +bool +wxGrid::DoCanResizeLine(int line, const wxGridFixedIndicesSet *setFixed) const +{ + return !setFixed || !setFixed->count(line); +} + void wxGrid::EnableDragRowSize( bool enable ) { m_canDragRowSize = enable; @@ -10244,7 +7648,7 @@ void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows ) void wxGrid::SetRowSize( int row, int height ) { - wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") ); + wxCHECK_RET( row >= 0 && row < m_numRows, wxT("invalid row index") ); // if < 0 then calculate new height from label if ( height < 0 ) @@ -10302,7 +7706,7 @@ void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols ) void wxGrid::SetColSize( int col, int width ) { - wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") ); + wxCHECK_RET( col >= 0 && col < m_numCols, wxT("invalid column index") ); // if < 0 then calculate new width from label if ( width < 0 ) @@ -10321,15 +7725,12 @@ void wxGrid::SetColSize( int col, int width ) width = wxMax(width, GetColMinimalAcceptableWidth()); } - // should we check that it's bigger than GetColMinimalWidth(col) here? - // (VZ) - // No, because it is reasonable to assume the library user know's - // what he is doing. However we should test against the weaker - // constraint of minimalAcceptableWidth, as this breaks rendering - // - // This test then fixes sf.net bug #645734 - - if ( width < GetColMinimalAcceptableWidth() ) + // we intentionally don't test whether the width is less than + // GetColMinimalWidth() here but we do compare it with + // GetColMinimalAcceptableWidth() as otherwise things currently break (see + // #651) -- and we also always allow the width of 0 as it has the special + // sense of hiding the column + if ( width > 0 && width < GetColMinimalAcceptableWidth() ) return; if ( m_colWidths.IsEmpty() ) @@ -10338,9 +7739,11 @@ void wxGrid::SetColSize( int col, int width ) InitColWidths(); } - int w = wxMax( 0, width ); - int diff = w - m_colWidths[col]; - m_colWidths[col] = w; + const int diff = width - m_colWidths[col]; + m_colWidths[col] = width; + if ( m_useNativeHeader ) + GetGridColHeader()->UpdateColumn(col); + //else: will be refreshed when the header is redrawn for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ ) { @@ -10429,13 +7832,20 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) HideCellEditControl(); SaveEditControlValue(); - // init both of them to avoid compiler warnings, even if we only need one - int row = -1, - col = -1; + // initialize both of them just to avoid compiler warnings even if only + // really needs to be initialized here + int row, + col; if ( column ) + { + row = -1; col = colOrRow; + } else + { row = colOrRow; + col = -1; + } wxCoord extent, extentMax = 0; int max = column ? m_numRows : m_numCols; @@ -10446,12 +7856,41 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) else col = rowOrCol; + // we need to account for the cells spanning multiple columns/rows: + // while they may need a lot of space, they don't need all of it in + // this column/row + int numRows, numCols; + const CellSpan span = GetCellSize(row, col, &numRows, &numCols); + if ( span == CellSpan_Inside ) + { + // we need to get the size of the main cell, not of a cell hidden + // by it + row += numRows; + col += numCols; + + // get the size of the main cell too + GetCellSize(row, col, &numRows, &numCols); + } + wxGridCellAttr *attr = GetCellAttr(row, col); wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col); if ( renderer ) { wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col); extent = column ? size.x : size.y; + + if ( span != CellSpan_None ) + { + // we spread the size of a spanning cell over all the cells it + // covers evenly -- this is probably not ideal but we can't + // really do much better here + // + // notice that numCols and numRows are never 0 as they + // correspond to the size of the main cell of the span and not + // of the cell inside it + extent /= column ? numCols : numRows; + } + if ( extent > extentMax ) extentMax = extent; @@ -10506,7 +7945,7 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) { if ( m_useNativeHeader ) { - GetColHeader()->UpdateColumn(col); + GetGridColHeader()->UpdateColumn(col); } else { @@ -10653,10 +8092,8 @@ void wxGrid::AutoSize() // we know that we're not going to have scrollbars so disable them now to // avoid trouble in SetClientSize() which can otherwise set the correct // client size but also leave space for (not needed any more) scrollbars - SetScrollbars(0, 0, 0, 0, 0, 0, true); - - // restore the scroll rate parameters overwritten by SetScrollbars() - SetScrollRate(m_scrollLineX, m_scrollLineY); + SetScrollbars(m_xScrollPixelsPerLine, m_yScrollPixelsPerLine, + 0, 0, 0, 0, true); SetClientSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight); } @@ -10693,7 +8130,7 @@ void wxGrid::AutoSizeColLabelSize( int col ) wxSize wxGrid::DoGetBestSize() const { - wxGrid *self = (wxGrid *)this; // const_cast + wxGrid * const self = const_cast(this); // we do the same as in AutoSize() here with the exception that we don't // change the column/row sizes, only calculate them @@ -10808,7 +8245,8 @@ void wxGrid::DeselectLine(int line, const wxGridOperations& oper) return; const wxGridSelectionModes mode = m_selection->GetSelectionMode(); - if ( mode == oper.GetSelectionMode() ) + if ( mode == oper.GetSelectionMode() || + mode == wxGrid::wxGridSelectRowsOrColumns ) { const wxGridCellCoords c(oper.MakeCoords(line, 0)); if ( m_selection->IsInSelection(c) ) @@ -10926,8 +8364,10 @@ void wxGrid::ClearSelection() m_selectedBlockBottomRight = m_selectedBlockCorner = wxGridNoCellCoords; - Refresh( false, &r1 ); - Refresh( false, &r2 ); + if ( !r1.IsEmpty() ) + RefreshRect(r1, false); + if ( !r2.IsEmpty() ) + RefreshRect(r2, false); if ( m_selection ) m_selection->ClearSelection(); @@ -11059,6 +8499,48 @@ wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft, return resultRect; } +void wxGrid::DoSetSizes(const wxGridSizesInfo& sizeInfo, + const wxGridOperations& oper) +{ + BeginBatch(); + oper.SetDefaultLineSize(this, sizeInfo.m_sizeDefault, true); + const int numLines = oper.GetNumberOfLines(this); + for ( int i = 0; i < numLines; i++ ) + { + int size = sizeInfo.GetSize(i); + if ( size != sizeInfo.m_sizeDefault) + oper.SetLineSize(this, i, size); + } + EndBatch(); +} + +void wxGrid::SetColSizes(const wxGridSizesInfo& sizeInfo) +{ + DoSetSizes(sizeInfo, wxGridColumnOperations()); +} + +void wxGrid::SetRowSizes(const wxGridSizesInfo& sizeInfo) +{ + DoSetSizes(sizeInfo, wxGridRowOperations()); +} + +wxGridSizesInfo::wxGridSizesInfo(int defSize, const wxArrayInt& allSizes) +{ + m_sizeDefault = defSize; + for ( size_t i = 0; i < allSizes.size(); i++ ) + { + if ( allSizes[i] != defSize ) + m_customSizes[i] = allSizes[i]; + } +} + +int wxGridSizesInfo::GetSize(unsigned pos) const +{ + wxUnsignedToIntHashMap::const_iterator it = m_customSizes.find(pos); + + return it == m_customSizes.end() ? m_sizeDefault : it->second; +} + // ---------------------------------------------------------------------------- // drop target // ---------------------------------------------------------------------------- @@ -11133,4 +8615,166 @@ wxGridEditorCreatedEvent::wxGridEditorCreatedEvent(int id, wxEventType type, m_ctrl = ctrl; } + +// ---------------------------------------------------------------------------- +// wxGridTypeRegistry +// ---------------------------------------------------------------------------- + +wxGridTypeRegistry::~wxGridTypeRegistry() +{ + size_t count = m_typeinfo.GetCount(); + for ( size_t i = 0; i < count; i++ ) + delete m_typeinfo[i]; +} + +void wxGridTypeRegistry::RegisterDataType(const wxString& typeName, + wxGridCellRenderer* renderer, + wxGridCellEditor* editor) +{ + wxGridDataTypeInfo* info = new wxGridDataTypeInfo(typeName, renderer, editor); + + // is it already registered? + int loc = FindRegisteredDataType(typeName); + if ( loc != wxNOT_FOUND ) + { + delete m_typeinfo[loc]; + m_typeinfo[loc] = info; + } + else + { + m_typeinfo.Add(info); + } +} + +int wxGridTypeRegistry::FindRegisteredDataType(const wxString& typeName) +{ + size_t count = m_typeinfo.GetCount(); + for ( size_t i = 0; i < count; i++ ) + { + if ( typeName == m_typeinfo[i]->m_typeName ) + { + return i; + } + } + + return wxNOT_FOUND; +} + +int wxGridTypeRegistry::FindDataType(const wxString& typeName) +{ + int index = FindRegisteredDataType(typeName); + if ( index == wxNOT_FOUND ) + { + // check whether this is one of the standard ones, in which case + // register it "on the fly" +#if wxUSE_TEXTCTRL + if ( typeName == wxGRID_VALUE_STRING ) + { + RegisterDataType(wxGRID_VALUE_STRING, + new wxGridCellStringRenderer, + new wxGridCellTextEditor); + } + else +#endif // wxUSE_TEXTCTRL +#if wxUSE_CHECKBOX + if ( typeName == wxGRID_VALUE_BOOL ) + { + RegisterDataType(wxGRID_VALUE_BOOL, + new wxGridCellBoolRenderer, + new wxGridCellBoolEditor); + } + else +#endif // wxUSE_CHECKBOX +#if wxUSE_TEXTCTRL + if ( typeName == wxGRID_VALUE_NUMBER ) + { + RegisterDataType(wxGRID_VALUE_NUMBER, + new wxGridCellNumberRenderer, + new wxGridCellNumberEditor); + } + else if ( typeName == wxGRID_VALUE_FLOAT ) + { + RegisterDataType(wxGRID_VALUE_FLOAT, + new wxGridCellFloatRenderer, + new wxGridCellFloatEditor); + } + else +#endif // wxUSE_TEXTCTRL +#if wxUSE_COMBOBOX + if ( typeName == wxGRID_VALUE_CHOICE ) + { + RegisterDataType(wxGRID_VALUE_CHOICE, + new wxGridCellStringRenderer, + new wxGridCellChoiceEditor); + } + else +#endif // wxUSE_COMBOBOX + { + return wxNOT_FOUND; + } + + // we get here only if just added the entry for this type, so return + // the last index + index = m_typeinfo.GetCount() - 1; + } + + return index; +} + +int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName) +{ + int index = FindDataType(typeName); + if ( index == wxNOT_FOUND ) + { + // the first part of the typename is the "real" type, anything after ':' + // are the parameters for the renderer + index = FindDataType(typeName.BeforeFirst(wxT(':'))); + if ( index == wxNOT_FOUND ) + { + return wxNOT_FOUND; + } + + wxGridCellRenderer *renderer = GetRenderer(index); + wxGridCellRenderer *rendererOld = renderer; + renderer = renderer->Clone(); + rendererOld->DecRef(); + + wxGridCellEditor *editor = GetEditor(index); + wxGridCellEditor *editorOld = editor; + editor = editor->Clone(); + editorOld->DecRef(); + + // do it even if there are no parameters to reset them to defaults + wxString params = typeName.AfterFirst(wxT(':')); + renderer->SetParameters(params); + editor->SetParameters(params); + + // register the new typename + RegisterDataType(typeName, renderer, editor); + + // we just registered it, it's the last one + index = m_typeinfo.GetCount() - 1; + } + + return index; +} + +wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index) +{ + wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer; + if (renderer) + renderer->IncRef(); + + return renderer; +} + +wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) +{ + wxGridCellEditor* editor = m_typeinfo[index]->m_editor; + if (editor) + editor->IncRef(); + + return editor; +} + #endif // wxUSE_GRID