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"
17 #if wxUSE_DATAVIEWCTRL
19 #include "wx/dataview.h"
21 #ifdef wxUSE_GENERICDATAVIEWCTRL
25 #include "wx/msw/wrapwin.h"
29 #include "wx/dcclient.h"
31 #include "wx/settings.h"
34 #include "wx/stockitem.h"
35 #include "wx/calctrl.h"
36 #include "wx/popupwin.h"
37 #include "wx/renderer.h"
39 //-----------------------------------------------------------------------------
41 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
46 // wxDataViewHeaderWindow
47 //-----------------------------------------------------------------------------
49 class wxDataViewHeaderWindow
: public wxWindow
52 wxDataViewHeaderWindow( wxDataViewCtrl
*parent
,
54 const wxPoint
&pos
= wxDefaultPosition
,
55 const wxSize
&size
= wxDefaultSize
,
56 const wxString
&name
= wxT("wxdataviewctrlheaderwindow") );
57 virtual ~wxDataViewHeaderWindow();
59 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
60 wxDataViewCtrl
*GetOwner() { return m_owner
; }
62 void OnPaint( wxPaintEvent
&event
);
63 void OnMouse( wxMouseEvent
&event
);
64 void OnSetFocus( wxFocusEvent
&event
);
67 wxDataViewCtrl
*m_owner
;
68 wxCursor
*m_resizeCursor
;
71 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindow
)
75 //-----------------------------------------------------------------------------
76 // wxDataViewRenameTimer
77 //-----------------------------------------------------------------------------
79 class wxDataViewRenameTimer
: public wxTimer
82 wxDataViewMainWindow
*m_owner
;
85 wxDataViewRenameTimer( wxDataViewMainWindow
*owner
);
89 //-----------------------------------------------------------------------------
90 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
91 //-----------------------------------------------------------------------------
93 class wxDataViewTextCtrlWrapper
: public wxEvtHandler
96 // NB: text must be a valid object but not Create()d yet
97 wxDataViewTextCtrlWrapper( wxDataViewMainWindow
*owner
,
99 wxDataViewListModel
*model
,
100 size_t col
, size_t row
,
103 wxTextCtrl
*GetText() const { return m_text
; }
105 void AcceptChangesAndFinish();
108 void OnChar( wxKeyEvent
&event
);
109 void OnKeyUp( wxKeyEvent
&event
);
110 void OnKillFocus( wxFocusEvent
&event
);
112 bool AcceptChanges();
116 wxDataViewMainWindow
*m_owner
;
118 wxString m_startValue
;
119 wxDataViewListModel
*m_model
;
123 bool m_aboutToFinish
;
125 DECLARE_EVENT_TABLE()
128 //-----------------------------------------------------------------------------
129 // wxDataViewMainWindow
130 //-----------------------------------------------------------------------------
132 WX_DEFINE_SORTED_ARRAY_SIZE_T( size_t, wxDataViewSelection
);
134 class wxDataViewMainWindow
: public wxWindow
137 wxDataViewMainWindow( wxDataViewCtrl
*parent
,
139 const wxPoint
&pos
= wxDefaultPosition
,
140 const wxSize
&size
= wxDefaultSize
,
141 const wxString
&name
= wxT("wxdataviewctrlmainwindow") );
142 virtual ~wxDataViewMainWindow();
144 // notifications from wxDataViewListModel
147 bool RowInserted( size_t before
);
148 bool RowDeleted( size_t row
);
149 bool RowChanged( size_t row
);
150 bool ValueChanged( size_t col
, size_t row
);
151 bool RowsReordered( size_t *new_order
);
154 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
155 wxDataViewCtrl
*GetOwner() { return m_owner
; }
157 void OnPaint( wxPaintEvent
&event
);
158 void OnArrowChar(size_t newCurrent
, const wxKeyEvent
& event
);
159 void OnChar( wxKeyEvent
&event
);
160 void OnMouse( wxMouseEvent
&event
);
161 void OnSetFocus( wxFocusEvent
&event
);
162 void OnKillFocus( wxFocusEvent
&event
);
164 void UpdateDisplay();
165 void RecalculateDisplay();
166 void OnInternalIdle();
168 void OnRenameTimer();
169 void FinishEditing( wxTextCtrl
*text
);
171 void ScrollWindow( int dx
, int dy
, const wxRect
*rect
);
173 bool HasCurrentRow() { return m_currentRow
!= (size_t)-1; }
174 void ChangeCurrentRow( size_t row
);
176 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE
); };
177 bool IsEmpty() { return GetRowCount() == 0; }
179 int GetCountPerPage();
180 int GetEndOfLastCol();
181 size_t GetFirstVisibleRow();
182 size_t GetLastVisibleRow();
183 size_t GetRowCount();
185 void SelectAllRows( bool on
);
186 void SelectRow( size_t row
, bool on
);
187 void SelectRows( size_t from
, size_t to
, bool on
);
188 void ReverseRowSelection( size_t row
);
189 bool IsRowSelected( size_t row
);
191 void RefreshRow( size_t row
);
192 void RefreshRows( size_t from
, size_t to
);
193 void RefreshRowsAfter( size_t firstRow
);
196 wxDataViewCtrl
*m_owner
;
200 wxDataViewColumn
*m_currentCol
;
202 wxDataViewSelection m_selection
;
204 wxDataViewRenameTimer
*m_renameTimer
;
205 wxDataViewTextCtrlWrapper
*m_textctrlWrapper
;
213 // for double click logic
214 size_t m_lineLastClicked
,
215 m_lineBeforeLastClicked
,
216 m_lineSelectSingleOnUp
;
219 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow
)
220 DECLARE_EVENT_TABLE()
223 // ---------------------------------------------------------
224 // wxGenericDataViewListModelNotifier
225 // ---------------------------------------------------------
227 class wxGenericDataViewListModelNotifier
: public wxDataViewListModelNotifier
230 wxGenericDataViewListModelNotifier( wxDataViewMainWindow
*mainWindow
)
231 { m_mainWindow
= mainWindow
; }
233 virtual bool RowAppended()
234 { return m_mainWindow
->RowAppended(); }
235 virtual bool RowPrepended()
236 { return m_mainWindow
->RowPrepended(); }
237 virtual bool RowInserted( size_t before
)
238 { return m_mainWindow
->RowInserted( before
); }
239 virtual bool RowDeleted( size_t row
)
240 { return m_mainWindow
->RowDeleted( row
); }
241 virtual bool RowChanged( size_t row
)
242 { return m_mainWindow
->RowChanged( row
); }
243 virtual bool ValueChanged( size_t col
, size_t row
)
244 { return m_mainWindow
->ValueChanged( col
, row
); }
245 virtual bool RowsReordered( size_t *new_order
)
246 { return m_mainWindow
->RowsReordered( new_order
); }
247 virtual bool Cleared()
248 { return m_mainWindow
->Cleared(); }
250 wxDataViewMainWindow
*m_mainWindow
;
253 // ---------------------------------------------------------
255 // ---------------------------------------------------------
257 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCell
, wxDataViewCellBase
)
259 wxDataViewCell::wxDataViewCell( const wxString
&varianttype
, wxDataViewCellMode mode
) :
260 wxDataViewCellBase( varianttype
, mode
)
265 wxDataViewCell::~wxDataViewCell()
271 wxDC
*wxDataViewCell::GetDC()
275 if (GetOwner() == NULL
)
277 if (GetOwner()->GetOwner() == NULL
)
279 m_dc
= new wxClientDC( GetOwner()->GetOwner() );
285 // ---------------------------------------------------------
286 // wxDataViewCustomCell
287 // ---------------------------------------------------------
289 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomCell
, wxDataViewCell
)
291 wxDataViewCustomCell::wxDataViewCustomCell( const wxString
&varianttype
,
292 wxDataViewCellMode mode
) :
293 wxDataViewCell( varianttype
, mode
)
297 // ---------------------------------------------------------
298 // wxDataViewTextCell
299 // ---------------------------------------------------------
301 IMPLEMENT_ABSTRACT_CLASS(wxDataViewTextCell
, wxDataViewCustomCell
)
303 wxDataViewTextCell::wxDataViewTextCell( const wxString
&varianttype
, wxDataViewCellMode mode
) :
304 wxDataViewCustomCell( varianttype
, mode
)
308 bool wxDataViewTextCell::SetValue( const wxVariant
&value
)
310 m_text
= value
.GetString();
315 bool wxDataViewTextCell::GetValue( wxVariant
& WXUNUSED(value
) )
320 bool wxDataViewTextCell::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
322 dc
->DrawText( m_text
, cell
.x
, cell
.y
);
327 wxSize
wxDataViewTextCell::GetSize()
329 return wxSize(80,20);
332 // ---------------------------------------------------------
333 // wxDataViewToggleCell
334 // ---------------------------------------------------------
336 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleCell
, wxDataViewCustomCell
)
338 wxDataViewToggleCell::wxDataViewToggleCell( const wxString
&varianttype
,
339 wxDataViewCellMode mode
) :
340 wxDataViewCustomCell( varianttype
, mode
)
345 bool wxDataViewToggleCell::SetValue( const wxVariant
&value
)
347 m_toggle
= value
.GetBool();
352 bool wxDataViewToggleCell::GetValue( wxVariant
&WXUNUSED(value
) )
357 bool wxDataViewToggleCell::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
359 // User wxRenderer here
362 rect
.x
= cell
.x
+ cell
.width
/2 - 10;
364 rect
.y
= cell
.y
+ cell
.height
/2 - 10;
369 flags
|= wxCONTROL_CHECKED
;
370 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE
)
371 flags
|= wxCONTROL_DISABLED
;
373 wxRendererNative::Get().DrawCheckBox(
374 GetOwner()->GetOwner(),
382 bool wxDataViewToggleCell::Activate( wxRect
WXUNUSED(cell
), wxDataViewListModel
*model
, size_t col
, size_t row
)
384 bool value
= !m_toggle
;
385 wxVariant variant
= value
;
386 model
->SetValue( variant
, col
, row
);
387 model
->ValueChanged( col
, row
);
391 wxSize
wxDataViewToggleCell::GetSize()
393 return wxSize(20,20);
396 // ---------------------------------------------------------
397 // wxDataViewProgressCell
398 // ---------------------------------------------------------
400 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell
, wxDataViewCustomCell
)
402 wxDataViewProgressCell::wxDataViewProgressCell( const wxString
&label
,
403 const wxString
&varianttype
, wxDataViewCellMode mode
) :
404 wxDataViewCustomCell( varianttype
, mode
)
410 wxDataViewProgressCell::~wxDataViewProgressCell()
414 bool wxDataViewProgressCell::SetValue( const wxVariant
&value
)
416 m_value
= (long) value
;
418 if (m_value
< 0) m_value
= 0;
419 if (m_value
> 100) m_value
= 100;
424 bool wxDataViewProgressCell::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
426 double pct
= (double)m_value
/ 100.0;
428 bar
.width
= (int)(cell
.width
* pct
);
429 dc
->SetPen( *wxTRANSPARENT_PEN
);
430 dc
->SetBrush( *wxBLUE_BRUSH
);
431 dc
->DrawRectangle( bar
);
433 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
434 dc
->SetPen( *wxBLACK_PEN
);
435 dc
->DrawRectangle( cell
);
440 wxSize
wxDataViewProgressCell::GetSize()
442 return wxSize(40,12);
445 // ---------------------------------------------------------
446 // wxDataViewDateCell
447 // ---------------------------------------------------------
449 class wxDataViewDateCellPopupTransient
: public wxPopupTransientWindow
452 wxDataViewDateCellPopupTransient( wxWindow
* parent
, wxDateTime
*value
,
453 wxDataViewListModel
*model
, size_t col
, size_t row
) :
454 wxPopupTransientWindow( parent
, wxBORDER_SIMPLE
)
459 m_cal
= new wxCalendarCtrl( this, wxID_ANY
, *value
);
460 wxBoxSizer
*sizer
= new wxBoxSizer( wxHORIZONTAL
);
461 sizer
->Add( m_cal
, 1, wxGROW
);
466 void OnCalendar( wxCalendarEvent
&event
);
468 wxCalendarCtrl
*m_cal
;
469 wxDataViewListModel
*m_model
;
474 virtual void OnDismiss()
479 DECLARE_EVENT_TABLE()
482 BEGIN_EVENT_TABLE(wxDataViewDateCellPopupTransient
,wxPopupTransientWindow
)
483 EVT_CALENDAR( wxID_ANY
, wxDataViewDateCellPopupTransient::OnCalendar
)
486 void wxDataViewDateCellPopupTransient::OnCalendar( wxCalendarEvent
&event
)
488 wxDateTime date
= event
.GetDate();
489 wxVariant value
= date
;
490 m_model
->SetValue( value
, m_col
, m_row
);
491 m_model
->ValueChanged( m_col
, m_row
);
495 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateCell
, wxDataViewCustomCell
)
497 wxDataViewDateCell::wxDataViewDateCell( const wxString
&varianttype
,
498 wxDataViewCellMode mode
) :
499 wxDataViewCustomCell( varianttype
, mode
)
503 bool wxDataViewDateCell::SetValue( const wxVariant
&value
)
505 m_date
= value
.GetDateTime();
510 bool wxDataViewDateCell::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
512 dc
->SetFont( GetOwner()->GetOwner()->GetFont() );
513 wxString tmp
= m_date
.FormatDate();
514 dc
->DrawText( tmp
, cell
.x
, cell
.y
);
519 wxSize
wxDataViewDateCell::GetSize()
521 wxDataViewCtrl
* view
= GetOwner()->GetOwner();
522 wxString tmp
= m_date
.FormatDate();
524 view
->GetTextExtent( tmp
, &x
, &y
, &d
);
525 return wxSize(x
,y
+d
);
528 bool wxDataViewDateCell::Activate( wxRect
WXUNUSED(cell
), wxDataViewListModel
*model
, size_t col
, size_t row
)
531 model
->GetValue( variant
, col
, row
);
532 wxDateTime value
= variant
.GetDateTime();
534 wxDataViewDateCellPopupTransient
*popup
= new wxDataViewDateCellPopupTransient(
535 GetOwner()->GetOwner()->GetParent(), &value
, model
, col
, row
);
536 wxPoint pos
= wxGetMousePosition();
539 popup
->Popup( popup
->m_cal
);
544 // ---------------------------------------------------------
546 // ---------------------------------------------------------
548 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn
, wxDataViewColumnBase
)
550 wxDataViewColumn::wxDataViewColumn( const wxString
&title
, wxDataViewCell
*cell
, size_t model_column
,
551 int fixed_width
, wxDataViewColumnSizing sizing
, int flags
) :
552 wxDataViewColumnBase( title
, cell
, model_column
, flags
)
556 m_width
= fixed_width
;
557 m_fixedWidth
= fixed_width
;
560 wxDataViewColumn::~wxDataViewColumn()
564 void wxDataViewColumn::SetTitle( const wxString
&title
)
566 wxDataViewColumnBase::SetTitle( title
);
570 int wxDataViewColumn::GetWidth()
575 void wxDataViewColumn::SetFixedWidth( int width
)
577 m_fixedWidth
= width
;
579 if (m_sizing
== wxDATAVIEW_COL_WIDTH_FIXED
)
586 int wxDataViewColumn::GetFixedWidth()
591 //-----------------------------------------------------------------------------
592 // wxDataViewHeaderWindow
593 //-----------------------------------------------------------------------------
595 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow
, wxWindow
)
597 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow
,wxWindow
)
598 EVT_PAINT (wxDataViewHeaderWindow::OnPaint
)
599 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse
)
600 EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus
)
603 wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl
*parent
, wxWindowID id
,
604 const wxPoint
&pos
, const wxSize
&size
, const wxString
&name
) :
605 wxWindow( parent
, id
, pos
, size
, 0, name
)
609 m_resizeCursor
= new wxCursor( wxCURSOR_SIZEWE
);
611 wxVisualAttributes attr
= wxPanel::GetClassDefaultAttributes();
612 SetOwnForegroundColour( attr
.colFg
);
613 SetOwnBackgroundColour( attr
.colBg
);
615 SetOwnFont( attr
.font
);
618 wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
620 delete m_resizeCursor
;
623 void wxDataViewHeaderWindow::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
626 GetClientSize( &w
, &h
);
628 wxPaintDC
dc( this );
631 m_owner
->GetScrollPixelsPerUnit( &xpix
, NULL
);
634 m_owner
->GetViewStart( &x
, NULL
);
636 // account for the horz scrollbar offset
637 dc
.SetDeviceOrigin( -x
* xpix
, 0 );
639 dc
.SetFont( GetFont() );
641 size_t cols
= GetOwner()->GetNumberOfColumns();
644 for (i
= 0; i
< cols
; i
++)
646 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
647 int width
= col
->GetWidth();
652 wxRendererNative::Get().DrawHeaderButton
656 wxRect(xpos
, 0, cw
, ch
-1),
657 m_parent
->IsEnabled() ? 0
658 : (int)wxCONTROL_DISABLED
661 dc
.DrawText( col
->GetTitle(), xpos
+3, 3 );
667 void wxDataViewHeaderWindow::OnMouse( wxMouseEvent
&WXUNUSED(event
) )
671 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent
&event
)
673 GetParent()->SetFocus();
677 //-----------------------------------------------------------------------------
678 // wxDataViewRenameTimer
679 //-----------------------------------------------------------------------------
681 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow
*owner
)
686 void wxDataViewRenameTimer::Notify()
688 m_owner
->OnRenameTimer();
691 //-----------------------------------------------------------------------------
692 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
693 //-----------------------------------------------------------------------------
695 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper
, wxEvtHandler
)
696 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar
)
697 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp
)
698 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus
)
701 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
702 wxDataViewMainWindow
*owner
,
704 wxDataViewListModel
*model
,
705 size_t col
, size_t row
,
715 m_aboutToFinish
= false;
718 model
->GetValue( value
, col
, row
);
719 m_startValue
= value
.GetString();
721 m_owner
->GetOwner()->CalcScrolledPosition(
722 rectLabel
.x
, rectLabel
.y
, &rectLabel
.x
, &rectLabel
.y
);
724 m_text
->Create( owner
, wxID_ANY
, m_startValue
,
725 wxPoint(rectLabel
.x
-2,rectLabel
.y
-2),
726 wxSize(rectLabel
.width
+7,rectLabel
.height
+4) );
729 m_text
->PushEventHandler(this);
732 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
734 m_aboutToFinish
= true;
736 // Notify the owner about the changes
739 // Even if vetoed, close the control (consistent with MSW)
743 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent
&event
)
745 switch ( event
.m_keyCode
)
748 AcceptChangesAndFinish();
752 // m_owner->OnRenameCancelled( m_itemEdited );
761 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent
&event
)
769 // auto-grow the textctrl
770 wxSize parentSize
= m_owner
->GetSize();
771 wxPoint myPos
= m_text
->GetPosition();
772 wxSize mySize
= m_text
->GetSize();
774 m_text
->GetTextExtent(m_text
->GetValue() + _T("MM"), &sx
, &sy
);
775 if (myPos
.x
+ sx
> parentSize
.x
)
776 sx
= parentSize
.x
- myPos
.x
;
779 m_text
->SetSize(sx
, wxDefaultCoord
);
784 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent
&event
)
786 if ( !m_finished
&& !m_aboutToFinish
)
789 //if ( !AcceptChanges() )
790 // m_owner->OnRenameCancelled( m_itemEdited );
795 // We must let the native text control handle focus
799 bool wxDataViewTextCtrlWrapper::AcceptChanges()
801 const wxString value
= m_text
->GetValue();
803 if ( value
== m_startValue
)
804 // nothing changed, always accept
807 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
808 // vetoed by the user
811 // accepted, do rename the item
814 m_model
->SetValue( variant
, m_col
, m_row
);
815 m_model
->ValueChanged( m_col
, m_row
);
820 void wxDataViewTextCtrlWrapper::Finish()
826 m_text
->RemoveEventHandler(this);
827 m_owner
->FinishEditing(m_text
);
830 wxPendingDelete
.Append( this );
834 //-----------------------------------------------------------------------------
835 // wxDataViewMainWindow
836 //-----------------------------------------------------------------------------
838 int LINKAGEMODE
wxDataViewSelectionCmp( size_t row1
, size_t row2
)
840 if (row1
> row2
) return 1;
841 if (row1
== row2
) return 0;
846 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow
, wxWindow
)
848 BEGIN_EVENT_TABLE(wxDataViewMainWindow
,wxWindow
)
849 EVT_PAINT (wxDataViewMainWindow::OnPaint
)
850 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse
)
851 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus
)
852 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus
)
853 EVT_CHAR (wxDataViewMainWindow::OnChar
)
856 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl
*parent
, wxWindowID id
,
857 const wxPoint
&pos
, const wxSize
&size
, const wxString
&name
) :
858 wxWindow( parent
, id
, pos
, size
, wxWANTS_CHARS
, name
),
859 m_selection( wxDataViewSelectionCmp
)
864 m_lastOnSame
= false;
865 m_renameTimer
= new wxDataViewRenameTimer( this );
866 m_textctrlWrapper
= NULL
;
868 // TODO: user better initial values/nothing selected
872 // TODO: we need to calculate this smartly
876 m_dragStart
= wxPoint(0,0);
877 m_lineLastClicked
= (size_t) -1;
878 m_lineBeforeLastClicked
= (size_t) -1;
879 m_lineSelectSingleOnUp
= (size_t) -1;
883 SetBackgroundColour( *wxWHITE
);
888 wxDataViewMainWindow::~wxDataViewMainWindow()
890 delete m_renameTimer
;
893 void wxDataViewMainWindow::OnRenameTimer()
895 // We have to call this here because changes may just have
896 // been made and no screen update taken place.
902 size_t cols
= GetOwner()->GetNumberOfColumns();
904 for (i
= 0; i
< cols
; i
++)
906 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
907 if (c
== m_currentCol
)
909 xpos
+= c
->GetWidth();
911 wxRect
labelRect( xpos
, m_currentRow
* m_lineHeight
, m_currentCol
->GetWidth(), m_lineHeight
);
913 wxClassInfo
*textControlClass
= CLASSINFO(wxTextCtrl
);
915 wxTextCtrl
* const text
= (wxTextCtrl
*)textControlClass
->CreateObject();
916 m_textctrlWrapper
= new wxDataViewTextCtrlWrapper(this, text
, GetOwner()->GetModel(),
917 m_currentCol
->GetModelColumn(), m_currentRow
, labelRect
);
920 void wxDataViewMainWindow::FinishEditing( wxTextCtrl
*text
)
923 m_textctrlWrapper
= NULL
;
925 // SetFocusIgnoringChildren();
928 bool wxDataViewMainWindow::RowAppended()
933 bool wxDataViewMainWindow::RowPrepended()
938 bool wxDataViewMainWindow::RowInserted( size_t WXUNUSED(before
) )
943 bool wxDataViewMainWindow::RowDeleted( size_t WXUNUSED(row
) )
948 bool wxDataViewMainWindow::RowChanged( size_t WXUNUSED(row
) )
953 bool wxDataViewMainWindow::ValueChanged( size_t WXUNUSED(col
), size_t row
)
955 wxRect
rect( 0, row
*m_lineHeight
, 10000, m_lineHeight
);
956 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
957 Refresh( true, &rect
);
962 bool wxDataViewMainWindow::RowsReordered( size_t *WXUNUSED(new_order
) )
969 bool wxDataViewMainWindow::Cleared()
974 void wxDataViewMainWindow::UpdateDisplay()
979 void wxDataViewMainWindow::OnInternalIdle()
981 wxWindow::OnInternalIdle();
985 RecalculateDisplay();
990 void wxDataViewMainWindow::RecalculateDisplay()
992 wxDataViewListModel
*model
= GetOwner()->GetModel();
1000 size_t cols
= GetOwner()->GetNumberOfColumns();
1002 for (i
= 0; i
< cols
; i
++)
1004 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
1005 width
+= col
->GetWidth();
1008 int height
= model
->GetNumberOfRows() * m_lineHeight
;
1010 SetVirtualSize( width
, height
);
1011 GetOwner()->SetScrollRate( 10, m_lineHeight
);
1016 void wxDataViewMainWindow::ScrollWindow( int dx
, int dy
, const wxRect
*rect
)
1018 wxWindow::ScrollWindow( dx
, dy
, rect
);
1019 GetOwner()->m_headerArea
->ScrollWindow( dx
, 0 );
1022 void wxDataViewMainWindow::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
1024 wxPaintDC
dc( this );
1026 GetOwner()->PrepareDC( dc
);
1028 dc
.SetFont( GetFont() );
1030 wxRect update
= GetUpdateRegion().GetBox();
1031 m_owner
->CalcUnscrolledPosition( update
.x
, update
.y
, &update
.x
, &update
.y
);
1033 wxDataViewListModel
*model
= GetOwner()->GetModel();
1035 size_t item_start
= wxMax( 0, (update
.y
/ m_lineHeight
) );
1036 size_t item_count
= wxMin( (int)(((update
.y
+ update
.height
) / m_lineHeight
) - item_start
+ 1),
1037 (int)(model
->GetNumberOfRows()-item_start
) );
1042 for (item
= item_start
; item
< item_start
+item_count
; item
++)
1044 if (m_selection
.Index( item
) != wxNOT_FOUND
)
1046 int flags
= wxCONTROL_SELECTED
;
1047 if (item
== m_currentRow
)
1048 flags
|= wxCONTROL_CURRENT
;
1050 flags
|= wxCONTROL_FOCUSED
;
1051 wxRect
rect( 0, item
*m_lineHeight
+1, GetEndOfLastCol(), m_lineHeight
-2 );
1052 wxRendererNative::Get().DrawItemSelectionRect
1062 if (item
== m_currentRow
)
1064 int flags
= wxCONTROL_CURRENT
;
1066 flags
|= wxCONTROL_FOCUSED
; // should have no effect
1067 wxRect
rect( 0, item
*m_lineHeight
+1, GetEndOfLastCol(), m_lineHeight
-2 );
1068 wxRendererNative::Get().DrawItemSelectionRect
1082 cell_rect
.height
= m_lineHeight
;
1083 size_t cols
= GetOwner()->GetNumberOfColumns();
1085 for (i
= 0; i
< cols
; i
++)
1087 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
1088 wxDataViewCell
*cell
= col
->GetCell();
1089 cell_rect
.width
= col
->GetWidth();
1091 for (item
= item_start
; item
< item_start
+item_count
; item
++)
1093 cell_rect
.y
= item
*m_lineHeight
;
1095 model
->GetValue( value
, col
->GetModelColumn(), item
);
1096 cell
->SetValue( value
);
1097 wxSize size
= cell
->GetSize();
1098 // cannot be bigger than allocated space
1099 size
.x
= wxMin( size
.x
, cell_rect
.width
);
1100 size
.y
= wxMin( size
.y
, cell_rect
.height
);
1101 // TODO: check for left/right/centre alignment here
1104 item_rect
.x
= cell_rect
.x
+ (cell_rect
.width
/ 2) - (size
.x
/ 2);
1105 item_rect
.y
= cell_rect
.y
+ (cell_rect
.height
/ 2) - (size
.y
/ 2);
1107 item_rect
.width
= size
.x
;
1108 item_rect
.height
= size
.y
;
1109 cell
->Render( item_rect
, &dc
, 0 );
1112 cell_rect
.x
+= cell_rect
.width
;
1116 int wxDataViewMainWindow::GetCountPerPage()
1118 wxSize size
= GetClientSize();
1119 return size
.y
/ m_lineHeight
;
1122 int wxDataViewMainWindow::GetEndOfLastCol()
1126 for (i
= 0; i
< GetOwner()->GetNumberOfColumns(); i
++)
1128 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
1129 width
+= c
->GetWidth();
1134 size_t wxDataViewMainWindow::GetFirstVisibleRow()
1138 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
1140 return y
/ m_lineHeight
;
1143 size_t wxDataViewMainWindow::GetLastVisibleRow()
1145 wxSize client_size
= GetClientSize();
1146 m_owner
->CalcUnscrolledPosition( client_size
.x
, client_size
.y
, &client_size
.x
, &client_size
.y
);
1148 return wxMin( GetRowCount()-1, ((unsigned)client_size
.y
/m_lineHeight
)+1 );
1151 size_t wxDataViewMainWindow::GetRowCount()
1153 return GetOwner()->GetModel()->GetNumberOfRows();
1156 void wxDataViewMainWindow::ChangeCurrentRow( size_t row
)
1163 void wxDataViewMainWindow::SelectAllRows( bool on
)
1170 m_selection
.Clear();
1171 for (size_t i
= 0; i
< GetRowCount(); i
++)
1172 m_selection
.Add( i
);
1177 size_t first_visible
= GetFirstVisibleRow();
1178 size_t last_visible
= GetLastVisibleRow();
1180 for (i
= 0; i
< m_selection
.GetCount(); i
++)
1182 size_t row
= m_selection
[i
];
1183 if ((row
>= first_visible
) && (row
<= last_visible
))
1186 m_selection
.Clear();
1190 void wxDataViewMainWindow::SelectRow( size_t row
, bool on
)
1192 if (m_selection
.Index( row
) == wxNOT_FOUND
)
1196 m_selection
.Add( row
);
1204 m_selection
.Remove( row
);
1210 void wxDataViewMainWindow::SelectRows( size_t from
, size_t to
, bool on
)
1220 for (i
= from
; i
<= to
; i
++)
1222 if (m_selection
.Index( i
) == wxNOT_FOUND
)
1225 m_selection
.Add( i
);
1230 m_selection
.Remove( i
);
1233 RefreshRows( from
, to
);
1236 void wxDataViewMainWindow::ReverseRowSelection( size_t row
)
1238 if (m_selection
.Index( row
) == wxNOT_FOUND
)
1239 m_selection
.Add( row
);
1241 m_selection
.Remove( row
);
1245 bool wxDataViewMainWindow::IsRowSelected( size_t row
)
1247 return (m_selection
.Index( row
) != wxNOT_FOUND
);
1250 void wxDataViewMainWindow::RefreshRow( size_t row
)
1252 wxRect
rect( 0, row
*m_lineHeight
, GetEndOfLastCol(), m_lineHeight
);
1253 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1255 wxSize client_size
= GetClientSize();
1256 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1257 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1258 if (intersect_rect
.width
> 0)
1259 Refresh( true, &intersect_rect
);
1262 void wxDataViewMainWindow::RefreshRows( size_t from
, size_t to
)
1271 wxRect
rect( 0, from
*m_lineHeight
, GetEndOfLastCol(), (to
-from
+1) * m_lineHeight
);
1272 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1274 wxSize client_size
= GetClientSize();
1275 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1276 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1277 if (intersect_rect
.width
> 0)
1278 Refresh( true, &intersect_rect
);
1281 void wxDataViewMainWindow::RefreshRowsAfter( size_t firstRow
)
1283 size_t count
= GetRowCount();
1284 if (firstRow
> count
)
1287 wxRect
rect( 0, firstRow
*m_lineHeight
, GetEndOfLastCol(), count
* m_lineHeight
);
1288 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1290 wxSize client_size
= GetClientSize();
1291 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1292 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1293 if (intersect_rect
.width
> 0)
1294 Refresh( true, &intersect_rect
);
1297 void wxDataViewMainWindow::OnArrowChar(size_t newCurrent
, const wxKeyEvent
& event
)
1299 wxCHECK_RET( newCurrent
< GetRowCount(),
1300 _T("invalid item index in OnArrowChar()") );
1302 // if there is no selection, we cannot move it anywhere
1303 if (!HasCurrentRow())
1306 size_t oldCurrent
= m_currentRow
;
1308 // in single selection we just ignore Shift as we can't select several
1310 if ( event
.ShiftDown() && !IsSingleSel() )
1312 RefreshRow( oldCurrent
);
1314 ChangeCurrentRow( newCurrent
);
1316 // select all the items between the old and the new one
1317 if ( oldCurrent
> newCurrent
)
1319 newCurrent
= oldCurrent
;
1320 oldCurrent
= m_currentRow
;
1323 SelectRows( oldCurrent
, newCurrent
, true );
1327 RefreshRow( oldCurrent
);
1329 // all previously selected items are unselected unless ctrl is held
1330 if ( !event
.ControlDown() )
1331 SelectAllRows(false);
1333 ChangeCurrentRow( newCurrent
);
1335 if ( !event
.ControlDown() )
1336 SelectRow( m_currentRow
, true );
1338 RefreshRow( m_currentRow
);
1344 void wxDataViewMainWindow::OnChar( wxKeyEvent
&event
)
1346 if (event
.GetKeyCode() == WXK_TAB
)
1348 wxNavigationKeyEvent nevent
;
1349 nevent
.SetWindowChange( event
.ControlDown() );
1350 nevent
.SetDirection( !event
.ShiftDown() );
1351 nevent
.SetEventObject( GetParent()->GetParent() );
1352 nevent
.SetCurrentFocus( m_parent
);
1353 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent
))
1357 // no item -> nothing to do
1358 if (!HasCurrentRow())
1364 // don't use m_linesPerPage directly as it might not be computed yet
1365 const int pageSize
= GetCountPerPage();
1366 wxCHECK_RET( pageSize
, _T("should have non zero page size") );
1368 switch ( event
.GetKeyCode() )
1371 if ( m_currentRow
> 0 )
1372 OnArrowChar( m_currentRow
- 1, event
);
1376 if ( m_currentRow
< GetRowCount() - 1 )
1377 OnArrowChar( m_currentRow
+ 1, event
);
1382 OnArrowChar( GetRowCount() - 1, event
);
1387 OnArrowChar( 0, event
);
1392 int steps
= pageSize
- 1;
1393 int index
= m_currentRow
- steps
;
1397 OnArrowChar( index
, event
);
1403 int steps
= pageSize
- 1;
1404 size_t index
= m_currentRow
+ steps
;
1405 size_t count
= GetRowCount();
1406 if ( index
>= count
)
1409 OnArrowChar( index
, event
);
1418 void wxDataViewMainWindow::OnMouse( wxMouseEvent
&event
)
1420 if (event
.GetEventType() == wxEVT_MOUSEWHEEL
)
1422 // let the base handle mouse wheel events.
1427 int x
= event
.GetX();
1428 int y
= event
.GetY();
1429 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
1431 wxDataViewColumn
*col
= NULL
;
1434 size_t cols
= GetOwner()->GetNumberOfColumns();
1436 for (i
= 0; i
< cols
; i
++)
1438 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
1439 if (x
< xpos
+ c
->GetWidth())
1444 xpos
+= c
->GetWidth();
1448 wxDataViewCell
*cell
= col
->GetCell();
1450 size_t current
= y
/ m_lineHeight
;
1452 if ((current
> GetRowCount()) || (x
> GetEndOfLastCol()))
1454 // Unselect all if below the last row ?
1458 wxDataViewListModel
*model
= GetOwner()->GetModel();
1460 if (event
.Dragging())
1462 if (m_dragCount
== 0)
1464 // we have to report the raw, physical coords as we want to be
1465 // able to call HitTest(event.m_pointDrag) from the user code to
1466 // get the item being dragged
1467 m_dragStart
= event
.GetPosition();
1472 if (m_dragCount
!= 3)
1475 if (event
.LeftIsDown())
1477 // Notify cell about drag
1486 bool forceClick
= false;
1488 if (event
.ButtonDClick())
1490 m_renameTimer
->Stop();
1491 m_lastOnSame
= false;
1494 if (event
.LeftDClick())
1496 if ( current
== m_lineLastClicked
)
1498 if (cell
->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE
)
1501 model
->GetValue( value
, col
->GetModelColumn(), current
);
1502 cell
->SetValue( value
);
1503 wxRect
cell_rect( xpos
, current
* m_lineHeight
, col
->GetWidth(), m_lineHeight
);
1504 cell
->Activate( cell_rect
, model
, col
->GetModelColumn(), current
);
1510 // The first click was on another item, so don't interpret this as
1511 // a double click, but as a simple click instead
1518 if (m_lineSelectSingleOnUp
!= (size_t)-1)
1520 // select single line
1521 SelectAllRows( false );
1522 SelectRow( m_lineSelectSingleOnUp
, true );
1527 if ((col
== m_currentCol
) && (current
== m_currentRow
) &&
1528 (cell
->GetMode() == wxDATAVIEW_CELL_EDITABLE
) )
1530 m_renameTimer
->Start( 100, true );
1534 m_lastOnSame
= false;
1535 m_lineSelectSingleOnUp
= (size_t)-1;
1539 // This is necessary, because after a DnD operation in
1540 // from and to ourself, the up event is swallowed by the
1541 // DnD code. So on next non-up event (which means here and
1542 // now) m_lineSelectSingleOnUp should be reset.
1543 m_lineSelectSingleOnUp
= (size_t)-1;
1546 if (event
.RightDown())
1548 m_lineBeforeLastClicked
= m_lineLastClicked
;
1549 m_lineLastClicked
= current
;
1551 // If the item is already selected, do not update the selection.
1552 // Multi-selections should not be cleared if a selected item is clicked.
1553 if (!IsRowSelected(current
))
1555 SelectAllRows(false);
1556 ChangeCurrentRow(current
);
1557 SelectRow(m_currentRow
,true);
1560 // notify cell about right click
1563 // Allow generation of context menu event
1566 else if (event
.MiddleDown())
1568 // notify cell about middle click
1571 if (event
.LeftDown() || forceClick
)
1577 m_lineBeforeLastClicked
= m_lineLastClicked
;
1578 m_lineLastClicked
= current
;
1580 size_t oldCurrentRow
= m_currentRow
;
1581 bool oldWasSelected
= IsRowSelected(m_currentRow
);
1583 bool cmdModifierDown
= event
.CmdDown();
1584 if ( IsSingleSel() || !(cmdModifierDown
|| event
.ShiftDown()) )
1586 if ( IsSingleSel() || !IsRowSelected(current
) )
1588 SelectAllRows( false );
1590 ChangeCurrentRow(current
);
1592 SelectRow(m_currentRow
,true);
1594 else // multi sel & current is highlighted & no mod keys
1596 m_lineSelectSingleOnUp
= current
;
1597 ChangeCurrentRow(current
); // change focus
1600 else // multi sel & either ctrl or shift is down
1602 if (cmdModifierDown
)
1604 ChangeCurrentRow(current
);
1606 ReverseRowSelection(m_currentRow
);
1608 else if (event
.ShiftDown())
1610 ChangeCurrentRow(current
);
1612 size_t lineFrom
= oldCurrentRow
,
1615 if ( lineTo
< lineFrom
)
1618 lineFrom
= m_currentRow
;
1621 SelectRows(lineFrom
, lineTo
, true);
1623 else // !ctrl, !shift
1625 // test in the enclosing if should make it impossible
1626 wxFAIL_MSG( _T("how did we get here?") );
1630 if (m_currentRow
!= oldCurrentRow
)
1631 RefreshRow( oldCurrentRow
);
1633 wxDataViewColumn
*oldCurrentCol
= m_currentCol
;
1635 // Update selection here...
1638 m_lastOnSame
= !forceClick
&& ((col
== oldCurrentCol
) && (current
== oldCurrentRow
)) && oldWasSelected
;
1642 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent
&event
)
1646 if (HasCurrentRow())
1652 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent
&event
)
1656 if (HasCurrentRow())
1662 //-----------------------------------------------------------------------------
1664 //-----------------------------------------------------------------------------
1666 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl
, wxDataViewCtrlBase
)
1668 BEGIN_EVENT_TABLE(wxDataViewCtrl
, wxDataViewCtrlBase
)
1669 EVT_SIZE(wxDataViewCtrl::OnSize
)
1672 wxDataViewCtrl::~wxDataViewCtrl()
1675 GetModel()->RemoveNotifier( m_notifier
);
1678 void wxDataViewCtrl::Init()
1683 bool wxDataViewCtrl::Create(wxWindow
*parent
, wxWindowID id
,
1684 const wxPoint
& pos
, const wxSize
& size
,
1685 long style
, const wxValidator
& validator
)
1687 if (!wxControl::Create( parent
, id
, pos
, size
, style
| wxScrolledWindowStyle
|wxSUNKEN_BORDER
, validator
))
1693 MacSetClipChildren( true ) ;
1696 m_clientArea
= new wxDataViewMainWindow( this, wxID_ANY
);
1698 m_headerArea
= new wxDataViewHeaderWindow( this, wxID_ANY
, wxDefaultPosition
, wxSize(wxDefaultCoord
,22) );
1700 m_headerArea
= new wxDataViewHeaderWindow( this, wxID_ANY
, wxDefaultPosition
, wxSize(wxDefaultCoord
,25) );
1703 SetTargetWindow( m_clientArea
);
1705 wxBoxSizer
*sizer
= new wxBoxSizer( wxVERTICAL
);
1706 sizer
->Add( m_headerArea
, 0, wxGROW
);
1707 sizer
->Add( m_clientArea
, 1, wxGROW
);
1714 WXLRESULT
wxDataViewCtrl::MSWWindowProc(WXUINT nMsg
,
1718 WXLRESULT rc
= wxDataViewCtrlBase::MSWWindowProc(nMsg
, wParam
, lParam
);
1721 // we need to process arrows ourselves for scrolling
1722 if ( nMsg
== WM_GETDLGCODE
)
1724 rc
|= DLGC_WANTARROWS
;
1732 void wxDataViewCtrl::OnSize( wxSizeEvent
&WXUNUSED(event
) )
1734 // We need to override OnSize so that our scrolled
1735 // window a) does call Layout() to use sizers for
1736 // positioning the controls but b) does not query
1737 // the sizer for their size and use that for setting
1738 // the scrollable area as set that ourselves by
1739 // calling SetScrollbar() further down.
1746 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel
*model
)
1748 if (!wxDataViewCtrlBase::AssociateModel( model
))
1751 m_notifier
= new wxGenericDataViewListModelNotifier( m_clientArea
);
1753 model
->AddNotifier( m_notifier
);
1755 m_clientArea
->UpdateDisplay();
1760 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn
*col
)
1762 if (!wxDataViewCtrlBase::AppendColumn(col
))
1765 m_clientArea
->UpdateDisplay();
1771 // !wxUSE_GENERICDATAVIEWCTRL
1774 // wxUSE_DATAVIEWCTRL