1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datavgen.cpp
3 // Purpose: wxDataViewCtrl generic implementation
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
13 #if wxUSE_DATAVIEWCTRL
15 #include "wx/dataview.h"
17 #ifdef wxUSE_GENERICDATAVIEWCTRL
24 #include "wx/stockitem.h"
25 #include "wx/dcclient.h"
26 #include "wx/calctrl.h"
27 #include "wx/popupwin.h"
28 #include "wx/renderer.h"
30 #include "wx/settings.h"
33 #include "wx/msw/wrapwin.h"
36 //-----------------------------------------------------------------------------
38 //-----------------------------------------------------------------------------
42 //-----------------------------------------------------------------------------
43 // wxDataViewHeaderWindow
44 //-----------------------------------------------------------------------------
46 class wxDataViewHeaderWindow
: public wxWindow
49 wxDataViewHeaderWindow( wxDataViewCtrl
*parent
,
51 const wxPoint
&pos
= wxDefaultPosition
,
52 const wxSize
&size
= wxDefaultSize
,
53 const wxString
&name
= wxT("wxdataviewctrlheaderwindow") );
54 ~wxDataViewHeaderWindow();
56 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
57 wxDataViewCtrl
*GetOwner() { return m_owner
; }
59 void OnPaint( wxPaintEvent
&event
);
60 void OnMouse( wxMouseEvent
&event
);
61 void OnSetFocus( wxFocusEvent
&event
);
64 wxDataViewCtrl
*m_owner
;
65 wxCursor
*m_resizeCursor
;
68 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindow
)
72 //-----------------------------------------------------------------------------
73 // wxDataViewRenameTimer
74 //-----------------------------------------------------------------------------
76 class wxDataViewRenameTimer
: public wxTimer
79 wxDataViewMainWindow
*m_owner
;
82 wxDataViewRenameTimer( wxDataViewMainWindow
*owner
);
86 //-----------------------------------------------------------------------------
87 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
88 //-----------------------------------------------------------------------------
90 class wxDataViewTextCtrlWrapper
: public wxEvtHandler
93 // NB: text must be a valid object but not Create()d yet
94 wxDataViewTextCtrlWrapper( wxDataViewMainWindow
*owner
,
96 wxDataViewListModel
*model
,
97 size_t col
, size_t row
,
100 wxTextCtrl
*GetText() const { return m_text
; }
102 void AcceptChangesAndFinish();
105 void OnChar( wxKeyEvent
&event
);
106 void OnKeyUp( wxKeyEvent
&event
);
107 void OnKillFocus( wxFocusEvent
&event
);
109 bool AcceptChanges();
113 wxDataViewMainWindow
*m_owner
;
115 wxString m_startValue
;
116 wxDataViewListModel
*m_model
;
120 bool m_aboutToFinish
;
122 DECLARE_EVENT_TABLE()
125 //-----------------------------------------------------------------------------
126 // wxDataViewMainWindow
127 //-----------------------------------------------------------------------------
129 WX_DEFINE_SORTED_ARRAY_SIZE_T( size_t, wxDataViewSelection
);
131 class wxDataViewMainWindow
: public wxWindow
134 wxDataViewMainWindow( wxDataViewCtrl
*parent
,
136 const wxPoint
&pos
= wxDefaultPosition
,
137 const wxSize
&size
= wxDefaultSize
,
138 const wxString
&name
= wxT("wxdataviewctrlmainwindow") );
139 ~wxDataViewMainWindow();
141 // notifications from wxDataViewListModel
144 bool RowInserted( size_t before
);
145 bool RowDeleted( size_t row
);
146 bool RowChanged( size_t row
);
147 bool ValueChanged( size_t col
, size_t row
);
148 bool RowsReordered( size_t *new_order
);
151 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
152 wxDataViewCtrl
*GetOwner() { return m_owner
; }
154 void OnPaint( wxPaintEvent
&event
);
155 void OnArrowChar(size_t newCurrent
, const wxKeyEvent
& event
);
156 void OnChar( wxKeyEvent
&event
);
157 void OnMouse( wxMouseEvent
&event
);
158 void OnSetFocus( wxFocusEvent
&event
);
159 void OnKillFocus( wxFocusEvent
&event
);
161 void UpdateDisplay();
162 void RecalculateDisplay();
163 void OnInternalIdle();
165 void OnRenameTimer();
166 void FinishEditing( wxTextCtrl
*text
);
168 void ScrollWindow( int dx
, int dy
, const wxRect
*rect
);
170 bool HasCurrentRow() { return m_currentRow
!= (size_t)-1; }
171 void ChangeCurrentRow( size_t row
);
173 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE
); };
174 bool IsEmpty() { return GetRowCount() == 0; }
176 int GetCountPerPage();
177 int GetEndOfLastCol();
178 size_t GetFirstVisibleRow();
179 size_t GetLastVisibleRow();
182 void SelectAllRows( bool on
);
183 void SelectRow( size_t row
, bool on
);
184 void SelectRows( size_t from
, size_t to
, bool on
);
185 void ReverseRowSelection( size_t row
);
186 bool IsRowSelected( size_t row
);
188 void RefreshRow( size_t row
);
189 void RefreshRows( size_t from
, size_t to
);
190 void RefreshRowsAfter( size_t firstRow
);
193 wxDataViewCtrl
*m_owner
;
197 wxDataViewColumn
*m_currentCol
;
199 wxDataViewSelection m_selection
;
201 wxDataViewRenameTimer
*m_renameTimer
;
202 wxDataViewTextCtrlWrapper
*m_textctrlWrapper
;
210 // for double click logic
211 size_t m_lineLastClicked
,
212 m_lineBeforeLastClicked
,
213 m_lineSelectSingleOnUp
;
216 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow
)
217 DECLARE_EVENT_TABLE()
220 // ---------------------------------------------------------
221 // wxGenericDataViewListModelNotifier
222 // ---------------------------------------------------------
224 class wxGenericDataViewListModelNotifier
: public wxDataViewListModelNotifier
227 wxGenericDataViewListModelNotifier( wxDataViewMainWindow
*mainWindow
)
228 { m_mainWindow
= mainWindow
; }
230 virtual bool RowAppended()
231 { return m_mainWindow
->RowAppended(); }
232 virtual bool RowPrepended()
233 { return m_mainWindow
->RowPrepended(); }
234 virtual bool RowInserted( size_t before
)
235 { return m_mainWindow
->RowInserted( before
); }
236 virtual bool RowDeleted( size_t row
)
237 { return m_mainWindow
->RowDeleted( row
); }
238 virtual bool RowChanged( size_t row
)
239 { return m_mainWindow
->RowChanged( row
); }
240 virtual bool ValueChanged( size_t col
, size_t row
)
241 { return m_mainWindow
->ValueChanged( col
, row
); }
242 virtual bool RowsReordered( size_t *new_order
)
243 { return m_mainWindow
->RowsReordered( new_order
); }
244 virtual bool Cleared()
245 { return m_mainWindow
->Cleared(); }
247 wxDataViewMainWindow
*m_mainWindow
;
250 // ---------------------------------------------------------
252 // ---------------------------------------------------------
254 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCell
, wxDataViewCellBase
)
256 wxDataViewCell
::wxDataViewCell( const wxString
&varianttype
, wxDataViewCellMode mode
) :
257 wxDataViewCellBase( varianttype
, mode
)
262 wxDataViewCell
::~wxDataViewCell()
268 wxDC
*wxDataViewCell
::GetDC()
272 if (GetOwner() == NULL
)
274 if (GetOwner()->GetOwner() == NULL
)
276 m_dc
= new wxClientDC( GetOwner()->GetOwner() );
282 // ---------------------------------------------------------
283 // wxDataViewCustomCell
284 // ---------------------------------------------------------
286 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomCell
, wxDataViewCell
)
288 wxDataViewCustomCell
::wxDataViewCustomCell( const wxString
&varianttype
,
289 wxDataViewCellMode mode
) :
290 wxDataViewCell( varianttype
, mode
)
294 // ---------------------------------------------------------
295 // wxDataViewTextCell
296 // ---------------------------------------------------------
298 IMPLEMENT_ABSTRACT_CLASS(wxDataViewTextCell
, wxDataViewCustomCell
)
300 wxDataViewTextCell
::wxDataViewTextCell( const wxString
&varianttype
, wxDataViewCellMode mode
) :
301 wxDataViewCustomCell( varianttype
, mode
)
305 bool wxDataViewTextCell
::SetValue( const wxVariant
&value
)
307 m_text
= value
.GetString();
312 bool wxDataViewTextCell
::GetValue( wxVariant
& WXUNUSED(value
) )
317 bool wxDataViewTextCell
::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
319 dc
->DrawText( m_text
, cell
.x
, cell
.y
);
324 wxSize wxDataViewTextCell
::GetSize()
326 return wxSize(80,20);
329 // ---------------------------------------------------------
330 // wxDataViewToggleCell
331 // ---------------------------------------------------------
333 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleCell
, wxDataViewCustomCell
)
335 wxDataViewToggleCell
::wxDataViewToggleCell( const wxString
&varianttype
,
336 wxDataViewCellMode mode
) :
337 wxDataViewCustomCell( varianttype
, mode
)
342 bool wxDataViewToggleCell
::SetValue( const wxVariant
&value
)
344 m_toggle
= value
.GetBool();
349 bool wxDataViewToggleCell
::GetValue( wxVariant
&WXUNUSED(value
) )
354 bool wxDataViewToggleCell
::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
356 // User wxRenderer here
359 rect
.x
= cell
.x
+ cell
.width
/2 - 10;
361 rect
.y
= cell
.y
+ cell
.height
/2 - 10;
366 flags
|= wxCONTROL_CHECKED
;
367 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE
)
368 flags
|= wxCONTROL_DISABLED
;
370 wxRendererNative
::Get().DrawCheckBox(
371 GetOwner()->GetOwner(),
379 bool wxDataViewToggleCell
::Activate( wxRect
WXUNUSED(cell
), wxDataViewListModel
*model
, size_t col
, size_t row
)
381 bool value
= !m_toggle
;
382 wxVariant variant
= value
;
383 model
->SetValue( variant
, col
, row
);
384 model
->ValueChanged( col
, row
);
388 wxSize wxDataViewToggleCell
::GetSize()
390 return wxSize(20,20);
393 // ---------------------------------------------------------
394 // wxDataViewProgressCell
395 // ---------------------------------------------------------
397 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell
, wxDataViewCustomCell
)
399 wxDataViewProgressCell
::wxDataViewProgressCell( const wxString
&label
,
400 const wxString
&varianttype
, wxDataViewCellMode mode
) :
401 wxDataViewCustomCell( varianttype
, mode
)
407 wxDataViewProgressCell
::~wxDataViewProgressCell()
411 bool wxDataViewProgressCell
::SetValue( const wxVariant
&value
)
413 m_value
= (long) value
;
415 if (m_value
< 0) m_value
= 0;
416 if (m_value
> 100) m_value
= 100;
421 bool wxDataViewProgressCell
::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
423 double pct
= (double)m_value
/ 100.0;
425 bar
.width
= (int)(cell
.width
* pct
);
426 dc
->SetPen( *wxTRANSPARENT_PEN
);
427 dc
->SetBrush( *wxBLUE_BRUSH
);
428 dc
->DrawRectangle( bar
);
430 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
431 dc
->SetPen( *wxBLACK_PEN
);
432 dc
->DrawRectangle( cell
);
437 wxSize wxDataViewProgressCell
::GetSize()
439 return wxSize(40,12);
442 // ---------------------------------------------------------
443 // wxDataViewDateCell
444 // ---------------------------------------------------------
446 class wxDataViewDateCellPopupTransient
: public wxPopupTransientWindow
449 wxDataViewDateCellPopupTransient( wxWindow
* parent
, wxDateTime
*value
,
450 wxDataViewListModel
*model
, size_t col
, size_t row
) :
451 wxPopupTransientWindow( parent
, wxBORDER_SIMPLE
)
456 m_cal
= new wxCalendarCtrl( this, wxID_ANY
, *value
);
457 wxBoxSizer
*sizer
= new wxBoxSizer( wxHORIZONTAL
);
458 sizer
->Add( m_cal
, 1, wxGROW
);
463 void OnCalendar( wxCalendarEvent
&event
);
465 wxCalendarCtrl
*m_cal
;
466 wxDataViewListModel
*m_model
;
471 virtual void OnDismiss()
476 DECLARE_EVENT_TABLE()
479 BEGIN_EVENT_TABLE(wxDataViewDateCellPopupTransient
,wxPopupTransientWindow
)
480 EVT_CALENDAR( wxID_ANY
, wxDataViewDateCellPopupTransient
::OnCalendar
)
483 void wxDataViewDateCellPopupTransient
::OnCalendar( wxCalendarEvent
&event
)
485 wxDateTime date
= event
.GetDate();
486 wxVariant value
= date
;
487 m_model
->SetValue( value
, m_col
, m_row
);
488 m_model
->ValueChanged( m_col
, m_row
);
492 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateCell
, wxDataViewCustomCell
)
494 wxDataViewDateCell
::wxDataViewDateCell( const wxString
&varianttype
,
495 wxDataViewCellMode mode
) :
496 wxDataViewCustomCell( varianttype
, mode
)
500 bool wxDataViewDateCell
::SetValue( const wxVariant
&value
)
502 m_date
= value
.GetDateTime();
507 bool wxDataViewDateCell
::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
509 dc
->SetFont( GetOwner()->GetOwner()->GetFont() );
510 wxString tmp
= m_date
.FormatDate();
511 dc
->DrawText( tmp
, cell
.x
, cell
.y
);
516 wxSize wxDataViewDateCell
::GetSize()
518 wxDataViewCtrl
* view
= GetOwner()->GetOwner();
519 wxString tmp
= m_date
.FormatDate();
521 view
->GetTextExtent( tmp
, &x
, &y
, &d
);
522 return wxSize(x
,y
+d
);
525 bool wxDataViewDateCell
::Activate( wxRect
WXUNUSED(cell
), wxDataViewListModel
*model
, size_t col
, size_t row
)
528 model
->GetValue( variant
, col
, row
);
529 wxDateTime value
= variant
.GetDateTime();
531 wxDataViewDateCellPopupTransient
*popup
= new wxDataViewDateCellPopupTransient(
532 GetOwner()->GetOwner()->GetParent(), &value
, model
, col
, row
);
533 wxPoint pos
= wxGetMousePosition();
536 popup
->Popup( popup
->m_cal
);
541 // ---------------------------------------------------------
543 // ---------------------------------------------------------
545 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn
, wxDataViewColumnBase
)
547 wxDataViewColumn
::wxDataViewColumn( const wxString
&title
, wxDataViewCell
*cell
, size_t model_column
,
548 int fixed_width
, wxDataViewColumnSizing sizing
, int flags
) :
549 wxDataViewColumnBase( title
, cell
, model_column
, flags
)
553 m_width
= fixed_width
;
554 m_fixedWidth
= fixed_width
;
557 wxDataViewColumn
::~wxDataViewColumn()
561 void wxDataViewColumn
::SetTitle( const wxString
&title
)
563 wxDataViewColumnBase
::SetTitle( title
);
567 int wxDataViewColumn
::GetWidth()
572 void wxDataViewColumn
::SetFixedWidth( int width
)
574 m_fixedWidth
= width
;
576 if (m_sizing
== wxDATAVIEW_COL_WIDTH_FIXED
)
583 int wxDataViewColumn
::GetFixedWidth()
588 //-----------------------------------------------------------------------------
589 // wxDataViewHeaderWindow
590 //-----------------------------------------------------------------------------
592 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow
, wxWindow
)
594 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow
,wxWindow
)
595 EVT_PAINT (wxDataViewHeaderWindow
::OnPaint
)
596 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow
::OnMouse
)
597 EVT_SET_FOCUS (wxDataViewHeaderWindow
::OnSetFocus
)
600 wxDataViewHeaderWindow
::wxDataViewHeaderWindow( wxDataViewCtrl
*parent
, wxWindowID id
,
601 const wxPoint
&pos
, const wxSize
&size
, const wxString
&name
) :
602 wxWindow( parent
, id
, pos
, size
, 0, name
)
606 m_resizeCursor
= new wxCursor( wxCURSOR_SIZEWE
);
608 wxVisualAttributes attr
= wxPanel
::GetClassDefaultAttributes();
609 SetOwnForegroundColour( attr
.colFg
);
610 SetOwnBackgroundColour( attr
.colBg
);
612 SetOwnFont( attr
.font
);
615 wxDataViewHeaderWindow
::~wxDataViewHeaderWindow()
617 delete m_resizeCursor
;
620 void wxDataViewHeaderWindow
::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
623 GetClientSize( &w
, &h
);
625 wxPaintDC
dc( this );
628 m_owner
->GetScrollPixelsPerUnit( &xpix
, NULL
);
631 m_owner
->GetViewStart( &x
, NULL
);
633 // account for the horz scrollbar offset
634 dc
.SetDeviceOrigin( -x
* xpix
, 0 );
636 dc
.SetFont( GetFont() );
638 size_t cols
= GetOwner()->GetNumberOfColumns();
641 for (i
= 0; i
< cols
; i
++)
643 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
644 int width
= col
->GetWidth();
649 wxRendererNative
::Get().DrawHeaderButton
653 wxRect(xpos
, 0, cw
, ch
-1),
654 m_parent
->IsEnabled() ?
0
655 : (int)wxCONTROL_DISABLED
658 dc
.DrawText( col
->GetTitle(), xpos
+3, 3 );
664 void wxDataViewHeaderWindow
::OnMouse( wxMouseEvent
&WXUNUSED(event
) )
668 void wxDataViewHeaderWindow
::OnSetFocus( wxFocusEvent
&event
)
670 GetParent()->SetFocus();
674 //-----------------------------------------------------------------------------
675 // wxDataViewRenameTimer
676 //-----------------------------------------------------------------------------
678 wxDataViewRenameTimer
::wxDataViewRenameTimer( wxDataViewMainWindow
*owner
)
683 void wxDataViewRenameTimer
::Notify()
685 m_owner
->OnRenameTimer();
688 //-----------------------------------------------------------------------------
689 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
690 //-----------------------------------------------------------------------------
692 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper
, wxEvtHandler
)
693 EVT_CHAR (wxDataViewTextCtrlWrapper
::OnChar
)
694 EVT_KEY_UP (wxDataViewTextCtrlWrapper
::OnKeyUp
)
695 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper
::OnKillFocus
)
698 wxDataViewTextCtrlWrapper
::wxDataViewTextCtrlWrapper(
699 wxDataViewMainWindow
*owner
,
701 wxDataViewListModel
*model
,
702 size_t col
, size_t row
,
712 m_aboutToFinish
= false;
715 model
->GetValue( value
, col
, row
);
716 m_startValue
= value
.GetString();
718 m_owner
->GetOwner()->CalcScrolledPosition(
719 rectLabel
.x
, rectLabel
.y
, &rectLabel
.x
, &rectLabel
.y
);
721 m_text
->Create( owner
, wxID_ANY
, m_startValue
,
722 wxPoint(rectLabel
.x
-2,rectLabel
.y
-2),
723 wxSize(rectLabel
.width
+7,rectLabel
.height
+4) );
726 m_text
->PushEventHandler(this);
729 void wxDataViewTextCtrlWrapper
::AcceptChangesAndFinish()
731 m_aboutToFinish
= true;
733 // Notify the owner about the changes
736 // Even if vetoed, close the control (consistent with MSW)
740 void wxDataViewTextCtrlWrapper
::OnChar( wxKeyEvent
&event
)
742 switch ( event
.m_keyCode
)
745 AcceptChangesAndFinish();
749 // m_owner->OnRenameCancelled( m_itemEdited );
758 void wxDataViewTextCtrlWrapper
::OnKeyUp( wxKeyEvent
&event
)
766 // auto-grow the textctrl
767 wxSize parentSize
= m_owner
->GetSize();
768 wxPoint myPos
= m_text
->GetPosition();
769 wxSize mySize
= m_text
->GetSize();
771 m_text
->GetTextExtent(m_text
->GetValue() + _T("MM"), &sx
, &sy
);
772 if (myPos
.x
+ sx
> parentSize
.x
)
773 sx
= parentSize
.x
- myPos
.x
;
776 m_text
->SetSize(sx
, wxDefaultCoord
);
781 void wxDataViewTextCtrlWrapper
::OnKillFocus( wxFocusEvent
&event
)
783 if ( !m_finished
&& !m_aboutToFinish
)
786 //if ( !AcceptChanges() )
787 // m_owner->OnRenameCancelled( m_itemEdited );
792 // We must let the native text control handle focus
796 bool wxDataViewTextCtrlWrapper
::AcceptChanges()
798 const wxString value
= m_text
->GetValue();
800 if ( value
== m_startValue
)
801 // nothing changed, always accept
804 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
805 // vetoed by the user
808 // accepted, do rename the item
811 m_model
->SetValue( variant
, m_col
, m_row
);
812 m_model
->ValueChanged( m_col
, m_row
);
817 void wxDataViewTextCtrlWrapper
::Finish()
823 m_text
->RemoveEventHandler(this);
824 m_owner
->FinishEditing(m_text
);
827 wxPendingDelete
.Append( this );
831 //-----------------------------------------------------------------------------
832 // wxDataViewMainWindow
833 //-----------------------------------------------------------------------------
835 int LINKAGEMODE
wxDataViewSelectionCmp( size_t row1
, size_t row2
)
837 if (row1
> row2
) return 1;
838 if (row1
== row2
) return 0;
843 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow
, wxWindow
)
845 BEGIN_EVENT_TABLE(wxDataViewMainWindow
,wxWindow
)
846 EVT_PAINT (wxDataViewMainWindow
::OnPaint
)
847 EVT_MOUSE_EVENTS (wxDataViewMainWindow
::OnMouse
)
848 EVT_SET_FOCUS (wxDataViewMainWindow
::OnSetFocus
)
849 EVT_KILL_FOCUS (wxDataViewMainWindow
::OnKillFocus
)
850 EVT_CHAR (wxDataViewMainWindow
::OnChar
)
853 wxDataViewMainWindow
::wxDataViewMainWindow( wxDataViewCtrl
*parent
, wxWindowID id
,
854 const wxPoint
&pos
, const wxSize
&size
, const wxString
&name
) :
855 wxWindow( parent
, id
, pos
, size
, wxWANTS_CHARS
, name
),
856 m_selection( wxDataViewSelectionCmp
)
861 m_lastOnSame
= false;
862 m_renameTimer
= new wxDataViewRenameTimer( this );
863 m_textctrlWrapper
= NULL
;
865 // TODO: user better initial values/nothing selected
869 // TODO: we need to calculate this smartly
873 m_dragStart
= wxPoint(0,0);
874 m_lineLastClicked
= (size_t) -1;
875 m_lineBeforeLastClicked
= (size_t) -1;
876 m_lineSelectSingleOnUp
= (size_t) -1;
880 SetBackgroundColour( *wxWHITE
);
885 wxDataViewMainWindow
::~wxDataViewMainWindow()
887 delete m_renameTimer
;
890 void wxDataViewMainWindow
::OnRenameTimer()
892 // We have to call this here because changes may just have
893 // been made and no screen update taken place.
899 size_t cols
= GetOwner()->GetNumberOfColumns();
901 for (i
= 0; i
< cols
; i
++)
903 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
904 if (c
== m_currentCol
)
906 xpos
+= c
->GetWidth();
908 wxRect
labelRect( xpos
, m_currentRow
* m_lineHeight
, m_currentCol
->GetWidth(), m_lineHeight
);
910 wxClassInfo
*textControlClass
= CLASSINFO(wxTextCtrl
);
912 wxTextCtrl
* const text
= (wxTextCtrl
*)textControlClass
->CreateObject();
913 m_textctrlWrapper
= new wxDataViewTextCtrlWrapper(this, text
, GetOwner()->GetModel(),
914 m_currentCol
->GetModelColumn(), m_currentRow
, labelRect
);
917 void wxDataViewMainWindow
::FinishEditing( wxTextCtrl
*text
)
920 m_textctrlWrapper
= NULL
;
922 // SetFocusIgnoringChildren();
925 bool wxDataViewMainWindow
::RowAppended()
930 bool wxDataViewMainWindow
::RowPrepended()
935 bool wxDataViewMainWindow
::RowInserted( size_t WXUNUSED(before
) )
940 bool wxDataViewMainWindow
::RowDeleted( size_t WXUNUSED(row
) )
945 bool wxDataViewMainWindow
::RowChanged( size_t WXUNUSED(row
) )
950 bool wxDataViewMainWindow
::ValueChanged( size_t WXUNUSED(col
), size_t row
)
952 wxRect
rect( 0, row
*m_lineHeight
, 10000, m_lineHeight
);
953 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
954 Refresh( true, &rect
);
959 bool wxDataViewMainWindow
::RowsReordered( size_t *WXUNUSED(new_order
) )
966 bool wxDataViewMainWindow
::Cleared()
971 void wxDataViewMainWindow
::UpdateDisplay()
976 void wxDataViewMainWindow
::OnInternalIdle()
978 wxWindow
::OnInternalIdle();
982 RecalculateDisplay();
987 void wxDataViewMainWindow
::RecalculateDisplay()
989 wxDataViewListModel
*model
= GetOwner()->GetModel();
997 size_t cols
= GetOwner()->GetNumberOfColumns();
999 for (i
= 0; i
< cols
; i
++)
1001 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
1002 width
+= col
->GetWidth();
1005 int height
= model
->GetNumberOfRows() * m_lineHeight
;
1007 SetVirtualSize( width
, height
);
1008 GetOwner()->SetScrollRate( 10, m_lineHeight
);
1013 void wxDataViewMainWindow
::ScrollWindow( int dx
, int dy
, const wxRect
*rect
)
1015 wxWindow
::ScrollWindow( dx
, dy
, rect
);
1016 GetOwner()->m_headerArea
->ScrollWindow( dx
, 0 );
1019 void wxDataViewMainWindow
::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
1021 wxPaintDC
dc( this );
1023 GetOwner()->PrepareDC( dc
);
1025 dc
.SetFont( GetFont() );
1027 wxRect update
= GetUpdateRegion().GetBox();
1028 m_owner
->CalcUnscrolledPosition( update
.x
, update
.y
, &update
.x
, &update
.y
);
1030 wxDataViewListModel
*model
= GetOwner()->GetModel();
1032 size_t item_start
= wxMax( 0, (update
.y
/ m_lineHeight
) );
1033 size_t item_count
= wxMin( (int)(((update
.y
+ update
.height
) / m_lineHeight
) - item_start
+ 1),
1034 (int)(model
->GetNumberOfRows()-item_start
) );
1039 for (item
= item_start
; item
< item_start
+item_count
; item
++)
1041 if (m_selection
.Index( item
) != wxNOT_FOUND
)
1043 int flags
= wxCONTROL_SELECTED
;
1044 if (item
== m_currentRow
)
1045 flags
|= wxCONTROL_CURRENT
;
1047 flags
|= wxCONTROL_FOCUSED
;
1048 wxRect
rect( 0, item
*m_lineHeight
+1, GetEndOfLastCol(), m_lineHeight
-2 );
1049 wxRendererNative
::Get().DrawItemSelectionRect
1059 if (item
== m_currentRow
)
1061 int flags
= wxCONTROL_CURRENT
;
1063 flags
|= wxCONTROL_FOCUSED
; // should have no effect
1064 wxRect
rect( 0, item
*m_lineHeight
+1, GetEndOfLastCol(), m_lineHeight
-2 );
1065 wxRendererNative
::Get().DrawItemSelectionRect
1079 cell_rect
.height
= m_lineHeight
;
1080 size_t cols
= GetOwner()->GetNumberOfColumns();
1082 for (i
= 0; i
< cols
; i
++)
1084 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
1085 wxDataViewCell
*cell
= col
->GetCell();
1086 cell_rect
.width
= col
->GetWidth();
1088 for (item
= item_start
; item
< item_start
+item_count
; item
++)
1090 cell_rect
.y
= item
*m_lineHeight
;
1092 model
->GetValue( value
, col
->GetModelColumn(), item
);
1093 cell
->SetValue( value
);
1094 wxSize size
= cell
->GetSize();
1095 // cannot be bigger than allocated space
1096 size
.x
= wxMin( size
.x
, cell_rect
.width
);
1097 size
.y
= wxMin( size
.y
, cell_rect
.height
);
1098 // TODO: check for left/right/centre alignment here
1101 item_rect
.x
= cell_rect
.x
+ (cell_rect
.width
/ 2) - (size
.x
/ 2);
1102 item_rect
.y
= cell_rect
.y
+ (cell_rect
.height
/ 2) - (size
.y
/ 2);
1104 item_rect
.width
= size
.x
;
1105 item_rect
.height
= size
.y
;
1106 cell
->Render( item_rect
, &dc
, 0 );
1109 cell_rect
.x
+= cell_rect
.width
;
1113 int wxDataViewMainWindow
::GetCountPerPage()
1115 wxSize size
= GetClientSize();
1116 return size
.y
/ m_lineHeight
;
1119 int wxDataViewMainWindow
::GetEndOfLastCol()
1123 for (i
= 0; i
< GetOwner()->GetNumberOfColumns(); i
++)
1125 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
1126 width
+= c
->GetWidth();
1131 size_t wxDataViewMainWindow
::GetFirstVisibleRow()
1135 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
1137 return y
/ m_lineHeight
;
1140 size_t wxDataViewMainWindow
::GetLastVisibleRow()
1142 wxSize client_size
= GetClientSize();
1143 m_owner
->CalcUnscrolledPosition( client_size
.x
, client_size
.y
, &client_size
.x
, &client_size
.y
);
1145 return wxMin( GetRowCount()-1, (client_size
.y
/m_lineHeight
)+1 );
1148 int wxDataViewMainWindow
::GetRowCount()
1150 return GetOwner()->GetModel()->GetNumberOfRows();
1153 void wxDataViewMainWindow
::ChangeCurrentRow( size_t row
)
1160 void wxDataViewMainWindow
::SelectAllRows( bool on
)
1162 if (GetRowCount() == 0) return;
1166 m_selection
.Clear();
1168 for (i
= 0; i
< GetRowCount(); i
++)
1169 m_selection
.Add( i
);
1174 size_t first_visible
= GetFirstVisibleRow();
1175 size_t last_visible
= GetLastVisibleRow();
1177 for (i
= 0; i
< m_selection
.GetCount(); i
++)
1179 size_t row
= m_selection
[i
];
1180 if ((row
>= first_visible
) && (row
<= last_visible
))
1183 m_selection
.Clear();
1187 void wxDataViewMainWindow
::SelectRow( size_t row
, bool on
)
1189 if (m_selection
.Index( row
) == wxNOT_FOUND
)
1193 m_selection
.Add( row
);
1201 m_selection
.Remove( row
);
1207 void wxDataViewMainWindow
::SelectRows( size_t from
, size_t to
, bool on
)
1217 for (i
= from
; i
<= to
; i
++)
1219 if (m_selection
.Index( i
) == wxNOT_FOUND
)
1222 m_selection
.Add( i
);
1227 m_selection
.Remove( i
);
1230 RefreshRows( from
, to
);
1233 void wxDataViewMainWindow
::ReverseRowSelection( size_t row
)
1235 if (m_selection
.Index( row
) == wxNOT_FOUND
)
1236 m_selection
.Add( row
);
1238 m_selection
.Remove( row
);
1242 bool wxDataViewMainWindow
::IsRowSelected( size_t row
)
1244 return (m_selection
.Index( row
) != wxNOT_FOUND
);
1247 void wxDataViewMainWindow
::RefreshRow( size_t row
)
1249 wxRect
rect( 0, row
*m_lineHeight
, GetEndOfLastCol(), m_lineHeight
);
1250 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1252 wxSize client_size
= GetClientSize();
1253 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1254 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1255 if (intersect_rect
.width
> 0)
1256 Refresh( true, &intersect_rect
);
1259 void wxDataViewMainWindow
::RefreshRows( size_t from
, size_t to
)
1268 wxRect
rect( 0, from
*m_lineHeight
, GetEndOfLastCol(), (to
-from
+1) * m_lineHeight
);
1269 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1271 wxSize client_size
= GetClientSize();
1272 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1273 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1274 if (intersect_rect
.width
> 0)
1275 Refresh( true, &intersect_rect
);
1278 void wxDataViewMainWindow
::RefreshRowsAfter( size_t firstRow
)
1280 size_t count
= GetRowCount();
1281 if (firstRow
> count
) return;
1283 wxRect
rect( 0, firstRow
*m_lineHeight
, GetEndOfLastCol(), count
* m_lineHeight
);
1284 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1286 wxSize client_size
= GetClientSize();
1287 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1288 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1289 if (intersect_rect
.width
> 0)
1290 Refresh( true, &intersect_rect
);
1293 void wxDataViewMainWindow
::OnArrowChar(size_t newCurrent
, const wxKeyEvent
& event
)
1295 wxCHECK_RET( newCurrent
< (size_t)GetRowCount(),
1296 _T("invalid item index in OnArrowChar()") );
1298 // if there is no selection, we cannot move it anywhere
1299 if (!HasCurrentRow())
1302 size_t oldCurrent
= m_currentRow
;
1304 // in single selection we just ignore Shift as we can't select several
1306 if ( event
.ShiftDown() && !IsSingleSel() )
1308 RefreshRow( oldCurrent
);
1310 ChangeCurrentRow( newCurrent
);
1312 // select all the items between the old and the new one
1313 if ( oldCurrent
> newCurrent
)
1315 newCurrent
= oldCurrent
;
1316 oldCurrent
= m_currentRow
;
1319 SelectRows( oldCurrent
, newCurrent
, true );
1323 RefreshRow( oldCurrent
);
1325 // all previously selected items are unselected unless ctrl is held
1326 if ( !event
.ControlDown() )
1327 SelectAllRows(false);
1329 ChangeCurrentRow( newCurrent
);
1331 if ( !event
.ControlDown() )
1332 SelectRow( m_currentRow
, true );
1334 RefreshRow( m_currentRow
);
1340 void wxDataViewMainWindow
::OnChar( wxKeyEvent
&event
)
1342 if (event
.GetKeyCode() == WXK_TAB
)
1344 wxNavigationKeyEvent nevent
;
1345 nevent
.SetWindowChange( event
.ControlDown() );
1346 nevent
.SetDirection( !event
.ShiftDown() );
1347 nevent
.SetEventObject( GetParent()->GetParent() );
1348 nevent
.SetCurrentFocus( m_parent
);
1349 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent
))
1353 // no item -> nothing to do
1354 if (!HasCurrentRow())
1360 // don't use m_linesPerPage directly as it might not be computed yet
1361 const int pageSize
= GetCountPerPage();
1362 wxCHECK_RET( pageSize
, _T("should have non zero page size") );
1364 switch ( event
.GetKeyCode() )
1367 if ( m_currentRow
> 0 )
1368 OnArrowChar( m_currentRow
- 1, event
);
1372 if ( m_currentRow
< (size_t)GetRowCount() - 1 )
1373 OnArrowChar( m_currentRow
+ 1, event
);
1378 OnArrowChar( GetRowCount() - 1, event
);
1383 OnArrowChar( 0, event
);
1388 int steps
= pageSize
- 1;
1389 int index
= m_currentRow
- steps
;
1393 OnArrowChar( index
, event
);
1399 int steps
= pageSize
- 1;
1400 size_t index
= m_currentRow
+ steps
;
1401 size_t count
= GetRowCount();
1402 if ( index
>= count
)
1405 OnArrowChar( index
, event
);
1414 void wxDataViewMainWindow
::OnMouse( wxMouseEvent
&event
)
1416 if (event
.GetEventType() == wxEVT_MOUSEWHEEL
)
1418 // let the base handle mouse wheel events.
1423 int x
= event
.GetX();
1424 int y
= event
.GetY();
1425 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
1427 wxDataViewColumn
*col
= NULL
;
1430 size_t cols
= GetOwner()->GetNumberOfColumns();
1432 for (i
= 0; i
< cols
; i
++)
1434 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
1435 if (x
< xpos
+ c
->GetWidth())
1440 xpos
+= c
->GetWidth();
1444 wxDataViewCell
*cell
= col
->GetCell();
1446 size_t current
= y
/ m_lineHeight
;
1448 if ((current
> GetRowCount()) || (x
> GetEndOfLastCol()))
1450 // Unselect all if below the last row ?
1454 wxDataViewListModel
*model
= GetOwner()->GetModel();
1456 if (event
.Dragging())
1458 if (m_dragCount
== 0)
1460 // we have to report the raw, physical coords as we want to be
1461 // able to call HitTest(event.m_pointDrag) from the user code to
1462 // get the item being dragged
1463 m_dragStart
= event
.GetPosition();
1468 if (m_dragCount
!= 3)
1471 if (event
.LeftIsDown())
1473 // Notify cell about drag
1482 bool forceClick
= false;
1484 if (event
.ButtonDClick())
1486 m_renameTimer
->Stop();
1487 m_lastOnSame
= false;
1490 if (event
.LeftDClick())
1492 if ( current
== m_lineLastClicked
)
1494 if (cell
->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE
)
1497 model
->GetValue( value
, col
->GetModelColumn(), current
);
1498 cell
->SetValue( value
);
1499 wxRect
cell_rect( xpos
, current
* m_lineHeight
, col
->GetWidth(), m_lineHeight
);
1500 cell
->Activate( cell_rect
, model
, col
->GetModelColumn(), current
);
1506 // The first click was on another item, so don't interpret this as
1507 // a double click, but as a simple click instead
1514 if (m_lineSelectSingleOnUp
!= (size_t)-1)
1516 // select single line
1517 SelectAllRows( false );
1518 SelectRow( m_lineSelectSingleOnUp
, true );
1523 if ((col
== m_currentCol
) && (current
== m_currentRow
) &&
1524 (cell
->GetMode() == wxDATAVIEW_CELL_EDITABLE
) )
1526 m_renameTimer
->Start( 100, true );
1530 m_lastOnSame
= false;
1531 m_lineSelectSingleOnUp
= (size_t)-1;
1535 // This is necessary, because after a DnD operation in
1536 // from and to ourself, the up event is swallowed by the
1537 // DnD code. So on next non-up event (which means here and
1538 // now) m_lineSelectSingleOnUp should be reset.
1539 m_lineSelectSingleOnUp
= (size_t)-1;
1542 if (event
.RightDown())
1544 m_lineBeforeLastClicked
= m_lineLastClicked
;
1545 m_lineLastClicked
= current
;
1547 // If the item is already selected, do not update the selection.
1548 // Multi-selections should not be cleared if a selected item is clicked.
1549 if (!IsRowSelected(current
))
1551 SelectAllRows(false);
1552 ChangeCurrentRow(current
);
1553 SelectRow(m_currentRow
,true);
1556 // notify cell about right click
1559 // Allow generation of context menu event
1562 else if (event
.MiddleDown())
1564 // notify cell about middle click
1567 if (event
.LeftDown() || forceClick
)
1573 m_lineBeforeLastClicked
= m_lineLastClicked
;
1574 m_lineLastClicked
= current
;
1576 size_t oldCurrentRow
= m_currentRow
;
1577 bool oldWasSelected
= IsRowSelected(m_currentRow
);
1579 bool cmdModifierDown
= event
.CmdDown();
1580 if ( IsSingleSel() || !(cmdModifierDown
|| event
.ShiftDown()) )
1582 if ( IsSingleSel() || !IsRowSelected(current
) )
1584 SelectAllRows( false );
1586 ChangeCurrentRow(current
);
1588 SelectRow(m_currentRow
,true);
1590 else // multi sel & current is highlighted & no mod keys
1592 m_lineSelectSingleOnUp
= current
;
1593 ChangeCurrentRow(current
); // change focus
1596 else // multi sel & either ctrl or shift is down
1598 if (cmdModifierDown
)
1600 ChangeCurrentRow(current
);
1602 ReverseRowSelection(m_currentRow
);
1604 else if (event
.ShiftDown())
1606 ChangeCurrentRow(current
);
1608 size_t lineFrom
= oldCurrentRow
,
1611 if ( lineTo
< lineFrom
)
1614 lineFrom
= m_currentRow
;
1617 SelectRows(lineFrom
, lineTo
, true);
1619 else // !ctrl, !shift
1621 // test in the enclosing if should make it impossible
1622 wxFAIL_MSG( _T("how did we get here?") );
1626 if (m_currentRow
!= oldCurrentRow
)
1627 RefreshRow( oldCurrentRow
);
1629 wxDataViewColumn
*oldCurrentCol
= m_currentCol
;
1631 // Update selection here...
1634 m_lastOnSame
= !forceClick
&& ((col
== oldCurrentCol
) && (current
== oldCurrentRow
)) && oldWasSelected
;
1638 void wxDataViewMainWindow
::OnSetFocus( wxFocusEvent
&event
)
1642 if (HasCurrentRow())
1648 void wxDataViewMainWindow
::OnKillFocus( wxFocusEvent
&event
)
1652 if (HasCurrentRow())
1658 //-----------------------------------------------------------------------------
1660 //-----------------------------------------------------------------------------
1662 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl
, wxDataViewCtrlBase
)
1664 BEGIN_EVENT_TABLE(wxDataViewCtrl
, wxDataViewCtrlBase
)
1665 EVT_SIZE(wxDataViewCtrl
::OnSize
)
1668 wxDataViewCtrl
::~wxDataViewCtrl()
1671 GetModel()->RemoveNotifier( m_notifier
);
1674 void wxDataViewCtrl
::Init()
1679 bool wxDataViewCtrl
::Create(wxWindow
*parent
, wxWindowID id
,
1680 const wxPoint
& pos
, const wxSize
& size
,
1681 long style
, const wxValidator
& validator
)
1683 if (!wxControl
::Create( parent
, id
, pos
, size
, style
| wxScrolledWindowStyle
|wxSUNKEN_BORDER
, validator
))
1689 MacSetClipChildren( true ) ;
1692 m_clientArea
= new wxDataViewMainWindow( this, wxID_ANY
);
1694 m_headerArea
= new wxDataViewHeaderWindow( this, wxID_ANY
, wxDefaultPosition
, wxSize(wxDefaultCoord
,22) );
1696 m_headerArea
= new wxDataViewHeaderWindow( this, wxID_ANY
, wxDefaultPosition
, wxSize(wxDefaultCoord
,25) );
1699 SetTargetWindow( m_clientArea
);
1701 wxBoxSizer
*sizer
= new wxBoxSizer( wxVERTICAL
);
1702 sizer
->Add( m_headerArea
, 0, wxGROW
);
1703 sizer
->Add( m_clientArea
, 1, wxGROW
);
1710 WXLRESULT wxDataViewCtrl
::MSWWindowProc(WXUINT nMsg
,
1714 WXLRESULT rc
= wxDataViewCtrlBase
::MSWWindowProc(nMsg
, wParam
, lParam
);
1717 // we need to process arrows ourselves for scrolling
1718 if ( nMsg
== WM_GETDLGCODE
)
1720 rc
|= DLGC_WANTARROWS
;
1728 void wxDataViewCtrl
::OnSize( wxSizeEvent
&WXUNUSED(event
) )
1730 // We need to override OnSize so that our scrolled
1731 // window a) does call Layout() to use sizers for
1732 // positioning the controls but b) does not query
1733 // the sizer for their size and use that for setting
1734 // the scrollable area as set that ourselves by
1735 // calling SetScrollbar() further down.
1742 bool wxDataViewCtrl
::AssociateModel( wxDataViewListModel
*model
)
1744 if (!wxDataViewCtrlBase
::AssociateModel( model
))
1747 m_notifier
= new wxGenericDataViewListModelNotifier( m_clientArea
);
1749 model
->AddNotifier( m_notifier
);
1751 m_clientArea
->UpdateDisplay();
1756 bool wxDataViewCtrl
::AppendColumn( wxDataViewColumn
*col
)
1758 if (!wxDataViewCtrlBase
::AppendColumn(col
))
1761 m_clientArea
->UpdateDisplay();
1767 // !wxUSE_GENERICDATAVIEWCTRL
1770 // wxUSE_DATAVIEWCTRL