From 87f0efe2d7e053089507a4e76c26da516975f6cd Mon Sep 17 00:00:00 2001 From: Robert Roebling Date: Sat, 17 Feb 2007 12:32:38 +0000 Subject: [PATCH] Most of FM's wxDataViewCtrl patch. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@44523 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/dataview.h | 104 +++-- include/wx/generic/dataview.h | 54 ++- include/wx/gtk/dataview.h | 26 +- samples/dataview/dataview.cpp | 354 +++++++++++---- src/common/datavcmn.cpp | 88 ++-- src/generic/datavgen.cpp | 793 +++++++++++++++++++++++++++++----- src/gtk/dataview.cpp | 36 +- 7 files changed, 1175 insertions(+), 280 deletions(-) diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 8893378606..d255228773 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -47,25 +47,31 @@ class WXDLLIMPEXP_ADV wxDataViewRenderer; extern WXDLLIMPEXP_DATA_ADV(const wxChar) wxDataViewCtrlNameStr[]; +// the default width of new (text) columns: +#define wxDVC_DEFAULT_WIDTH 80 + +// the default width of new toggle columns: +#define wxDVC_TOGGLE_DEFAULT_WIDTH 30 + + // --------------------------------------------------------- // wxDataViewModel // --------------------------------------------------------- -class WXDLLIMPEXP_ADV wxDataViewModel: public wxObject +class WXDLLIMPEXP_ADV wxDataViewModel: public wxObjectRefData { public: wxDataViewModel() { } - virtual ~wxDataViewModel() { } protected: - DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewModel) + // the user should not delete this class directly: he should use DecRef() instead! + virtual ~wxDataViewModel() { } }; // --------------------------------------------------------- // wxDataViewListModelNotifier // --------------------------------------------------------- - class WXDLLIMPEXP_ADV wxDataViewListModelNotifier: public wxObject { public: @@ -80,6 +86,8 @@ public: virtual bool ValueChanged( unsigned int col, unsigned int row ) = 0; virtual bool RowsReordered( unsigned int *new_order ) = 0; virtual bool Cleared() = 0; + virtual bool Freed() + { m_owner = NULL; return true; } void SetOwner( wxDataViewListModel *owner ) { m_owner = owner; } wxDataViewListModel *GetOwner() { return m_owner; } @@ -107,16 +115,24 @@ public: class WXDLLIMPEXP_ADV wxDataViewListModel: public wxDataViewModel { + friend class WXDLLIMPEXP_ADV wxDataViewCtrl; + friend class WXDLLIMPEXP_ADV wxDataViewCtrlBase; + friend class WXDLLIMPEXP_ADV wxDataViewSortedListModel; + friend class WXDLLIMPEXP_ADV wxDataViewColumnBase; + friend class WXDLLIMPEXP_ADV wxGtkDataViewListModelNotifier; + public: wxDataViewListModel(); - virtual ~wxDataViewListModel(); virtual unsigned int GetNumberOfRows() = 0; virtual unsigned int GetNumberOfCols() = 0; + // return type as reported by wxVariant virtual wxString GetColType( unsigned int col ) = 0; + // get value into a wxVariant virtual void GetValue( wxVariant &variant, unsigned int col, unsigned int row ) = 0; + // set value, call ValueChanged() afterwards! virtual bool SetValue( wxVariant &variant, unsigned int col, unsigned int row ) = 0; @@ -130,6 +146,10 @@ public: virtual bool RowsReordered( unsigned int *new_order ); virtual bool Cleared(); +protected: + // the user should not delete this class directly: he should use DecRef() instead! + virtual ~wxDataViewListModel(); + // Used internally void AddViewingColumn( wxDataViewColumn *view_column, unsigned int model_column ); void RemoveViewingColumn( wxDataViewColumn *column ); @@ -139,11 +159,10 @@ public: wxList m_notifiers; wxList m_viewingColumns; - -protected: - DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewListModel) }; + + // --------------------------------------------------------- // wxDataViewSortedListModel // --------------------------------------------------------- @@ -155,6 +174,8 @@ WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSortedIndexA class WXDLLIMPEXP_ADV wxDataViewSortedListModel: public wxDataViewListModel { + friend class wxDataViewSortedListModelNotifier; + public: wxDataViewSortedListModel( wxDataViewListModel *child ); virtual ~wxDataViewSortedListModel(); @@ -200,9 +221,6 @@ private: wxDataViewListModelNotifier *m_notifierOnChild; void InitStatics(); // BAD - -protected: - DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewSortedListModel) }; // --------------------------------------------------------- @@ -262,10 +280,14 @@ enum wxDataViewColumnFlags class WXDLLIMPEXP_ADV wxDataViewColumnBase: public wxObject { public: - wxDataViewColumnBase( const wxString &title, wxDataViewRenderer *renderer, unsigned int model_column, - int width = 80, int flags = wxDATAVIEW_COL_RESIZABLE ); - wxDataViewColumnBase( const wxBitmap &bitmap, wxDataViewRenderer *renderer, unsigned int model_column, - int width = 80, int flags = wxDATAVIEW_COL_RESIZABLE ); + wxDataViewColumnBase( const wxString &title, wxDataViewRenderer *renderer, + unsigned int model_column, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); + wxDataViewColumnBase( const wxBitmap &bitmap, wxDataViewRenderer *renderer, + unsigned int model_column, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); virtual ~wxDataViewColumnBase(); virtual void SetTitle( const wxString &title ); @@ -277,9 +299,15 @@ public: virtual void SetAlignment( wxAlignment align ) = 0; virtual void SetSortable( bool sortable ) = 0; - virtual bool GetSortable() = 0; + virtual bool IsSortable() const + { return (m_flags & wxDATAVIEW_COL_SORTABLE) != 0; } + virtual bool IsResizeable() const + { return (m_flags & wxDATAVIEW_COL_RESIZABLE) != 0; } + virtual bool IsHidden() const + { return (m_flags & wxDATAVIEW_COL_HIDDEN) != 0; } + virtual void SetSortOrder( bool ascending ) = 0; - virtual bool IsSortOrderAscending() = 0; + virtual bool IsSortOrderAscending() const = 0; wxDataViewRenderer* GetRenderer() { return m_renderer; } @@ -288,7 +316,7 @@ public: virtual void SetOwner( wxDataViewCtrl *owner ) { m_owner = owner; } wxDataViewCtrl *GetOwner() { return m_owner; } - virtual int GetWidth() = 0; + virtual int GetWidth() const = 0; private: wxDataViewCtrl *m_ctrl; @@ -321,25 +349,45 @@ public: // short cuts bool AppendTextColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendToggleColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = 30 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = wxDVC_TOGGLE_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendProgressColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = 80 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendDateColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_ACTIVATABLE, int width = -1 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_ACTIVATABLE, int width = -1, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendBitmapColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendTextColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendToggleColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = 30 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = wxDVC_TOGGLE_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendProgressColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = 80 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendDateColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_ACTIVATABLE, int width = -1 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_ACTIVATABLE, int width = -1, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); bool AppendBitmapColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1 ); + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, int width = -1, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); virtual bool AppendColumn( wxDataViewColumn *col ); virtual unsigned int GetNumberOfColumns(); diff --git a/include/wx/generic/dataview.h b/include/wx/generic/dataview.h index a8800e37aa..0a96c88b3f 100644 --- a/include/wx/generic/dataview.h +++ b/include/wx/generic/dataview.h @@ -32,7 +32,8 @@ class WXDLLIMPEXP_ADV wxDataViewHeaderWindow; class WXDLLIMPEXP_ADV wxDataViewRenderer: public wxDataViewRendererBase { public: - wxDataViewRenderer( const wxString &varianttype, wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT ); + wxDataViewRenderer( const wxString &varianttype, + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT ); virtual ~wxDataViewRenderer(); virtual bool Render( wxRect cell, wxDC *dc, int state ) = 0; @@ -148,7 +149,8 @@ public: bool GetValue( wxVariant &value ); bool Render( wxRect cell, wxDC *dc, int state ); - bool Activate( wxRect cell, wxDataViewListModel *model, unsigned int col, unsigned int row ); + bool Activate( wxRect cell, wxDataViewListModel *model, unsigned int col, + unsigned int row ); wxSize GetSize(); private: @@ -213,28 +215,46 @@ protected: class WXDLLIMPEXP_ADV wxDataViewColumn: public wxDataViewColumnBase { + friend class wxDataViewHeaderWindowBase; + friend class wxDataViewHeaderWindow; + friend class wxDataViewHeaderWindowMSW; + public: - wxDataViewColumn( const wxString &title, wxDataViewRenderer *renderer, unsigned int model_column, - int width = 80, int flags = wxDATAVIEW_COL_RESIZABLE ); - wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *renderer, unsigned int model_column, - int width = 80, int flags = wxDATAVIEW_COL_RESIZABLE ); + wxDataViewColumn( const wxString &title, wxDataViewRenderer *renderer, + unsigned int model_column, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); + wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *renderer, + unsigned int model_column, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); virtual ~wxDataViewColumn(); virtual void SetTitle( const wxString &title ); virtual void SetBitmap( const wxBitmap &bitmap ); - virtual void SetAlignment( wxAlignment align ); + virtual void SetAlignment( wxAlignment align ) + { m_align=align; } + wxAlignment GetAlignment() const + { return m_align; } virtual void SetSortable( bool sortable ); - virtual bool GetSortable(); virtual void SetSortOrder( bool ascending ); - virtual bool IsSortOrderAscending(); + virtual bool IsSortOrderAscending() const; + + virtual int GetWidth() const; + +protected: - virtual int GetWidth(); + void SetWidth(int w) + { m_width=w; } private: int m_width; int m_fixedWidth; + wxAlignment m_align; + + void Init(int width); protected: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewColumn) @@ -247,6 +267,11 @@ protected: class WXDLLIMPEXP_ADV wxDataViewCtrl: public wxDataViewCtrlBase, public wxScrollHelperNative { + friend class wxDataViewMainWindow; + friend class wxDataViewHeaderWindowBase; + friend class wxDataViewHeaderWindow; + friend class wxDataViewHeaderWindowMSW; + public: wxDataViewCtrl() : wxScrollHelperNative(this) { @@ -283,9 +308,14 @@ public: virtual int GetSelection() const; virtual int GetSelections(wxArrayInt& aSelections) const; + // returns the "best" width for the idx-th column + unsigned int GetBestColumnWidth(int WXUNUSED(idx)) + { + return GetClientSize().GetWidth() / GetNumberOfColumns(); + } + + private: - friend class wxDataViewMainWindow; - friend class wxDataViewHeaderWindow; wxDataViewListModelNotifier *m_notifier; wxDataViewMainWindow *m_clientArea; wxDataViewHeaderWindow *m_headerArea; diff --git a/include/wx/gtk/dataview.h b/include/wx/gtk/dataview.h index e575a4f2c0..78ddb1d55b 100644 --- a/include/wx/gtk/dataview.h +++ b/include/wx/gtk/dataview.h @@ -21,6 +21,7 @@ class WXDLLIMPEXP_CORE wxDataViewCtrl; + // --------------------------------------------------------- // wxDataViewRenderer // --------------------------------------------------------- @@ -28,7 +29,8 @@ class WXDLLIMPEXP_CORE wxDataViewCtrl; class wxDataViewRenderer: public wxDataViewRendererBase { public: - wxDataViewRenderer( const wxString &varianttype, wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT ); + wxDataViewRenderer( const wxString &varianttype, + wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT ); // implementation void* GetGtkHandle() { return m_renderer; } @@ -188,10 +190,15 @@ protected: class WXDLLIMPEXP_CORE wxDataViewColumn: public wxDataViewColumnBase { public: - wxDataViewColumn( const wxString &title, wxDataViewRenderer *renderer, unsigned int model_column, - int width = 80, int flags = wxDATAVIEW_COL_RESIZABLE ); - wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *renderer, unsigned int model_column, - int width = 80, int flags = wxDATAVIEW_COL_RESIZABLE ); + wxDataViewColumn( const wxString &title, wxDataViewRenderer *renderer, + unsigned int model_column, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); + wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *renderer, + unsigned int model_column, int width = wxDVC_DEFAULT_WIDTH, + wxAlignment align = wxALIGN_CENTER, + int flags = wxDATAVIEW_COL_RESIZABLE ); + virtual ~wxDataViewColumn(); virtual void SetTitle( const wxString &title ); @@ -202,14 +209,15 @@ public: virtual void SetAlignment( wxAlignment align ); virtual void SetSortable( bool sortable ); - virtual bool GetSortable(); virtual void SetSortOrder( bool ascending ); - virtual bool IsSortOrderAscending(); - virtual int GetWidth(); + virtual bool IsSortable() const; + virtual bool IsSortOrderAscending() const; + + virtual int GetWidth() const; virtual void SetFixedWidth( int width ); - virtual int GetFixedWidth(); + virtual int GetFixedWidth() const; // implementation void* GetGtkHandle() { return m_column; } diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index a8e6d7935a..dd44fadb32 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -21,6 +21,8 @@ #endif #include "wx/datetime.h" +#include "wx/splitter.h" +#include "wx/aboutdlg.h" #ifndef __WXMSW__ #include "../sample.xpm" @@ -34,9 +36,9 @@ // MyTextModel // ------------------------------------- -WX_DECLARE_LIST(wxDateTime,wxArrayDate); -#include -WX_DEFINE_LIST(wxArrayDate) +WX_DECLARE_OBJARRAY(wxDateTime,wxArrayDate); +#include +WX_DEFINE_OBJARRAY(wxArrayDate) class MyTextModel: public wxDataViewListModel { @@ -54,8 +56,8 @@ public: { m_progress.Add( i/10 ); } for (i = 0; i < 1000; i++) { - wxDateTime *date = new wxDateTime( wxDateTime::Now() ); - m_dates.Append( date ); + wxDateTime date( wxDateTime::Now() ); + m_dates.Add( date ); } } @@ -83,7 +85,7 @@ public: { if (col == 6) { - variant = (wxDateTime) *m_dates[row]; + variant = (wxDateTime) m_dates[row]; } else if (col == 5) { @@ -95,7 +97,7 @@ public: } else if (col == 3) { - variant = (bool) m_bools[row]; + variant = (bool) (m_bools[row] != 0); } else if (col == 2) { @@ -112,7 +114,7 @@ public: { if (col == 6) { - *m_dates[row] = value.GetDateTime(); + m_dates[row] = value.GetDateTime(); } else if (col == 3) { @@ -263,13 +265,14 @@ class MyApp: public wxApp { public: bool OnInit(void); + int OnExit(); }; // ------------------------------------- // MyFrame // ------------------------------------- -class MyFrame: public wxFrame +class MyFrame : public wxFrame { public: MyFrame(wxFrame *frame, wxChar *title, int x, int y, int w, int h); @@ -277,10 +280,26 @@ public: public: void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); + void OnNewSortingFrame(wxCommandEvent& event); + + void OnSingleSelMode(wxCommandEvent& event); + void OnMultipleSelMode(wxCommandEvent& event); + void OnResizeableColumn(wxCommandEvent& event); + void OnSortableColumn(wxCommandEvent& event); + void OnHideColumn(wxCommandEvent& event); + void OnChooseAlign(wxCommandEvent& event); private: wxDataViewCtrl* dataview_left; wxDataViewCtrl* dataview_right; + wxSplitterWindow *m_splitter; + + wxAlignment m_align; + + void CreateControls(); + +private: + DECLARE_EVENT_TABLE() }; // ------------------------------------- @@ -358,80 +377,174 @@ private: // MyApp // ------------------------------------- -#define DYNAMIC_QUIT wxID_EXIT -#define DYNAMIC_ABOUT wxID_ABOUT - -IMPLEMENT_APP (MyApp) +IMPLEMENT_APP(MyApp) bool MyApp::OnInit(void) { if ( !wxApp::OnInit() ) return false; - MyFrame *frame = new MyFrame(NULL, wxT("wxDataViewCtrl feature test"), 10, 10, 800, 340); + // build the first frame + MyFrame *frame = + new MyFrame(NULL, wxT("wxDataViewCtrl feature test"), 10, 10, 800, 340); frame->Show(true); - MySortingFrame *frame2 = new MySortingFrame(NULL, wxT("wxDataViewCtrl sorting test"), 10, 150, 600, 500); - frame2->Show(true); - SetTopWindow(frame); - return true; } +int MyApp::OnExit() +{ + return 0; +} + + // ------------------------------------- // MyFrame // ------------------------------------- +enum +{ + // file menu + ID_ABOUT = wxID_ABOUT, + ID_NEW_SORT_FRAME, + ID_EXIT = wxID_EXIT, + + // dataviewctrl menu + ID_SINGLE_SEL_MODE, + ID_MULTIPLE_SEL_MODE, + + ID_RESIZEABLE_COLUMNS, + ID_SORTABLE_COLUMNS, + ID_HIDDEN_COLUMNS, + + ID_CHOOSE_ALIGNMENT +}; + +BEGIN_EVENT_TABLE(MyFrame, wxFrame) + + // file menu + EVT_MENU( ID_ABOUT, MyFrame::OnAbout ) + EVT_MENU( ID_NEW_SORT_FRAME, MyFrame::OnNewSortingFrame ) + EVT_MENU( ID_EXIT, MyFrame::OnQuit ) + + // dataviewctrl menu + EVT_MENU( ID_SINGLE_SEL_MODE, MyFrame::OnSingleSelMode ) + EVT_MENU( ID_MULTIPLE_SEL_MODE, MyFrame::OnMultipleSelMode ) + + EVT_MENU( ID_RESIZEABLE_COLUMNS, MyFrame::OnResizeableColumn ) + EVT_MENU( ID_SORTABLE_COLUMNS, MyFrame::OnSortableColumn ) + EVT_MENU( ID_HIDDEN_COLUMNS, MyFrame::OnHideColumn ) + + EVT_MENU( ID_CHOOSE_ALIGNMENT, MyFrame::OnChooseAlign ) + +END_EVENT_TABLE() + MyFrame::MyFrame(wxFrame *frame, wxChar *title, int x, int y, int w, int h): wxFrame(frame, wxID_ANY, title, wxPoint(x, y), wxSize(w, h)) { SetIcon(wxICON(sample)); + // build the menus: + wxMenu *file_menu = new wxMenu; + file_menu->Append(ID_NEW_SORT_FRAME, _T("&New sorting frame")); + file_menu->AppendSeparator(); + file_menu->Append(ID_ABOUT, _T("&About")); + file_menu->AppendSeparator(); + file_menu->Append(ID_EXIT, _T("E&xit")); + + wxMenu *data_menu = new wxMenu; + data_menu->AppendRadioItem(ID_SINGLE_SEL_MODE, _T("&Single selection mode")); + data_menu->AppendRadioItem(ID_MULTIPLE_SEL_MODE, _T("&Multiple selection mode")); + data_menu->AppendSeparator(); + data_menu->AppendCheckItem(ID_RESIZEABLE_COLUMNS, _T("Make columns resizeable")); + data_menu->AppendCheckItem(ID_SORTABLE_COLUMNS, _T("Make columns sortable")); + data_menu->AppendCheckItem(ID_HIDDEN_COLUMNS, _T("Make columns hidden")); + data_menu->AppendSeparator(); + data_menu->Append(ID_CHOOSE_ALIGNMENT, _T("Set alignment...")); - file_menu->Append(DYNAMIC_ABOUT, _T("&About")); - file_menu->Append(DYNAMIC_QUIT, _T("E&xit")); wxMenuBar *menu_bar = new wxMenuBar; menu_bar->Append(file_menu, _T("&File")); + menu_bar->Append(data_menu, _T("&DataViewCtrl")); + SetMenuBar(menu_bar); + CreateStatusBar(); - // You used to have to do some casting for param 4, but now there are type-safe handlers - Connect( DYNAMIC_QUIT, wxID_ANY, - wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnQuit) ); - Connect( DYNAMIC_ABOUT, wxID_ANY, - wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnAbout) ); - CreateStatusBar(); + // build the other controls: - wxPanel *panel = new wxPanel( this, wxID_ANY ); + m_splitter = new wxSplitterWindow( this, wxID_ANY ); + m_splitter->SetSashGravity(0.5); + + m_align = wxALIGN_CENTER; + dataview_left = dataview_right = NULL; + CreateControls(); + + m_splitter->SplitVertically(dataview_left, dataview_right); +} + +void MyFrame::CreateControls() +{ + wxDataViewCtrl *old1 = NULL, *old2 = NULL; + + if (dataview_left) + old1 = dataview_left; + if (dataview_right) + old2 = dataview_right; + + // styles: + + long style = 0; + if (GetMenuBar()->FindItem(ID_MULTIPLE_SEL_MODE)->IsChecked()) + style |= wxDV_MULTIPLE; + + int flags = 0; + if (GetMenuBar()->FindItem(ID_RESIZEABLE_COLUMNS)->IsChecked()) + flags |= wxDATAVIEW_COL_RESIZABLE; + if (GetMenuBar()->FindItem(ID_SORTABLE_COLUMNS)->IsChecked()) + flags |= wxDATAVIEW_COL_SORTABLE; + if (GetMenuBar()->FindItem(ID_HIDDEN_COLUMNS)->IsChecked()) + flags |= wxDATAVIEW_COL_HIDDEN; // Left wxDataViewCtrl - dataview_left = new wxDataViewCtrl( panel, wxID_ANY ); + dataview_left = new wxDataViewCtrl( m_splitter, wxID_ANY, wxDefaultPosition, + wxDefaultSize, style ); MyTextModel *model = new MyTextModel; dataview_left->AssociateModel( model ); + model->DecRef(); // don't leak memory - dataview_left->AppendTextColumn( wxT("first"), 0 ); - dataview_left->AppendTextColumn( wxT("second"), 1 ); + dataview_left->AppendTextColumn( wxT("first"), 0, wxDATAVIEW_CELL_INERT, -1, + m_align, flags ); + dataview_left->AppendTextColumn( wxT("second"), 1, wxDATAVIEW_CELL_INERT, -1, + m_align, flags ); - wxDataViewTextRenderer *text_renderer = new wxDataViewTextRenderer( wxT("string"), wxDATAVIEW_CELL_EDITABLE ); - wxDataViewColumn *column = new wxDataViewColumn( wxT("editable"), text_renderer, 2 ); + wxDataViewTextRenderer *text_renderer = + new wxDataViewTextRenderer( wxT("string"), wxDATAVIEW_CELL_EDITABLE ); + wxDataViewColumn *column = new wxDataViewColumn( wxT("editable"), text_renderer, 2, + -1, m_align, flags ); dataview_left->AppendColumn( column ); - dataview_left->AppendToggleColumn( wxT("fourth"), 3 ); + dataview_left->AppendToggleColumn( wxT("fourth"), 3, wxDATAVIEW_CELL_INERT, -1, + m_align, flags ); MyCustomRenderer *custom_renderer = new MyCustomRenderer; - column = new wxDataViewColumn( wxT("custom"), custom_renderer, 4 ); + column = new wxDataViewColumn( wxT("custom"), custom_renderer, 4, -1, + m_align, flags ); dataview_left->AppendColumn( column ); - dataview_left->AppendProgressColumn( wxT("progress"), 5 ); + dataview_left->AppendProgressColumn( wxT("progress"), 5, wxDATAVIEW_CELL_INERT, -1, + m_align, flags ); + + dataview_left->AppendDateColumn( wxT("date"), 6, wxDATAVIEW_CELL_INERT, -1, + m_align, flags ); - dataview_left->AppendDateColumn( wxT("date"), 6 ); // Right wxDataViewCtrl using the same model - dataview_right = new wxDataViewCtrl( panel, wxID_ANY ); + dataview_right = new wxDataViewCtrl( m_splitter, wxID_ANY, wxDefaultPosition, + wxDefaultSize, style ); dataview_right->AssociateModel( model ); text_renderer = new wxDataViewTextRenderer( wxT("string"), wxDATAVIEW_CELL_EDITABLE ); @@ -439,19 +552,25 @@ MyFrame::MyFrame(wxFrame *frame, wxChar *title, int x, int y, int w, int h): dataview_right->AppendColumn( column ); dataview_right->AppendTextColumn( wxT("first"), 0 ); dataview_right->AppendTextColumn( wxT("second"), 1 ); - wxDataViewToggleRenderer *toggle_renderer = new wxDataViewToggleRenderer( wxT("bool"), wxDATAVIEW_CELL_ACTIVATABLE ); + wxDataViewToggleRenderer *toggle_renderer = + new wxDataViewToggleRenderer( wxT("bool"), wxDATAVIEW_CELL_ACTIVATABLE ); column = new wxDataViewColumn( wxT("bool"), toggle_renderer, 3, 30 ); dataview_right->AppendColumn( column ); dataview_right->AppendDateColumn( wxT("date"), 6 ); - // layout dataview controls. + // layout dataview controls + if (old1) + { + m_splitter->ReplaceWindow(old1, dataview_left); + delete old1; + } - wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL ); - sizer->Add( dataview_left, 3, wxGROW ); - sizer->Add(10,10); - sizer->Add( dataview_right, 2, wxGROW ); - panel->SetSizer( sizer ); + if (old2) + { + m_splitter->ReplaceWindow(old2, dataview_right); + delete old2; + } } void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) @@ -461,12 +580,84 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) ) { - wxMessageDialog dialog(this, _T("This demonstrates the dataview control handling"), - _T("About DataView"), wxOK); + wxAboutDialogInfo info; + info.SetName(_("DataView sample")); + info.SetDescription(_("This sample demonstrates the dataview control handling")); + info.SetCopyright(_T("(C) 2007 Robert Roebling")); - dialog.ShowModal(); + wxAboutBox(info); +} + +void MyFrame::OnNewSortingFrame(wxCommandEvent& WXUNUSED(event) ) +{ + MySortingFrame *frame2 = + new MySortingFrame(NULL, wxT("wxDataViewCtrl sorting test"), 10, 150, 600, 500); + frame2->Show(true); +} + +void MyFrame::OnSingleSelMode(wxCommandEvent& event) +{ + CreateControls(); +} + +void MyFrame::OnMultipleSelMode(wxCommandEvent& event) +{ + CreateControls(); +} + +void MyFrame::OnResizeableColumn(wxCommandEvent& event) +{ + CreateControls(); +} + +void MyFrame::OnSortableColumn(wxCommandEvent& event) +{ + CreateControls(); +} + +void MyFrame::OnHideColumn(wxCommandEvent& event) +{ + CreateControls(); } +void MyFrame::OnChooseAlign(wxCommandEvent& event) +{ + const wxString choices[] = + { + wxT("Left"), + wxT("Center horizontally"), + wxT("Right"), + wxT("Top"), + wxT("Center vertically"), + wxT("Bottom"), + wxT("Center") + }; + + wxAlignment flags[] = + { + wxALIGN_LEFT, + wxALIGN_CENTER_HORIZONTAL, + wxALIGN_RIGHT, + wxALIGN_TOP, + wxALIGN_CENTER_VERTICAL, + wxALIGN_BOTTOM, + wxALIGN_CENTER + }; + + int choice = wxGetSingleChoiceIndex( + wxT("Select the alignment for the cells of the wxDataViewCtrl:"), + wxT("Alignment"), + WXSIZEOF(choices), choices, + this); + + if (choice == wxNOT_FOUND) + return; + + m_align = flags[choice]; + CreateControls(); +} + + // ------------------------------------- // MySortingFrame // ------------------------------------- @@ -491,43 +682,39 @@ MySortingFrame::MySortingFrame(wxFrame *frame, wxChar *title, int x, int y, int m_logOld = NULL; SetIcon(wxICON(sample)); - - wxMenu *file_menu = new wxMenu; - - file_menu->Append(DYNAMIC_ABOUT, _T("&About")); - file_menu->Append(DYNAMIC_QUIT, _T("E&xit")); - wxMenuBar *menu_bar = new wxMenuBar; - menu_bar->Append(file_menu, _T("&File")); - SetMenuBar(menu_bar); - - // You used to have to do some casting for param 4, but now there are type-safe handlers - Connect( DYNAMIC_QUIT, wxID_ANY, - wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MySortingFrame::OnQuit) ); - Connect( DYNAMIC_ABOUT, wxID_ANY, - wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MySortingFrame::OnAbout) ); - CreateStatusBar(); + wxPanel *main = new wxPanel(this); // Left wxDataViewCtrl - dataview_left = new wxDataViewCtrl( this, ID_UNSORTED, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE ); + dataview_left = new wxDataViewCtrl( main, ID_UNSORTED, wxDefaultPosition, + wxDefaultSize, wxDV_MULTIPLE ); m_unsorted_model = new MyUnsortedTextModel; dataview_left->AssociateModel( m_unsorted_model ); - wxDataViewTextRenderer *text_renderer = new wxDataViewTextRenderer( wxT("string"), wxDATAVIEW_CELL_EDITABLE ); + m_unsorted_model->DecRef(); // don't leak memory + + wxDataViewTextRenderer *text_renderer = + new wxDataViewTextRenderer( wxT("string"), wxDATAVIEW_CELL_EDITABLE ); wxDataViewColumn *column = new wxDataViewColumn( wxT("editable"), text_renderer, 0 ); dataview_left->AppendColumn( column ); dataview_left->AppendTextColumn( wxT("second"), 1 ); - dataview_left->AppendColumn( new wxDataViewColumn( wxBitmap(null_xpm), new wxDataViewBitmapRenderer, 2, 25 ) ); - dataview_left->AppendColumn( new wxDataViewColumn( wxT("icon"), new wxDataViewBitmapRenderer, 3, 25 ) ); + dataview_left->AppendColumn( new wxDataViewColumn( wxBitmap(null_xpm), + new wxDataViewBitmapRenderer, 2, 25 ) ); + dataview_left->AppendColumn( new wxDataViewColumn( wxT("icon"), + new wxDataViewBitmapRenderer, 3, 25 ) ); // Right wxDataViewCtrl using the sorting model - dataview_right = new wxDataViewCtrl( this, ID_SORTED ); + dataview_right = new wxDataViewCtrl( main, ID_SORTED ); m_sorted_model = new wxDataViewSortedListModel( m_unsorted_model ); dataview_right->AssociateModel( m_sorted_model ); + m_sorted_model->DecRef(); // don't leak memory + text_renderer = new wxDataViewTextRenderer( wxT("string"), wxDATAVIEW_CELL_EDITABLE ); - column = new wxDataViewColumn( wxT("editable"), text_renderer, 0, -1, wxDATAVIEW_COL_SORTABLE|wxDATAVIEW_COL_RESIZABLE ); + column = new wxDataViewColumn( wxT("editable"), text_renderer, 0, -1, + wxALIGN_CENTER, + wxDATAVIEW_COL_SORTABLE|wxDATAVIEW_COL_RESIZABLE ); dataview_right->AppendColumn( column ); dataview_right->AppendTextColumn( wxT("second"), 1 ); @@ -542,22 +729,22 @@ MySortingFrame::MySortingFrame(wxFrame *frame, wxChar *title, int x, int y, int wxBoxSizer *button_sizer = new wxBoxSizer( wxHORIZONTAL ); button_sizer->Add( 10, 10, 1 ); wxFlexGridSizer *left_sizer = new wxFlexGridSizer( 2 ); - left_sizer->Add( new wxButton( this, ID_APPEND_ROW_LEFT, wxT("Append") ), 0, wxALL, 5 ); - left_sizer->Add( new wxButton( this, ID_PREPEND_ROW_LEFT, wxT("Prepend") ), 0, wxALL, 5 ); - left_sizer->Add( new wxButton( this, ID_INSERT_ROW_LEFT, wxT("Insert") ), 0, wxALL, 5 ); - left_sizer->Add( new wxButton( this, ID_DELETE_ROW_LEFT, wxT("Delete second") ), 0, wxALL, 5 ); - left_sizer->Add( new wxButton( this, ID_EDIT_ROW_LEFT, wxT("Edit") ), 0, wxALL, 5 ); + left_sizer->Add( new wxButton( main, ID_APPEND_ROW_LEFT, wxT("Append") ), 0, wxALL, 5 ); + left_sizer->Add( new wxButton( main, ID_PREPEND_ROW_LEFT, wxT("Prepend") ), 0, wxALL, 5 ); + left_sizer->Add( new wxButton( main, ID_INSERT_ROW_LEFT, wxT("Insert") ), 0, wxALL, 5 ); + left_sizer->Add( new wxButton( main, ID_DELETE_ROW_LEFT, wxT("Delete second") ), 0, wxALL, 5 ); + left_sizer->Add( new wxButton( main, ID_EDIT_ROW_LEFT, wxT("Edit") ), 0, wxALL, 5 ); left_sizer->Add( 5,5 ); - left_sizer->Add( new wxButton( this, ID_SELECT, wxT("Select third") ), 0, wxALL, 5 ); - left_sizer->Add( new wxButton( this, ID_UNSELECT_ALL, wxT("Unselect all") ), 0, wxALL, 5 ); + left_sizer->Add( new wxButton( main, ID_SELECT, wxT("Select third") ), 0, wxALL, 5 ); + left_sizer->Add( new wxButton( main, ID_UNSELECT_ALL, wxT("Unselect all") ), 0, wxALL, 5 ); button_sizer->Add( left_sizer ); button_sizer->Add( 10, 10, 2 ); wxFlexGridSizer *right_sizer = new wxFlexGridSizer( 2 ); - right_sizer->Add( new wxButton( this, ID_APPEND_ROW_RIGHT, wxT("Append") ), 0, wxALL, 5 ); - right_sizer->Add( new wxButton( this, ID_PREPEND_ROW_RIGHT, wxT("Prepend") ), 0, wxALL, 5 ); - right_sizer->Add( new wxButton( this, ID_INSERT_ROW_RIGHT, wxT("Insert") ), 0, wxALL, 5 ); - right_sizer->Add( new wxButton( this, ID_DELETE_ROW_RIGHT, wxT("Delete second") ), 0, wxALL, 5 ); - right_sizer->Add( new wxButton( this, ID_EDIT_ROW_RIGHT, wxT("Edit") ), 0, wxALL, 5 ); + right_sizer->Add( new wxButton( main, ID_APPEND_ROW_RIGHT, wxT("Append") ), 0, wxALL, 5 ); + right_sizer->Add( new wxButton( main, ID_PREPEND_ROW_RIGHT, wxT("Prepend") ), 0, wxALL, 5 ); + right_sizer->Add( new wxButton( main, ID_INSERT_ROW_RIGHT, wxT("Insert") ), 0, wxALL, 5 ); + right_sizer->Add( new wxButton( main, ID_DELETE_ROW_RIGHT, wxT("Delete second") ), 0, wxALL, 5 ); + right_sizer->Add( new wxButton( main, ID_EDIT_ROW_RIGHT, wxT("Edit") ), 0, wxALL, 5 ); button_sizer->Add( right_sizer ); button_sizer->Add( 10, 10, 1 ); @@ -565,7 +752,7 @@ MySortingFrame::MySortingFrame(wxFrame *frame, wxChar *title, int x, int y, int main_sizer->Add( top_sizer, 1, wxGROW ); main_sizer->Add( button_sizer, 0, wxGROW ); - m_logWindow = new wxTextCtrl(this, wxID_ANY, wxEmptyString, + m_logWindow = new wxTextCtrl(main, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxSUNKEN_BORDER); main_sizer->Add( 20,20 ); @@ -573,7 +760,7 @@ MySortingFrame::MySortingFrame(wxFrame *frame, wxChar *title, int x, int y, int m_logOld = wxLog::SetActiveTarget(new wxLogTextCtrl(m_logWindow)); - SetSizer( main_sizer ); + main->SetSizer( main_sizer ); } MySortingFrame::~MySortingFrame() @@ -625,7 +812,8 @@ void MySortingFrame::OnHeaderClickSorted(wxDataViewEvent &event) void MySortingFrame::OnHeaderClickUnsorted(wxDataViewEvent &event) { - wxLogMessage( wxT("OnHeaderClick from unsorted list, column %s"), event.GetDataViewColumn()->GetTitle().c_str() ); + wxLogMessage( wxT("OnHeaderClick from unsorted list, column %s"), + event.GetDataViewColumn()->GetTitle().c_str() ); } void MySortingFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index 1094b1ebca..8ebcfef112 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -25,18 +25,11 @@ const wxChar wxDataViewCtrlNameStr[] = wxT("dataviewCtrl"); -// --------------------------------------------------------- -// wxDataViewModel -// --------------------------------------------------------- - -IMPLEMENT_ABSTRACT_CLASS(wxDataViewModel, wxObject) // --------------------------------------------------------- // wxDataViewListModel // --------------------------------------------------------- -IMPLEMENT_ABSTRACT_CLASS(wxDataViewListModel, wxDataViewModel) - wxDataViewListModel::wxDataViewListModel() { m_viewingColumns.DeleteContents( true ); @@ -45,6 +38,13 @@ wxDataViewListModel::wxDataViewListModel() wxDataViewListModel::~wxDataViewListModel() { + wxList::compatibility_iterator node = m_notifiers.GetFirst(); + while (node) + { + wxDataViewListModelNotifier* notifier = (wxDataViewListModelNotifier*) node->GetData(); + notifier->Freed(); + node = node->GetNext(); + } } bool wxDataViewListModel::RowAppended() @@ -242,6 +242,9 @@ public: virtual bool Cleared() { return m_model->ChildCleared(); } + virtual bool Freed() + { m_model->m_child = NULL; return wxDataViewListModelNotifier::Freed(); } + wxDataViewSortedListModel *m_model; }; @@ -306,8 +309,6 @@ int LINKAGEMODE wxDataViewIntermediateCmp( unsigned int row1, unsigned int row2 // wxDataViewSortedListModel // --------------------------------------------------------- -IMPLEMENT_ABSTRACT_CLASS(wxDataViewSortedListModel, wxDataViewListModel) - wxDataViewSortedListModel::wxDataViewSortedListModel( wxDataViewListModel *child ) : m_array( wxDataViewIntermediateCmp ) { @@ -323,6 +324,7 @@ wxDataViewSortedListModel::wxDataViewSortedListModel( wxDataViewListModel *child wxDataViewSortedListModel::~wxDataViewSortedListModel() { + if (m_child) m_child->RemoveNotifier( m_notifierOnChild ); } @@ -715,6 +717,7 @@ wxDataViewColumnBase::wxDataViewColumnBase(const wxString& title, wxDataViewRenderer *renderer, unsigned int model_column, int WXUNUSED(width), + wxAlignment WXUNUSED(align), int flags ) { m_renderer = renderer; @@ -729,6 +732,7 @@ wxDataViewColumnBase::wxDataViewColumnBase(const wxBitmap& bitmap, wxDataViewRenderer *renderer, unsigned int model_column, int WXUNUSED(width), + wxAlignment WXUNUSED(align), int flags ) { m_renderer = renderer; @@ -784,11 +788,27 @@ wxDataViewCtrlBase::wxDataViewCtrlBase() wxDataViewCtrlBase::~wxDataViewCtrlBase() { + // IMPORTANT: before calling DecRef() on our model (since it may + // result in a free() call), erase all columns (since + // they hold a pointer to our model) + m_cols.Clear(); + + if (m_model) + { + m_model->DecRef(); + m_model = NULL; + } } bool wxDataViewCtrlBase::AssociateModel( wxDataViewListModel *model ) { + if ( m_model ) + m_model->DecRef(); // discard old model, if any + + // add our own reference to the new model: m_model = model; + if ( m_model ) + m_model->IncRef(); return true; } @@ -799,73 +819,83 @@ wxDataViewListModel* wxDataViewCtrlBase::GetModel() } bool wxDataViewCtrlBase::AppendTextColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewTextRenderer( wxT("string"), mode ), model_column, width ) ); + new wxDataViewTextRenderer( wxT("string"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendToggleColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewToggleRenderer( wxT("bool"), mode ), model_column, width ) ); + new wxDataViewToggleRenderer( wxT("bool"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendProgressColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewProgressRenderer( wxEmptyString, wxT("long"), mode ), model_column, width ) ); + new wxDataViewProgressRenderer( wxEmptyString, wxT("long"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendDateColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewDateRenderer( wxT("datetime"), mode), model_column, width ) ); + new wxDataViewDateRenderer( wxT("datetime"), mode), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendBitmapColumn( const wxString &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewBitmapRenderer( wxT("wxBitmap"), mode ), model_column, width ) ); + new wxDataViewBitmapRenderer( wxT("wxBitmap"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendTextColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewTextRenderer( wxT("string"), mode ), model_column, width ) ); + new wxDataViewTextRenderer( wxT("string"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendToggleColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewToggleRenderer( wxT("bool"), mode ), model_column, width ) ); + new wxDataViewToggleRenderer( wxT("bool"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendProgressColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewProgressRenderer( wxEmptyString, wxT("long"), mode ), model_column, width ) ); + new wxDataViewProgressRenderer( wxEmptyString, wxT("long"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendDateColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewDateRenderer( wxT("datetime"), mode ), model_column, width ) ); + new wxDataViewDateRenderer( wxT("datetime"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendBitmapColumn( const wxBitmap &label, unsigned int model_column, - wxDataViewCellMode mode, int width ) + wxDataViewCellMode mode, int width, wxAlignment align, int flags ) { return AppendColumn( new wxDataViewColumn( label, - new wxDataViewBitmapRenderer( wxT("wxBitmap"), mode ), model_column, width ) ); + new wxDataViewBitmapRenderer( wxT("wxBitmap"), mode ), model_column, + width, align, flags ) ); } bool wxDataViewCtrlBase::AppendColumn( wxDataViewColumn *col ) diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index f1bcc09c3c..c6996a685f 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -2,6 +2,7 @@ // Name: src/generic/datavgen.cpp // Purpose: wxDataViewCtrl generic implementation // Author: Robert Roebling +// Modified by: Francesco Montorsi, Guru Kathiresan, Otto Wyss // Id: $Id$ // Copyright: (c) 1998 Robert Roebling // Licence: wxWindows licence @@ -22,7 +23,9 @@ #ifndef WX_PRECOMP #ifdef __WXMSW__ + #include "wx/msw/private.h" #include "wx/msw/wrapwin.h" + #include "wx/msw/wrapcctl.h" // include "properly" #endif #include "wx/sizer.h" #include "wx/log.h" @@ -49,32 +52,157 @@ class wxDataViewCtrl; // wxDataViewHeaderWindow //----------------------------------------------------------------------------- -class wxDataViewHeaderWindow: public wxWindow +#define USE_NATIVE_HEADER_WINDOW 1 + +class wxDataViewHeaderWindowBase : public wxControl +{ +public: + wxDataViewHeaderWindowBase() + { m_owner = NULL; } + + bool Create(wxDataViewCtrl *parent, wxWindowID id, + const wxPoint &pos, const wxSize &size, + const wxString &name) + { + return wxWindow::Create(parent, id, pos, size, wxNO_BORDER, name); + } + + void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; } + wxDataViewCtrl *GetOwner() { return m_owner; } + + // called on column addition/removal + virtual void UpdateDisplay() { /* by default, do nothing */ } + + // updates the n-th column's width + virtual void SetColumnWidth(unsigned int n, int width); + + // returns the n-th column + wxDataViewColumn *GetColumn(unsigned int n) + { + wxASSERT(m_owner); + wxDataViewColumn *ret = m_owner->GetColumn(n); + wxASSERT(ret); + + return ret; + } + +protected: + wxDataViewCtrl *m_owner; + + // sends an event generated from the n-th wxDataViewColumn + void SendEvent(wxEventType type, unsigned int n); +}; + +// on wxMSW the header window (only that part however) can be made native! +#if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW + +#define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow + +class wxDataViewHeaderWindowMSW : public wxDataViewHeaderWindowBase { public: - wxDataViewHeaderWindow( wxDataViewCtrl *parent, + + wxDataViewHeaderWindowMSW( wxDataViewCtrl *parent, wxWindowID id, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, - const wxString &name = wxT("wxdataviewctrlheaderwindow") ); - virtual ~wxDataViewHeaderWindow(); + const wxString &name = wxT("wxdataviewctrlheaderwindow") ) + { + Create(parent, id, pos, size, name); + } - void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; } - wxDataViewCtrl *GetOwner() { return m_owner; } + bool Create(wxDataViewCtrl *parent, wxWindowID id, + const wxPoint &pos, const wxSize &size, + const wxString &name); + + ~wxDataViewHeaderWindowMSW(); + + // called on column addition/removal + virtual void UpdateDisplay(); + + // called when the main window gets scrolled + virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL); + +protected: + virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result); + +private: + DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW) +}; + +#else // !defined(__WXMSW__) + +#define HEADER_WINDOW_HEIGHT 25 +#define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow + +class wxGenericDataViewHeaderWindow : public wxDataViewHeaderWindowBase +{ +public: + wxGenericDataViewHeaderWindow( wxDataViewCtrl *parent, + wxWindowID id, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + const wxString &name = wxT("wxdataviewctrlheaderwindow") ) + { + Init(); + Create(parent, id, pos, size, name); + } + + bool Create(wxDataViewCtrl *parent, wxWindowID id, + const wxPoint &pos, const wxSize &size, + const wxString &name); + + ~wxGenericDataViewHeaderWindow() + { + delete m_resizeCursor; + } + + // event handlers: void OnPaint( wxPaintEvent &event ); void OnMouse( wxMouseEvent &event ); void OnSetFocus( wxFocusEvent &event ); -private: - wxDataViewCtrl *m_owner; + +protected: + + // vars used for column resizing: + wxCursor *m_resizeCursor; + const wxCursor *m_currentCursor; + bool m_isDragging; + + bool m_dirty; // needs refresh? + int m_column; // index of the column being resized + int m_currentX; // divider line position in logical (unscrolled) coords + int m_minX; // minimal position beyond which the divider line + // can't be dragged in logical coords + + // internal utilities: + + void Init() + { + m_currentCursor = (wxCursor *) NULL; + m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE ); + + m_isDragging = false; + m_dirty = false; + + m_column = wxNOT_FOUND; + m_currentX = 0; + m_minX = 0; + } + + void DrawCurrent(); + void AdjustDC(wxDC& dc); private: - DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindow) + DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow) DECLARE_EVENT_TABLE() }; +#endif // defined(__WXMSW__) + //----------------------------------------------------------------------------- // wxDataViewRenameTimer //----------------------------------------------------------------------------- @@ -132,7 +260,8 @@ private: // wxDataViewMainWindow //----------------------------------------------------------------------------- -WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection, WXDLLIMPEXP_ADV); +WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection, + WXDLLIMPEXP_ADV); class wxDataViewMainWindow: public wxWindow { @@ -185,6 +314,7 @@ public: unsigned int GetLastVisibleRow(); unsigned int GetRowCount(); + void Select( const wxArrayInt& aSelections ); void SelectAllRows( bool on ); void SelectRow( unsigned int row, bool on ); void SelectRows( unsigned int from, unsigned int to, bool on ); @@ -259,7 +389,8 @@ public: IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase) -wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype, wxDataViewCellMode mode ) : +wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype, + wxDataViewCellMode mode ) : wxDataViewRendererBase( varianttype, mode ) { m_dc = NULL; @@ -303,7 +434,8 @@ wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype, IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer) -wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype, wxDataViewCellMode mode ) : +wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype, + wxDataViewCellMode mode ) : wxDataViewCustomRenderer( varianttype, mode ) { } @@ -320,15 +452,38 @@ bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) return false; } -bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) ) +bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state ) { + wxDataViewCtrl *view = GetOwner()->GetOwner(); + wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ? + wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) : + view->GetForegroundColour(); + + dc->SetTextForeground(col); + + // TODO: it would be much more efficient to create a clipping + // region for the entire column being rendered (in the OnPaint + // of wxDataViewMainWindow) instead of a single clip region for + // each cell. However it would mean that each renderer should + // respect the given wxRect's top & bottom coords, eventually + // violating only the left & right coords - however the user can + // make its own renderer and thus we cannot be sure of that. + dc->SetClippingRegion(cell); dc->DrawText( m_text, cell.x, cell.y ); + dc->DestroyClippingRegion(); return true; } wxSize wxDataViewTextRenderer::GetSize() { + wxDataViewCtrl *view = GetOwner()->GetOwner(); + if (!m_text.empty()) + { + int x,y; + view->GetTextExtent( m_text, &x, &y ); + return wxSize( x, y ); + } return wxSize(80,20); } @@ -338,7 +493,8 @@ wxSize wxDataViewTextRenderer::GetSize() IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer) -wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype, wxDataViewCellMode mode ) : +wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype, + wxDataViewCellMode mode ) : wxDataViewCustomRenderer( varianttype, mode ) { } @@ -428,7 +584,9 @@ bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state return true; } -bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, unsigned int col, unsigned int row ) +bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell), + wxDataViewListModel *model, + unsigned int col, unsigned int row ) { bool value = !m_toggle; wxVariant variant = value; @@ -580,7 +738,8 @@ wxSize wxDataViewDateRenderer::GetSize() return wxSize(x,y+d); } -bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, unsigned int col, unsigned int row ) +bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, + unsigned int col, unsigned int row ) { wxVariant variant; model->GetValue( variant, col, row ); @@ -605,27 +764,28 @@ bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewListMode IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase) -wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell, unsigned int model_column, - int width, int flags ) : - wxDataViewColumnBase( title, cell, model_column, width, flags ) +wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell, + unsigned int model_column, + int width, wxAlignment align, int flags ) : + wxDataViewColumnBase( title, cell, model_column, width, align, flags ) { - m_width = width; - if (m_width < 0) - m_width = 80; + m_align = align; + Init(width < 0 ? 80 : width); } -wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell, unsigned int model_column, - int width, int flags ) : - wxDataViewColumnBase( bitmap, cell, model_column, width, flags ) +wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell, + unsigned int model_column, + int width, wxAlignment align, int flags ) : + wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags ) { - m_width = width; - if (m_width < 0) - m_width = 30; + m_align = align; + Init(width < 0 ? 30 : width); } -void wxDataViewColumn::SetAlignment( wxAlignment WXUNUSED(align) ) +void wxDataViewColumn::Init(int width) { - // TODO + m_width = width; + m_fixedWidth = -1; } void wxDataViewColumn::SetSortable( bool WXUNUSED(sortable) ) @@ -633,18 +793,12 @@ void wxDataViewColumn::SetSortable( bool WXUNUSED(sortable) ) // TODO } -bool wxDataViewColumn::GetSortable() -{ - // TODO - return false; -} - void wxDataViewColumn::SetSortOrder( bool WXUNUSED(ascending) ) { // TODO } -bool wxDataViewColumn::IsSortOrderAscending() +bool wxDataViewColumn::IsSortOrderAscending() const { // TODO return true; @@ -667,30 +821,272 @@ void wxDataViewColumn::SetBitmap( const wxBitmap &bitmap ) } -int wxDataViewColumn::GetWidth() +int wxDataViewColumn::GetWidth() const { return m_width; } //----------------------------------------------------------------------------- -// wxDataViewHeaderWindow +// wxDataViewHeaderWindowBase //----------------------------------------------------------------------------- -IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow) +void wxDataViewHeaderWindowBase::SetColumnWidth(unsigned int n, int width) +{ + GetColumn(n)->m_width = width; + m_owner->m_clientArea->RecalculateDisplay(); +} + +void wxDataViewHeaderWindowBase::SendEvent(wxEventType type, unsigned int n) +{ + wxWindow *parent = GetParent(); + wxDataViewEvent le(type, parent->GetId()); + + le.SetEventObject(parent); + le.SetColumn(n); + le.SetDataViewColumn(GetColumn(n)); + le.SetModel(GetOwner()->GetModel()); + + // for events created by wxDataViewHeaderWindow the + // row / value fields are not valid + + parent->GetEventHandler()->ProcessEvent(le); +} + +#if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW + +// implemented in msw/window.cpp: +void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win); +void wxRemoveHandleAssociation(wxWindowMSW *win); + +// implemented in msw/listctrl.cpp: +unsigned int wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick); + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow); + +bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl *parent, wxWindowID id, + const wxPoint &pos, const wxSize &size, + const wxString &name ) +{ + m_owner = parent; + + if ( !CreateControl(parent, id, pos, size, 0, wxDefaultValidator, name) ) + return false; + + int x = pos.x == wxDefaultCoord ? 0 : pos.x, + y = pos.y == wxDefaultCoord ? 0 : pos.y, + w = size.x == wxDefaultCoord ? 1 : size.x, + h = size.y == wxDefaultCoord ? 22 : size.y; + + // create the native WC_HEADER window: + WXHWND hwndParent = (HWND)parent->GetHandle(); + WXDWORD msStyle = WS_CHILD | HDS_BUTTONS | HDS_HORZ | HDS_HOTTRACK | HDS_FULLDRAG; + m_hWnd = CreateWindowEx(0, + WC_HEADER, + (LPCTSTR) NULL, + msStyle, + x, y, w, h, + (HWND)hwndParent, + (HMENU)-1, + wxGetInstance(), + (LPVOID) NULL); + if (m_hWnd == NULL) + { + wxLogLastError(_T("CreateWindowEx")); + return false; + } + + // we need to do the association to force wxWindow::HandleNotify + // to call wxDataViewHeaderWindow::MSWOnNotify + wxAssociateWinWithHandle((HWND)m_hWnd, this); + + + RECT rcParent; + HDLAYOUT hdl; + WINDOWPOS wp; + + // Retrieve the bounding rectangle of the parent window's + // client area, and then request size and position values + // from the header control. + ::GetClientRect((HWND)hwndParent, &rcParent); + + hdl.prc = &rcParent; + hdl.pwpos = ℘ + if (!SendMessage((HWND)m_hWnd, HDM_LAYOUT, 0, (LPARAM) &hdl)) + { + wxLogLastError(_T("SendMessage")); + return false; + } + + // Set the size, position, and visibility of the header control. + SetWindowPos((HWND)m_hWnd, + wp.hwndInsertAfter, + wp.x, wp.y, + wp.cx, wp.cy, + wp.flags | SWP_SHOWWINDOW); + + // set our size hints: wxDataViewCtrl will put this wxWindow inside + // a wxBoxSizer and in order to avoid super-big header windows, + // we need to set our height as fixed + SetMinSize(wxSize(-1, wp.cy)); + SetMaxSize(wxSize(-1, wp.cy)); + + // the following is required to get the default win's font for header windows + SetFont(GetFont()); + + return true; +} + +wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow() +{ + wxRemoveHandleAssociation(this); +} + +void wxDataViewHeaderWindowMSW::UpdateDisplay() +{ + // remove old columns + for (int i=0, max=Header_GetItemCount((HWND)m_hWnd); i < max; i++) + Header_DeleteItem((HWND)m_hWnd, 0); + + // add the updated array of columns to the header control + unsigned int cols = GetOwner()->GetNumberOfColumns(); + for (unsigned int i = 0; i < cols; i++) + { + wxDataViewColumn *col = GetColumn( i ); + if (col->IsHidden()) + continue; // don't add it! + + HDITEM hdi; + hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH; + hdi.pszText = (WCHAR*) col->GetTitle().c_str(); + hdi.cxy = col->GetWidth(); + hdi.cchTextMax = sizeof(hdi.pszText)/sizeof(hdi.pszText[0]); + hdi.fmt = HDF_LEFT | HDF_STRING; + + SendMessage((HWND)m_hWnd, HDM_INSERTITEM, (WPARAM)i, (LPARAM)&hdi); + } +} + +bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) +{ + NMHDR *nmhdr = (NMHDR *)lParam; + + // is it a message from the header? + if ( nmhdr->hwndFrom != (HWND)m_hWnd ) + return wxWindow::MSWOnNotify(idCtrl, lParam, result); + + NMHEADER *nmHDR = (NMHEADER *)nmhdr; + switch ( nmhdr->code ) + { + case HDN_BEGINTRACK: + // user has started to resize a column: + // do we need to veto it? + if (!GetColumn(nmHDR->iItem)->IsResizeable()) + { + // veto it! + *result = TRUE; + } + break; + + case HDN_BEGINDRAG: + // user has started to reorder a column + break; + + case HDN_ITEMCHANGED: // user is resizing a column + case HDN_ENDTRACK: // user has finished resizing a column + case HDN_ENDDRAG: // user has finished reordering a column + + // update the width of the modified column: + if ((nmHDR->pitem->mask & HDI_WIDTH) != 0 && + nmHDR->pitem != NULL) + SetColumnWidth(nmHDR->iItem, nmHDR->pitem->cxy); + break; + + case HDN_ITEMCLICK: + { + wxEventType evt = nmHDR->iButton == 0 ? + wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK : + wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK; + SendEvent(evt, nmHDR->iItem); + } + break; + + case NM_RCLICK: + { + // NOTE: for some reason (i.e. for a bug in Windows) + // the HDN_ITEMCLICK notification is not sent on + // right clicks, so we need to handle NM_RCLICK + + POINT ptClick; + unsigned int column = + wxMSWGetColumnClicked(nmhdr, &ptClick); + + if (column != wxNOT_FOUND) + SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK, + column); + } + break; + + case HDN_GETDISPINFOW: + // see wxListCtrl::MSWOnNotify for more info! + break; + + case HDN_ITEMDBLCLICK: + { + unsigned int idx = nmHDR->iItem; + int w = GetOwner()->GetBestColumnWidth(idx); + + // update the native control: + HDITEM hd; + ZeroMemory(&hd, sizeof(hd)); + hd.mask = HDI_WIDTH; + hd.cxy = w; + Header_SetItem(GetHwnd(), idx, &hd); + + // update the wxDataViewColumn class: + SetColumnWidth(idx, w); + } + break; + + default: + return wxWindow::MSWOnNotify(idCtrl, lParam, result); + } + + return true; +} + +void wxDataViewHeaderWindowMSW::ScrollWindow(int WXUNUSED(dx), int WXUNUSED(dy), + const wxRect *WXUNUSED(rect)) +{ + wxSize ourSz = GetClientSize(); + wxSize ownerSz = m_owner->GetClientSize(); + + // where should the (logical) origin of this window be placed? + int x1 = 0, y1 = 0; + m_owner->CalcUnscrolledPosition(0, 0, &x1, &y1); -BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow) - EVT_PAINT (wxDataViewHeaderWindow::OnPaint) - EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse) - EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus) + // put this window on top of our parent and + SetWindowPos((HWND)m_hWnd, HWND_TOP, -x1, y1, + ownerSz.GetWidth() + x1, ourSz.GetHeight(), + SWP_SHOWWINDOW); +} + +#else // !defined(__WXMSW__) + +IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow) +BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow) + EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint) + EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse) + EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus) END_EVENT_TABLE() -wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size, const wxString &name ) : - wxWindow( parent, id, pos, size, 0, name ) +bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl *parent, wxWindowID id, + const wxPoint &pos, const wxSize &size, + const wxString &name ) { - SetOwner( parent ); + m_owner = parent; - m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE ); + if (!wxDataViewHeaderWindowBase::Create(parent, id, pos, size, name)) + return false; wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes(); SetBackgroundStyle( wxBG_STYLE_CUSTOM ); @@ -698,14 +1094,17 @@ wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindow SetOwnBackgroundColour( attr.colBg ); if (!m_hasFont) SetOwnFont( attr.font ); -} -wxDataViewHeaderWindow::~wxDataViewHeaderWindow() -{ - delete m_resizeCursor; + // set our size hints: wxDataViewCtrl will put this wxWindow inside + // a wxBoxSizer and in order to avoid super-big header windows, + // we need to set our height as fixed + SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT)); + SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT)); + + return true; } -void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) +void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { int w, h; GetClientSize( &w, &h ); @@ -731,10 +1130,11 @@ void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) int xpos = 0; for (i = 0; i < cols; i++) { - wxDataViewColumn *col = GetOwner()->GetColumn( i ); - int width = col->GetWidth(); + wxDataViewColumn *col = GetColumn( i ); + if (col->IsHidden()) + break; // don't draw it! - int cw = width; + int cw = col->GetWidth(); int ch = h; wxRendererNative::Get().DrawHeaderButton @@ -748,20 +1148,173 @@ void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) dc.DrawText( col->GetTitle(), xpos+3, 3 ); - xpos += width; + xpos += cw; } } -void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) ) +void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event ) { + GetParent()->SetFocus(); + event.Skip(); } -void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event ) +void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent &event ) { - GetParent()->SetFocus(); - event.Skip(); + // we want to work with logical coords + int x; + m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL); + int y = event.GetY(); + + if (m_isDragging) + { + // we don't draw the line beyond our window, + // but we allow dragging it there + int w = 0; + GetClientSize( &w, NULL ); + m_owner->CalcUnscrolledPosition(w, 0, &w, NULL); + w -= 6; + + // erase the line if it was drawn + if (m_currentX < w) + DrawCurrent(); + + if (event.ButtonUp()) + { + m_isDragging = false; + if (HasCapture()) + ReleaseMouse(); + + m_dirty = true; + + SetColumnWidth(m_column, m_currentX - m_minX); + + Refresh(); + GetOwner()->Refresh(); + } + else + { + m_currentX = wxMax(m_minX + 7, x); + + // draw in the new location + if (m_currentX < w) DrawCurrent(); + } + + } + else // not dragging + { + m_minX = 0; + m_column = wxNOT_FOUND; + + bool hit_border = false; + + // end of the current column + int xpos = 0; + + // find the column where this event occured + int countCol = m_owner->GetNumberOfColumns(); + for (int column = 0; column < countCol; column++) + { + wxDataViewColumn *p = GetColumn(column); + + if (p->IsHidden()) + continue; // skip if not shown + + xpos += p->GetWidth(); + m_column = column; + if ((abs(x-xpos) < 3) && (y < 22)) + { + hit_border = true; + break; + } + + if (x < xpos) + { + // inside the column + break; + } + + m_minX = xpos; + } + + if (m_column == wxNOT_FOUND) + return; + + bool resizeable = GetColumn(m_column)->IsResizeable(); + if (event.LeftDClick() && resizeable) + { + SetColumnWidth(m_column, GetOwner()->GetBestColumnWidth(m_column)); + Refresh(); + } + else if (event.LeftDown() || event.RightUp()) + { + if (hit_border && event.LeftDown() && resizeable) + { + m_isDragging = true; + CaptureMouse(); + m_currentX = x; + DrawCurrent(); + } + else // click on a column + { + wxEventType evt = event.LeftDown() ? + wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK : + wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK; + SendEvent(evt, m_column); + } + } + else if (event.Moving()) + { + if (hit_border && resizeable) + m_currentCursor = m_resizeCursor; + else + m_currentCursor = wxSTANDARD_CURSOR; + + SetCursor(*m_currentCursor); + } + } +} + +void wxGenericDataViewHeaderWindow::DrawCurrent() +{ + int x1 = m_currentX; + int y1 = 0; + ClientToScreen (&x1, &y1); + + int x2 = m_currentX-1; +#ifdef __WXMSW__ + ++x2; // but why ???? +#endif + int y2 = 0; + m_owner->GetClientSize( NULL, &y2 ); + m_owner->ClientToScreen( &x2, &y2 ); + + wxScreenDC dc; + dc.SetLogicalFunction (wxINVERT); + //dc.SetPen (wxPen (*wxBLACK, 2, wxSOLID)); + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT),1, wxSOLID)); + dc.SetBrush (*wxTRANSPARENT_BRUSH); + + AdjustDC(dc); + dc.DrawLine (x1, y1, x2, y2); + dc.SetLogicalFunction (wxCOPY); + dc.SetPen (wxNullPen); + dc.SetBrush (wxNullBrush); +} + +void wxGenericDataViewHeaderWindow::AdjustDC(wxDC& dc) +{ + int xpix, x; + + m_owner->GetScrollPixelsPerUnit( &xpix, NULL ); + m_owner->GetViewStart( &x, NULL ); + + // shift the DC origin to match the position of the main window horizontal + // scrollbar: this allows us to always use logical coords + dc.SetDeviceOrigin( -x * xpix, 0 ); } +#endif // defined(__WXMSW__) + //----------------------------------------------------------------------------- // wxDataViewRenameTimer //----------------------------------------------------------------------------- @@ -958,7 +1511,12 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i m_currentRow = 0; // TODO: we need to calculate this smartly - m_lineHeight = 20; + m_lineHeight = +#ifdef __WXMSW__ + 17; +#else + 20; +#endif m_dragCount = 0; m_dragStart = wxPoint(0,0); @@ -997,7 +1555,8 @@ void wxDataViewMainWindow::OnRenameTimer() break; xpos += c->GetWidth(); } - wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight ); + wxRect labelRect( xpos, m_currentRow * m_lineHeight, + m_currentCol->GetWidth(), m_lineHeight ); wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl); @@ -1125,22 +1684,22 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) wxDataViewListModel *model = GetOwner()->GetModel(); unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) ); - unsigned int item_count = wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1), + unsigned int item_count = + wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1), (int)(model->GetNumberOfRows()-item_start) ); - - unsigned int item; for (item = item_start; item < item_start+item_count; item++) { - if (m_selection.Index( item ) != wxNOT_FOUND) + bool selected = m_selection.Index( item ) != wxNOT_FOUND; + if (selected || item == m_currentRow) { - int flags = wxCONTROL_SELECTED; + int flags = selected ? wxCONTROL_SELECTED : 0; if (item == m_currentRow) flags |= wxCONTROL_CURRENT; if (m_hasFocus) flags |= wxCONTROL_FOCUSED; - wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 ); + wxRect rect( 0, item*m_lineHeight, GetEndOfLastCol(), m_lineHeight ); wxRendererNative::Get().DrawItemSelectionRect ( this, @@ -1149,24 +1708,6 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) flags ); } - else - { - if (item == m_currentRow) - { - int flags = wxCONTROL_CURRENT; - if (m_hasFocus) - flags |= wxCONTROL_FOCUSED; // should have no effect - wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 ); - wxRendererNative::Get().DrawItemSelectionRect - ( - this, - dc, - rect, - flags - ); - - } - } } wxRect cell_rect; @@ -1180,28 +1721,50 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) wxDataViewRenderer *cell = col->GetRenderer(); cell_rect.width = col->GetWidth(); + if (col->IsHidden()) + continue; // skipt it! + for (item = item_start; item < item_start+item_count; item++) { - cell_rect.y = item*m_lineHeight; + // get the cell value and set it into the renderer wxVariant value; model->GetValue( value, col->GetModelColumn(), item ); cell->SetValue( value ); - wxSize size = cell->GetSize(); + + // update the y offset + cell_rect.y = item*m_lineHeight; + // cannot be bigger than allocated space + wxSize size = cell->GetSize(); size.x = wxMin( size.x, cell_rect.width ); size.y = wxMin( size.y, cell_rect.height ); - // TODO: check for left/right/centre alignment here - wxRect item_rect; - // for now: centre + + wxRect item_rect(cell_rect.GetTopLeft(), size); + + // horizontal alignment: + if (col->GetAlignment() & wxALIGN_CENTER_HORIZONTAL) item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2); + else if (col->GetAlignment() & wxALIGN_RIGHT) + item_rect.x = cell_rect.x + cell_rect.width - size.x; + //else: wxALIGN_LEFT is the default + + // vertical alignment: + if (col->GetAlignment() & wxALIGN_CENTER_VERTICAL) + item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2); + else if (col->GetAlignment() & wxALIGN_BOTTOM) + item_rect.y = cell_rect.y + cell_rect.height - size.y; + //else: wxALIGN_TOP is the default + item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2); item_rect.width = size.x; item_rect.height= size.y; int state = 0; - if (item == m_currentRow) + //if (item == m_currentRow) -- seems wrong to me... + if (m_selection.Index(item) != wxNOT_FOUND) state |= wxDATAVIEW_CELL_SELECTED; + cell->Render( item_rect, &dc, state ); } @@ -1239,7 +1802,8 @@ unsigned int wxDataViewMainWindow::GetFirstVisibleRow() unsigned int wxDataViewMainWindow::GetLastVisibleRow() { wxSize client_size = GetClientSize(); - m_owner->CalcUnscrolledPosition( client_size.x, client_size.y, &client_size.x, &client_size.y ); + m_owner->CalcUnscrolledPosition( client_size.x, client_size.y, + &client_size.x, &client_size.y ); return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 ); } @@ -1329,6 +1893,17 @@ void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool RefreshRows( from, to ); } +void wxDataViewMainWindow::Select( const wxArrayInt& aSelections ) +{ + for (size_t i=0; i < aSelections.GetCount(); i++) + { + int n = aSelections[i]; + + m_selection.Add( n ); + RefreshRow( n ); + } +} + void wxDataViewMainWindow::ReverseRowSelection( unsigned int row ) { if (m_selection.Index( row ) == wxNOT_FOUND) @@ -1596,7 +2171,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) wxVariant value; model->GetValue( value, col->GetModelColumn(), current ); cell->SetValue( value ); - wxRect cell_rect( xpos, current * m_lineHeight, col->GetWidth(), m_lineHeight ); + wxRect cell_rect( xpos, current * m_lineHeight, + col->GetWidth(), m_lineHeight ); cell->Activate( cell_rect, model, col->GetModelColumn(), current ); } return; @@ -1731,7 +2307,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event ) // Update selection here... m_currentCol = col; - m_lastOnSame = !forceClick && ((col == oldCurrentCol) && (current == oldCurrentRow)) && oldWasSelected; + m_lastOnSame = !forceClick && ((col == oldCurrentCol) && + (current == oldCurrentRow)) && oldWasSelected; } } @@ -1780,7 +2357,8 @@ bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator ) { - if (!wxControl::Create( parent, id, pos, size, style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator)) + if (!wxControl::Create( parent, id, pos, size, + style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator)) return false; Init(); @@ -1790,11 +2368,7 @@ bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id, #endif m_clientArea = new wxDataViewMainWindow( this, wxID_ANY ); -#ifdef __WXMSW__ - m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,22) ); -#else - m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,25) ); -#endif + m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY ); SetTargetWindow( m_clientArea ); @@ -1859,23 +2433,24 @@ bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col ) return false; m_clientArea->UpdateDisplay(); + m_headerArea->UpdateDisplay(); return true; } -void wxDataViewCtrl::SetSelection( int WXUNUSED(row) ) +void wxDataViewCtrl::SetSelection( int row ) { - // FIXME - TODO + m_clientArea->SelectRow(row, true); } -void wxDataViewCtrl::SetSelectionRange( unsigned int WXUNUSED(from), unsigned int WXUNUSED(to) ) +void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to ) { - // FIXME - TODO + m_clientArea->SelectRows(from, to, true); } -void wxDataViewCtrl::SetSelections( const wxArrayInt& WXUNUSED(aSelections) ) +void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections ) { - // FIXME - TODO + m_clientArea->Select(aSelections); } void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) ) diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index 02e84faae9..40b69f99e9 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -726,6 +726,13 @@ public: virtual bool RowsReordered( unsigned int *new_order ); virtual bool Cleared(); + virtual bool Freed() +{ + m_wx_model = NULL; + m_gtk_store = NULL; + return wxDataViewListModelNotifier::Freed(); +} + GtkWxListStore *m_gtk_store; wxDataViewListModel *m_wx_model; }; @@ -1430,9 +1437,10 @@ static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *column, IMPLEMENT_CLASS(wxDataViewColumn, wxDataViewColumnBase) -wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell, unsigned int model_column, - int width, int flags ) : - wxDataViewColumnBase( title, cell, model_column, width, flags ) +wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell, + unsigned int model_column, int width, + wxAlignment align, int flags ) : + wxDataViewColumnBase( title, cell, model_column, width, align, flags ) { m_isConnected = false; @@ -1464,11 +1472,13 @@ wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *c gtk_tree_view_column_set_cell_data_func( column, renderer, wxGtkTreeCellDataFunc, (gpointer) cell, NULL ); + SetAlignment(align); } -wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell, unsigned int model_column, - int width, int flags ) : - wxDataViewColumnBase( bitmap, cell, model_column, width, flags ) +wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell, + unsigned int model_column, int width, + wxAlignment align, int flags ) : + wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags ) { m_isConnected = false; @@ -1497,6 +1507,8 @@ wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer * gtk_tree_view_column_set_cell_data_func( column, renderer, wxGtkTreeCellDataFunc, (gpointer) cell, NULL ); + + SetAlignment(align); } wxDataViewColumn::~wxDataViewColumn() @@ -1605,7 +1617,7 @@ void wxDataViewColumn::SetSortable( bool sortable ) gtk_tree_view_column_set_sort_indicator( column, sortable ); } -bool wxDataViewColumn::GetSortable() +bool wxDataViewColumn::IsSortable() const { GtkTreeViewColumn *column = (GtkTreeViewColumn *)m_column; return gtk_tree_view_column_get_sort_indicator( column ); @@ -1621,14 +1633,14 @@ void wxDataViewColumn::SetSortOrder( bool ascending ) gtk_tree_view_column_set_sort_order( column, GTK_SORT_DESCENDING ); } -bool wxDataViewColumn::IsSortOrderAscending() +bool wxDataViewColumn::IsSortOrderAscending() const { GtkTreeViewColumn *column = (GtkTreeViewColumn *)m_column; return (gtk_tree_view_column_get_sort_order( column ) != GTK_SORT_DESCENDING); } -int wxDataViewColumn::GetWidth() +int wxDataViewColumn::GetWidth() const { return gtk_tree_view_column_get_width( (GtkTreeViewColumn *)m_column ); } @@ -1638,7 +1650,7 @@ void wxDataViewColumn::SetFixedWidth( int width ) gtk_tree_view_column_set_fixed_width( (GtkTreeViewColumn *)m_column, width ); } -int wxDataViewColumn::GetFixedWidth() +int wxDataViewColumn::GetFixedWidth() const { return gtk_tree_view_column_get_fixed_width( (GtkTreeViewColumn *)m_column ); } @@ -1680,6 +1692,10 @@ wxDataViewCtrl::~wxDataViewCtrl() { if (m_notifier) GetModel()->RemoveNotifier( m_notifier ); + + // remove the model from the GtkTreeView before it gets destroyed by the + // wxDataViewCtrlBase's dtor + gtk_tree_view_set_model( GTK_TREE_VIEW(m_treeview), NULL ); } void wxDataViewCtrl::Init() -- 2.45.2