1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datavgen.cpp
3 // Purpose: wxDataViewCtrl generic implementation
4 // Author: Robert Roebling
5 // Modified by: Francesco Montorsi, Guru Kathiresan, Otto Wyss
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
18 #if wxUSE_DATAVIEWCTRL
20 #include "wx/dataview.h"
22 #ifdef wxUSE_GENERICDATAVIEWCTRL
26 #include "wx/msw/private.h"
27 #include "wx/msw/wrapwin.h"
28 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
32 #include "wx/dcclient.h"
34 #include "wx/settings.h"
35 #include "wx/msgdlg.h"
38 #include "wx/stockitem.h"
39 #include "wx/calctrl.h"
40 #include "wx/popupwin.h"
41 #include "wx/renderer.h"
42 #include "wx/dcbuffer.h"
45 //-----------------------------------------------------------------------------
47 //-----------------------------------------------------------------------------
51 //-----------------------------------------------------------------------------
52 // wxDataViewHeaderWindow
53 //-----------------------------------------------------------------------------
55 #define USE_NATIVE_HEADER_WINDOW 1
57 class wxDataViewHeaderWindowBase
: public wxControl
60 wxDataViewHeaderWindowBase()
63 bool Create(wxDataViewCtrl
*parent
, wxWindowID id
,
64 const wxPoint
&pos
, const wxSize
&size
,
67 return wxWindow::Create(parent
, id
, pos
, size
, wxNO_BORDER
, name
);
70 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
71 wxDataViewCtrl
*GetOwner() { return m_owner
; }
73 // called on column addition/removal
74 virtual void UpdateDisplay() { /* by default, do nothing */ }
76 // updates the n-th column's width
77 virtual void SetColumnWidth(unsigned int n
, int width
);
79 // returns the n-th column
80 wxDataViewColumn
*GetColumn(unsigned int n
)
83 wxDataViewColumn
*ret
= m_owner
->GetColumn(n
);
90 wxDataViewCtrl
*m_owner
;
92 // sends an event generated from the n-th wxDataViewColumn
93 void SendEvent(wxEventType type
, unsigned int n
);
96 // on wxMSW the header window (only that part however) can be made native!
97 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
99 #define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow
101 class wxDataViewHeaderWindowMSW
: public wxDataViewHeaderWindowBase
105 wxDataViewHeaderWindowMSW( wxDataViewCtrl
*parent
,
107 const wxPoint
&pos
= wxDefaultPosition
,
108 const wxSize
&size
= wxDefaultSize
,
109 const wxString
&name
= wxT("wxdataviewctrlheaderwindow") )
111 Create(parent
, id
, pos
, size
, name
);
114 bool Create(wxDataViewCtrl
*parent
, wxWindowID id
,
115 const wxPoint
&pos
, const wxSize
&size
,
116 const wxString
&name
);
118 ~wxDataViewHeaderWindowMSW();
120 // called on column addition/removal
121 virtual void UpdateDisplay();
123 // called when the main window gets scrolled
124 virtual void ScrollWindow(int dx
, int dy
, const wxRect
*rect
= NULL
);
127 virtual bool MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
);
130 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW
)
133 #else // !defined(__WXMSW__)
135 #define HEADER_WINDOW_HEIGHT 25
136 #define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow
138 class wxGenericDataViewHeaderWindow
: public wxDataViewHeaderWindowBase
141 wxGenericDataViewHeaderWindow( wxDataViewCtrl
*parent
,
143 const wxPoint
&pos
= wxDefaultPosition
,
144 const wxSize
&size
= wxDefaultSize
,
145 const wxString
&name
= wxT("wxdataviewctrlheaderwindow") )
148 Create(parent
, id
, pos
, size
, name
);
151 bool Create(wxDataViewCtrl
*parent
, wxWindowID id
,
152 const wxPoint
&pos
, const wxSize
&size
,
153 const wxString
&name
);
155 ~wxGenericDataViewHeaderWindow()
157 delete m_resizeCursor
;
162 void OnPaint( wxPaintEvent
&event
);
163 void OnMouse( wxMouseEvent
&event
);
164 void OnSetFocus( wxFocusEvent
&event
);
169 // vars used for column resizing:
171 wxCursor
*m_resizeCursor
;
172 const wxCursor
*m_currentCursor
;
175 bool m_dirty
; // needs refresh?
176 int m_column
; // index of the column being resized
177 int m_currentX
; // divider line position in logical (unscrolled) coords
178 int m_minX
; // minimal position beyond which the divider line
179 // can't be dragged in logical coords
181 // internal utilities:
185 m_currentCursor
= (wxCursor
*) NULL
;
186 m_resizeCursor
= new wxCursor( wxCURSOR_SIZEWE
);
188 m_isDragging
= false;
191 m_column
= wxNOT_FOUND
;
197 void AdjustDC(wxDC
& dc
);
200 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow
)
201 DECLARE_EVENT_TABLE()
204 #endif // defined(__WXMSW__)
206 //-----------------------------------------------------------------------------
207 // wxDataViewRenameTimer
208 //-----------------------------------------------------------------------------
210 class wxDataViewRenameTimer
: public wxTimer
213 wxDataViewMainWindow
*m_owner
;
216 wxDataViewRenameTimer( wxDataViewMainWindow
*owner
);
220 //-----------------------------------------------------------------------------
221 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
222 //-----------------------------------------------------------------------------
224 class wxDataViewTextCtrlWrapper
: public wxEvtHandler
227 // NB: text must be a valid object but not Create()d yet
228 wxDataViewTextCtrlWrapper( wxDataViewMainWindow
*owner
,
230 wxDataViewListModel
*model
,
231 unsigned int col
, unsigned int row
,
234 wxTextCtrl
*GetText() const { return m_text
; }
236 void AcceptChangesAndFinish();
239 void OnChar( wxKeyEvent
&event
);
240 void OnKeyUp( wxKeyEvent
&event
);
241 void OnKillFocus( wxFocusEvent
&event
);
243 bool AcceptChanges();
247 wxDataViewMainWindow
*m_owner
;
249 wxString m_startValue
;
250 wxDataViewListModel
*m_model
;
254 bool m_aboutToFinish
;
256 DECLARE_EVENT_TABLE()
259 //-----------------------------------------------------------------------------
260 // wxDataViewMainWindow
261 //-----------------------------------------------------------------------------
263 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection
,
266 class wxDataViewMainWindow
: public wxWindow
269 wxDataViewMainWindow( wxDataViewCtrl
*parent
,
271 const wxPoint
&pos
= wxDefaultPosition
,
272 const wxSize
&size
= wxDefaultSize
,
273 const wxString
&name
= wxT("wxdataviewctrlmainwindow") );
274 virtual ~wxDataViewMainWindow();
276 // notifications from wxDataViewListModel
279 bool RowInserted( unsigned int before
);
280 bool RowDeleted( unsigned int row
);
281 bool RowChanged( unsigned int row
);
282 bool ValueChanged( unsigned int col
, unsigned int row
);
283 bool RowsReordered( unsigned int *new_order
);
286 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
287 wxDataViewCtrl
*GetOwner() { return m_owner
; }
289 void OnPaint( wxPaintEvent
&event
);
290 void OnArrowChar(unsigned int newCurrent
, const wxKeyEvent
& event
);
291 void OnChar( wxKeyEvent
&event
);
292 void OnMouse( wxMouseEvent
&event
);
293 void OnSetFocus( wxFocusEvent
&event
);
294 void OnKillFocus( wxFocusEvent
&event
);
296 void UpdateDisplay();
297 void RecalculateDisplay();
298 void OnInternalIdle();
300 void OnRenameTimer();
301 void FinishEditing( wxTextCtrl
*text
);
303 void ScrollWindow( int dx
, int dy
, const wxRect
*rect
);
305 bool HasCurrentRow() { return m_currentRow
!= (unsigned int)-1; }
306 void ChangeCurrentRow( unsigned int row
);
308 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE
); };
309 bool IsEmpty() { return GetRowCount() == 0; }
311 int GetCountPerPage();
312 int GetEndOfLastCol();
313 unsigned int GetFirstVisibleRow();
314 unsigned int GetLastVisibleRow();
315 unsigned int GetRowCount();
317 void Select( const wxArrayInt
& aSelections
);
318 void SelectAllRows( bool on
);
319 void SelectRow( unsigned int row
, bool on
);
320 void SelectRows( unsigned int from
, unsigned int to
, bool on
);
321 void ReverseRowSelection( unsigned int row
);
322 bool IsRowSelected( unsigned int row
);
324 void RefreshRow( unsigned int row
);
325 void RefreshRows( unsigned int from
, unsigned int to
);
326 void RefreshRowsAfter( unsigned int firstRow
);
329 wxDataViewCtrl
*m_owner
;
333 wxDataViewColumn
*m_currentCol
;
334 unsigned int m_currentRow
;
335 wxDataViewSelection m_selection
;
337 wxDataViewRenameTimer
*m_renameTimer
;
338 wxDataViewTextCtrlWrapper
*m_textctrlWrapper
;
346 // for double click logic
347 unsigned int m_lineLastClicked
,
348 m_lineBeforeLastClicked
,
349 m_lineSelectSingleOnUp
;
352 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow
)
353 DECLARE_EVENT_TABLE()
356 // ---------------------------------------------------------
357 // wxGenericDataViewListModelNotifier
358 // ---------------------------------------------------------
360 class wxGenericDataViewListModelNotifier
: public wxDataViewListModelNotifier
363 wxGenericDataViewListModelNotifier( wxDataViewMainWindow
*mainWindow
)
364 { m_mainWindow
= mainWindow
; }
366 virtual bool RowAppended()
367 { return m_mainWindow
->RowAppended(); }
368 virtual bool RowPrepended()
369 { return m_mainWindow
->RowPrepended(); }
370 virtual bool RowInserted( unsigned int before
)
371 { return m_mainWindow
->RowInserted( before
); }
372 virtual bool RowDeleted( unsigned int row
)
373 { return m_mainWindow
->RowDeleted( row
); }
374 virtual bool RowChanged( unsigned int row
)
375 { return m_mainWindow
->RowChanged( row
); }
376 virtual bool ValueChanged( unsigned int col
, unsigned int row
)
377 { return m_mainWindow
->ValueChanged( col
, row
); }
378 virtual bool RowsReordered( unsigned int *new_order
)
379 { return m_mainWindow
->RowsReordered( new_order
); }
380 virtual bool Cleared()
381 { return m_mainWindow
->Cleared(); }
383 wxDataViewMainWindow
*m_mainWindow
;
386 // ---------------------------------------------------------
387 // wxDataViewRenderer
388 // ---------------------------------------------------------
390 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer
, wxDataViewRendererBase
)
392 wxDataViewRenderer::wxDataViewRenderer( const wxString
&varianttype
,
393 wxDataViewCellMode mode
) :
394 wxDataViewRendererBase( varianttype
, mode
)
399 wxDataViewRenderer::~wxDataViewRenderer()
405 wxDC
*wxDataViewRenderer::GetDC()
409 if (GetOwner() == NULL
)
411 if (GetOwner()->GetOwner() == NULL
)
413 m_dc
= new wxClientDC( GetOwner()->GetOwner() );
419 // ---------------------------------------------------------
420 // wxDataViewCustomRenderer
421 // ---------------------------------------------------------
423 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer
, wxDataViewRenderer
)
425 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString
&varianttype
,
426 wxDataViewCellMode mode
) :
427 wxDataViewRenderer( varianttype
, mode
)
431 // ---------------------------------------------------------
432 // wxDataViewTextRenderer
433 // ---------------------------------------------------------
435 IMPLEMENT_CLASS(wxDataViewTextRenderer
, wxDataViewCustomRenderer
)
437 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString
&varianttype
,
438 wxDataViewCellMode mode
) :
439 wxDataViewCustomRenderer( varianttype
, mode
)
443 bool wxDataViewTextRenderer::SetValue( const wxVariant
&value
)
445 m_text
= value
.GetString();
450 bool wxDataViewTextRenderer::GetValue( wxVariant
& WXUNUSED(value
) )
455 bool wxDataViewTextRenderer::Render( wxRect cell
, wxDC
*dc
, int state
)
457 wxDataViewCtrl
*view
= GetOwner()->GetOwner();
458 wxColour col
= (state
& wxDATAVIEW_CELL_SELECTED
) ?
459 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
) :
460 view
->GetForegroundColour();
462 dc
->SetTextForeground(col
);
464 // TODO: it would be much more efficient to create a clipping
465 // region for the entire column being rendered (in the OnPaint
466 // of wxDataViewMainWindow) instead of a single clip region for
467 // each cell. However it would mean that each renderer should
468 // respect the given wxRect's top & bottom coords, eventually
469 // violating only the left & right coords - however the user can
470 // make its own renderer and thus we cannot be sure of that.
471 dc
->SetClippingRegion(cell
);
472 dc
->DrawText( m_text
, cell
.x
, cell
.y
);
473 dc
->DestroyClippingRegion();
478 wxSize
wxDataViewTextRenderer::GetSize()
480 wxDataViewCtrl
*view
= GetOwner()->GetOwner();
484 view
->GetTextExtent( m_text
, &x
, &y
);
485 return wxSize( x
, y
);
487 return wxSize(80,20);
490 // ---------------------------------------------------------
491 // wxDataViewBitmapRenderer
492 // ---------------------------------------------------------
494 IMPLEMENT_CLASS(wxDataViewBitmapRenderer
, wxDataViewCustomRenderer
)
496 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString
&varianttype
,
497 wxDataViewCellMode mode
) :
498 wxDataViewCustomRenderer( varianttype
, mode
)
502 bool wxDataViewBitmapRenderer::SetValue( const wxVariant
&value
)
504 if (value
.GetType() == wxT("wxBitmap"))
506 if (value
.GetType() == wxT("wxIcon"))
512 bool wxDataViewBitmapRenderer::GetValue( wxVariant
& WXUNUSED(value
) )
517 bool wxDataViewBitmapRenderer::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
520 dc
->DrawBitmap( m_bitmap
, cell
.x
, cell
.y
);
521 else if (m_icon
.Ok())
522 dc
->DrawIcon( m_icon
, cell
.x
, cell
.y
);
527 wxSize
wxDataViewBitmapRenderer::GetSize()
530 return wxSize( m_bitmap
.GetWidth(), m_bitmap
.GetHeight() );
531 else if (m_icon
.Ok())
532 return wxSize( m_icon
.GetWidth(), m_icon
.GetHeight() );
534 return wxSize(16,16);
537 // ---------------------------------------------------------
538 // wxDataViewToggleRenderer
539 // ---------------------------------------------------------
541 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer
, wxDataViewCustomRenderer
)
543 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString
&varianttype
,
544 wxDataViewCellMode mode
) :
545 wxDataViewCustomRenderer( varianttype
, mode
)
550 bool wxDataViewToggleRenderer::SetValue( const wxVariant
&value
)
552 m_toggle
= value
.GetBool();
557 bool wxDataViewToggleRenderer::GetValue( wxVariant
&WXUNUSED(value
) )
562 bool wxDataViewToggleRenderer::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
564 // User wxRenderer here
567 rect
.x
= cell
.x
+ cell
.width
/2 - 10;
569 rect
.y
= cell
.y
+ cell
.height
/2 - 10;
574 flags
|= wxCONTROL_CHECKED
;
575 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE
)
576 flags
|= wxCONTROL_DISABLED
;
578 wxRendererNative::Get().DrawCheckBox(
579 GetOwner()->GetOwner(),
587 bool wxDataViewToggleRenderer::Activate( wxRect
WXUNUSED(cell
),
588 wxDataViewListModel
*model
,
589 unsigned int col
, unsigned int row
)
591 bool value
= !m_toggle
;
592 wxVariant variant
= value
;
593 model
->SetValue( variant
, col
, row
);
594 model
->ValueChanged( col
, row
);
598 wxSize
wxDataViewToggleRenderer::GetSize()
600 return wxSize(20,20);
603 // ---------------------------------------------------------
604 // wxDataViewProgressRenderer
605 // ---------------------------------------------------------
607 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer
, wxDataViewCustomRenderer
)
609 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString
&label
,
610 const wxString
&varianttype
, wxDataViewCellMode mode
) :
611 wxDataViewCustomRenderer( varianttype
, mode
)
617 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
621 bool wxDataViewProgressRenderer::SetValue( const wxVariant
&value
)
623 m_value
= (long) value
;
625 if (m_value
< 0) m_value
= 0;
626 if (m_value
> 100) m_value
= 100;
631 bool wxDataViewProgressRenderer::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
633 double pct
= (double)m_value
/ 100.0;
635 bar
.width
= (int)(cell
.width
* pct
);
636 dc
->SetPen( *wxTRANSPARENT_PEN
);
637 dc
->SetBrush( *wxBLUE_BRUSH
);
638 dc
->DrawRectangle( bar
);
640 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
641 dc
->SetPen( *wxBLACK_PEN
);
642 dc
->DrawRectangle( cell
);
647 wxSize
wxDataViewProgressRenderer::GetSize()
649 return wxSize(40,12);
652 // ---------------------------------------------------------
653 // wxDataViewDateRenderer
654 // ---------------------------------------------------------
656 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
658 #if wxUSE_DATE_RENDERER_POPUP
660 class wxDataViewDateRendererPopupTransient
: public wxPopupTransientWindow
663 wxDataViewDateRendererPopupTransient( wxWindow
* parent
, wxDateTime
*value
,
664 wxDataViewListModel
*model
, unsigned int col
, unsigned int row
) :
665 wxPopupTransientWindow( parent
, wxBORDER_SIMPLE
)
670 m_cal
= new wxCalendarCtrl( this, wxID_ANY
, *value
);
671 wxBoxSizer
*sizer
= new wxBoxSizer( wxHORIZONTAL
);
672 sizer
->Add( m_cal
, 1, wxGROW
);
677 void OnCalendar( wxCalendarEvent
&event
);
679 wxCalendarCtrl
*m_cal
;
680 wxDataViewListModel
*m_model
;
685 virtual void OnDismiss()
690 DECLARE_EVENT_TABLE()
693 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient
,wxPopupTransientWindow
)
694 EVT_CALENDAR( wxID_ANY
, wxDataViewDateRendererPopupTransient::OnCalendar
)
697 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent
&event
)
699 wxDateTime date
= event
.GetDate();
700 wxVariant value
= date
;
701 m_model
->SetValue( value
, m_col
, m_row
);
702 m_model
->ValueChanged( m_col
, m_row
);
706 #endif // wxUSE_DATE_RENDERER_POPUP
708 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer
, wxDataViewCustomRenderer
)
710 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString
&varianttype
,
711 wxDataViewCellMode mode
) :
712 wxDataViewCustomRenderer( varianttype
, mode
)
716 bool wxDataViewDateRenderer::SetValue( const wxVariant
&value
)
718 m_date
= value
.GetDateTime();
723 bool wxDataViewDateRenderer::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
725 dc
->SetFont( GetOwner()->GetOwner()->GetFont() );
726 wxString tmp
= m_date
.FormatDate();
727 dc
->DrawText( tmp
, cell
.x
, cell
.y
);
732 wxSize
wxDataViewDateRenderer::GetSize()
734 wxDataViewCtrl
* view
= GetOwner()->GetOwner();
735 wxString tmp
= m_date
.FormatDate();
737 view
->GetTextExtent( tmp
, &x
, &y
, &d
);
738 return wxSize(x
,y
+d
);
741 bool wxDataViewDateRenderer::Activate( wxRect
WXUNUSED(cell
), wxDataViewListModel
*model
,
742 unsigned int col
, unsigned int row
)
745 model
->GetValue( variant
, col
, row
);
746 wxDateTime value
= variant
.GetDateTime();
748 #if wxUSE_DATE_RENDERER_POPUP
749 wxDataViewDateRendererPopupTransient
*popup
= new wxDataViewDateRendererPopupTransient(
750 GetOwner()->GetOwner()->GetParent(), &value
, model
, col
, row
);
751 wxPoint pos
= wxGetMousePosition();
754 popup
->Popup( popup
->m_cal
);
755 #else // !wxUSE_DATE_RENDERER_POPUP
756 wxMessageBox(value
.Format());
757 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
761 // ---------------------------------------------------------
763 // ---------------------------------------------------------
765 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn
, wxDataViewColumnBase
)
767 wxDataViewColumn::wxDataViewColumn( const wxString
&title
, wxDataViewRenderer
*cell
,
768 unsigned int model_column
,
769 int width
, wxAlignment align
, int flags
) :
770 wxDataViewColumnBase( title
, cell
, model_column
, width
, align
, flags
)
773 Init(width
< 0 ? 80 : width
);
776 wxDataViewColumn::wxDataViewColumn( const wxBitmap
&bitmap
, wxDataViewRenderer
*cell
,
777 unsigned int model_column
,
778 int width
, wxAlignment align
, int flags
) :
779 wxDataViewColumnBase( bitmap
, cell
, model_column
, width
, align
, flags
)
782 Init(width
< 0 ? 30 : width
);
785 void wxDataViewColumn::Init(int width
)
791 void wxDataViewColumn::SetSortable( bool WXUNUSED(sortable
) )
796 void wxDataViewColumn::SetSortOrder( bool WXUNUSED(ascending
) )
801 bool wxDataViewColumn::IsSortOrderAscending() const
808 wxDataViewColumn::~wxDataViewColumn()
812 void wxDataViewColumn::SetTitle( const wxString
&title
)
814 wxDataViewColumnBase::SetTitle( title
);
818 void wxDataViewColumn::SetBitmap( const wxBitmap
&bitmap
)
820 wxDataViewColumnBase::SetBitmap( bitmap
);
824 int wxDataViewColumn::GetWidth() const
829 //-----------------------------------------------------------------------------
830 // wxDataViewHeaderWindowBase
831 //-----------------------------------------------------------------------------
833 void wxDataViewHeaderWindowBase::SetColumnWidth(unsigned int n
, int width
)
835 GetColumn(n
)->m_width
= width
;
836 m_owner
->m_clientArea
->RecalculateDisplay();
839 void wxDataViewHeaderWindowBase::SendEvent(wxEventType type
, unsigned int n
)
841 wxWindow
*parent
= GetParent();
842 wxDataViewEvent
le(type
, parent
->GetId());
844 le
.SetEventObject(parent
);
846 le
.SetDataViewColumn(GetColumn(n
));
847 le
.SetModel(GetOwner()->GetModel());
849 // for events created by wxDataViewHeaderWindow the
850 // row / value fields are not valid
852 parent
->GetEventHandler()->ProcessEvent(le
);
855 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
857 // implemented in msw/window.cpp:
858 void wxAssociateWinWithHandle(HWND hWnd
, wxWindowMSW
*win
);
859 void wxRemoveHandleAssociation(wxWindowMSW
*win
);
861 // implemented in msw/listctrl.cpp:
862 unsigned int wxMSWGetColumnClicked(NMHDR
*nmhdr
, POINT
*ptClick
);
864 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW
, wxWindow
);
866 bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl
*parent
, wxWindowID id
,
867 const wxPoint
&pos
, const wxSize
&size
,
868 const wxString
&name
)
872 if ( !CreateControl(parent
, id
, pos
, size
, 0, wxDefaultValidator
, name
) )
875 int x
= pos
.x
== wxDefaultCoord
? 0 : pos
.x
,
876 y
= pos
.y
== wxDefaultCoord
? 0 : pos
.y
,
877 w
= size
.x
== wxDefaultCoord
? 1 : size
.x
,
878 h
= size
.y
== wxDefaultCoord
? 22 : size
.y
;
880 // create the native WC_HEADER window:
881 WXHWND hwndParent
= (HWND
)parent
->GetHandle();
882 WXDWORD msStyle
= WS_CHILD
| HDS_BUTTONS
| HDS_HORZ
| HDS_HOTTRACK
| HDS_FULLDRAG
;
883 m_hWnd
= CreateWindowEx(0,
894 wxLogLastError(_T("CreateWindowEx"));
898 // we need to do the association to force wxWindow::HandleNotify
899 // to call wxDataViewHeaderWindow::MSWOnNotify
900 wxAssociateWinWithHandle((HWND
)m_hWnd
, this);
907 // Retrieve the bounding rectangle of the parent window's
908 // client area, and then request size and position values
909 // from the header control.
910 ::GetClientRect((HWND
)hwndParent
, &rcParent
);
914 if (!SendMessage((HWND
)m_hWnd
, HDM_LAYOUT
, 0, (LPARAM
) &hdl
))
916 wxLogLastError(_T("SendMessage"));
920 // Set the size, position, and visibility of the header control.
921 SetWindowPos((HWND
)m_hWnd
,
925 wp
.flags
| SWP_SHOWWINDOW
);
927 // set our size hints: wxDataViewCtrl will put this wxWindow inside
928 // a wxBoxSizer and in order to avoid super-big header windows,
929 // we need to set our height as fixed
930 SetMinSize(wxSize(-1, wp
.cy
));
931 SetMaxSize(wxSize(-1, wp
.cy
));
933 // the following is required to get the default win's font for header windows
939 wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
941 wxRemoveHandleAssociation(this);
944 void wxDataViewHeaderWindowMSW::UpdateDisplay()
946 // remove old columns
947 for (int i
=0, max
=Header_GetItemCount((HWND
)m_hWnd
); i
< max
; i
++)
948 Header_DeleteItem((HWND
)m_hWnd
, 0);
950 // add the updated array of columns to the header control
951 unsigned int cols
= GetOwner()->GetNumberOfColumns();
952 for (unsigned int i
= 0; i
< cols
; i
++)
954 wxDataViewColumn
*col
= GetColumn( i
);
956 continue; // don't add it!
959 hdi
.mask
= HDI_TEXT
| HDI_FORMAT
| HDI_WIDTH
;
960 hdi
.pszText
= (WCHAR
*) col
->GetTitle().c_str();
961 hdi
.cxy
= col
->GetWidth();
962 hdi
.cchTextMax
= sizeof(hdi
.pszText
)/sizeof(hdi
.pszText
[0]);
963 hdi
.fmt
= HDF_LEFT
| HDF_STRING
;
965 SendMessage((HWND
)m_hWnd
, HDM_INSERTITEM
, (WPARAM
)i
, (LPARAM
)&hdi
);
969 bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
971 NMHDR
*nmhdr
= (NMHDR
*)lParam
;
973 // is it a message from the header?
974 if ( nmhdr
->hwndFrom
!= (HWND
)m_hWnd
)
975 return wxWindow::MSWOnNotify(idCtrl
, lParam
, result
);
977 NMHEADER
*nmHDR
= (NMHEADER
*)nmhdr
;
978 switch ( nmhdr
->code
)
981 // user has started to resize a column:
982 // do we need to veto it?
983 if (!GetColumn(nmHDR
->iItem
)->IsResizeable())
991 // user has started to reorder a column
994 case HDN_ITEMCHANGED
: // user is resizing a column
995 case HDN_ENDTRACK
: // user has finished resizing a column
996 case HDN_ENDDRAG
: // user has finished reordering a column
998 // update the width of the modified column:
999 if ((nmHDR
->pitem
->mask
& HDI_WIDTH
) != 0 &&
1000 nmHDR
->pitem
!= NULL
)
1001 SetColumnWidth(nmHDR
->iItem
, nmHDR
->pitem
->cxy
);
1006 wxEventType evt
= nmHDR
->iButton
== 0 ?
1007 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK
:
1008 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK
;
1009 SendEvent(evt
, nmHDR
->iItem
);
1015 // NOTE: for some reason (i.e. for a bug in Windows)
1016 // the HDN_ITEMCLICK notification is not sent on
1017 // right clicks, so we need to handle NM_RCLICK
1020 unsigned int column
=
1021 wxMSWGetColumnClicked(nmhdr
, &ptClick
);
1023 if (column
!= wxNOT_FOUND
)
1024 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK
,
1029 case HDN_GETDISPINFOW
:
1030 // see wxListCtrl::MSWOnNotify for more info!
1033 case HDN_ITEMDBLCLICK
:
1035 unsigned int idx
= nmHDR
->iItem
;
1036 int w
= GetOwner()->GetBestColumnWidth(idx
);
1038 // update the native control:
1040 ZeroMemory(&hd
, sizeof(hd
));
1041 hd
.mask
= HDI_WIDTH
;
1043 Header_SetItem(GetHwnd(), idx
, &hd
);
1045 // update the wxDataViewColumn class:
1046 SetColumnWidth(idx
, w
);
1051 return wxWindow::MSWOnNotify(idCtrl
, lParam
, result
);
1057 void wxDataViewHeaderWindowMSW::ScrollWindow(int WXUNUSED(dx
), int WXUNUSED(dy
),
1058 const wxRect
*WXUNUSED(rect
))
1060 wxSize ourSz
= GetClientSize();
1061 wxSize ownerSz
= m_owner
->GetClientSize();
1063 // where should the (logical) origin of this window be placed?
1065 m_owner
->CalcUnscrolledPosition(0, 0, &x1
, &y1
);
1067 // put this window on top of our parent and
1068 SetWindowPos((HWND
)m_hWnd
, HWND_TOP
, -x1
, y1
,
1069 ownerSz
.GetWidth() + x1
, ourSz
.GetHeight(),
1073 #else // !defined(__WXMSW__)
1075 IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow
, wxWindow
)
1076 BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow
, wxWindow
)
1077 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint
)
1078 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse
)
1079 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus
)
1082 bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl
*parent
, wxWindowID id
,
1083 const wxPoint
&pos
, const wxSize
&size
,
1084 const wxString
&name
)
1088 if (!wxDataViewHeaderWindowBase::Create(parent
, id
, pos
, size
, name
))
1091 wxVisualAttributes attr
= wxPanel::GetClassDefaultAttributes();
1092 SetBackgroundStyle( wxBG_STYLE_CUSTOM
);
1093 SetOwnForegroundColour( attr
.colFg
);
1094 SetOwnBackgroundColour( attr
.colBg
);
1096 SetOwnFont( attr
.font
);
1098 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1099 // a wxBoxSizer and in order to avoid super-big header windows,
1100 // we need to set our height as fixed
1101 SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT
));
1102 SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT
));
1107 void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
1110 GetClientSize( &w
, &h
);
1112 wxAutoBufferedPaintDC
dc( this );
1114 dc
.SetBackground(GetBackgroundColour());
1118 m_owner
->GetScrollPixelsPerUnit( &xpix
, NULL
);
1121 m_owner
->GetViewStart( &x
, NULL
);
1123 // account for the horz scrollbar offset
1124 dc
.SetDeviceOrigin( -x
* xpix
, 0 );
1126 dc
.SetFont( GetFont() );
1128 unsigned int cols
= GetOwner()->GetNumberOfColumns();
1131 for (i
= 0; i
< cols
; i
++)
1133 wxDataViewColumn
*col
= GetColumn( i
);
1134 if (col
->IsHidden())
1135 break; // don't draw it!
1137 int cw
= col
->GetWidth();
1140 wxRendererNative::Get().DrawHeaderButton
1144 wxRect(xpos
, 0, cw
, ch
-1),
1145 m_parent
->IsEnabled() ? 0
1146 : (int)wxCONTROL_DISABLED
1149 dc
.DrawText( col
->GetTitle(), xpos
+3, 3 );
1155 void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent
&event
)
1157 GetParent()->SetFocus();
1161 void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent
&event
)
1163 // we want to work with logical coords
1165 m_owner
->CalcUnscrolledPosition(event
.GetX(), 0, &x
, NULL
);
1166 int y
= event
.GetY();
1170 // we don't draw the line beyond our window,
1171 // but we allow dragging it there
1173 GetClientSize( &w
, NULL
);
1174 m_owner
->CalcUnscrolledPosition(w
, 0, &w
, NULL
);
1177 // erase the line if it was drawn
1181 if (event
.ButtonUp())
1183 m_isDragging
= false;
1189 SetColumnWidth(m_column
, m_currentX
- m_minX
);
1192 GetOwner()->Refresh();
1196 m_currentX
= wxMax(m_minX
+ 7, x
);
1198 // draw in the new location
1199 if (m_currentX
< w
) DrawCurrent();
1203 else // not dragging
1206 m_column
= wxNOT_FOUND
;
1208 bool hit_border
= false;
1210 // end of the current column
1213 // find the column where this event occured
1214 int countCol
= m_owner
->GetNumberOfColumns();
1215 for (int column
= 0; column
< countCol
; column
++)
1217 wxDataViewColumn
*p
= GetColumn(column
);
1220 continue; // skip if not shown
1222 xpos
+= p
->GetWidth();
1224 if ((abs(x
-xpos
) < 3) && (y
< 22))
1232 // inside the column
1239 if (m_column
== wxNOT_FOUND
)
1242 bool resizeable
= GetColumn(m_column
)->IsResizeable();
1243 if (event
.LeftDClick() && resizeable
)
1245 SetColumnWidth(m_column
, GetOwner()->GetBestColumnWidth(m_column
));
1248 else if (event
.LeftDown() || event
.RightUp())
1250 if (hit_border
&& event
.LeftDown() && resizeable
)
1252 m_isDragging
= true;
1257 else // click on a column
1259 wxEventType evt
= event
.LeftDown() ?
1260 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK
:
1261 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK
;
1262 SendEvent(evt
, m_column
);
1265 else if (event
.Moving())
1267 if (hit_border
&& resizeable
)
1268 m_currentCursor
= m_resizeCursor
;
1270 m_currentCursor
= wxSTANDARD_CURSOR
;
1272 SetCursor(*m_currentCursor
);
1277 void wxGenericDataViewHeaderWindow::DrawCurrent()
1279 int x1
= m_currentX
;
1281 ClientToScreen (&x1
, &y1
);
1283 int x2
= m_currentX
-1;
1285 ++x2
; // but why ????
1288 m_owner
->GetClientSize( NULL
, &y2
);
1289 m_owner
->ClientToScreen( &x2
, &y2
);
1292 dc
.SetLogicalFunction (wxINVERT
);
1293 //dc.SetPen (wxPen (*wxBLACK, 2, wxSOLID));
1294 dc
.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
),1, wxSOLID
));
1295 dc
.SetBrush (*wxTRANSPARENT_BRUSH
);
1298 dc
.DrawLine (x1
, y1
, x2
, y2
);
1299 dc
.SetLogicalFunction (wxCOPY
);
1300 dc
.SetPen (wxNullPen
);
1301 dc
.SetBrush (wxNullBrush
);
1304 void wxGenericDataViewHeaderWindow::AdjustDC(wxDC
& dc
)
1308 m_owner
->GetScrollPixelsPerUnit( &xpix
, NULL
);
1309 m_owner
->GetViewStart( &x
, NULL
);
1311 // shift the DC origin to match the position of the main window horizontal
1312 // scrollbar: this allows us to always use logical coords
1313 dc
.SetDeviceOrigin( -x
* xpix
, 0 );
1316 #endif // defined(__WXMSW__)
1318 //-----------------------------------------------------------------------------
1319 // wxDataViewRenameTimer
1320 //-----------------------------------------------------------------------------
1322 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow
*owner
)
1327 void wxDataViewRenameTimer::Notify()
1329 m_owner
->OnRenameTimer();
1332 //-----------------------------------------------------------------------------
1333 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
1334 //-----------------------------------------------------------------------------
1336 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper
, wxEvtHandler
)
1337 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar
)
1338 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp
)
1339 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus
)
1342 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
1343 wxDataViewMainWindow
*owner
,
1345 wxDataViewListModel
*model
,
1346 unsigned int col
, unsigned int row
,
1356 m_aboutToFinish
= false;
1359 model
->GetValue( value
, col
, row
);
1360 m_startValue
= value
.GetString();
1362 m_owner
->GetOwner()->CalcScrolledPosition(
1363 rectLabel
.x
, rectLabel
.y
, &rectLabel
.x
, &rectLabel
.y
);
1365 m_text
->Create( owner
, wxID_ANY
, m_startValue
,
1366 wxPoint(rectLabel
.x
-2,rectLabel
.y
-2),
1367 wxSize(rectLabel
.width
+7,rectLabel
.height
+4) );
1370 m_text
->PushEventHandler(this);
1373 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
1375 m_aboutToFinish
= true;
1377 // Notify the owner about the changes
1380 // Even if vetoed, close the control (consistent with MSW)
1384 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent
&event
)
1386 switch ( event
.m_keyCode
)
1389 AcceptChangesAndFinish();
1393 // m_owner->OnRenameCancelled( m_itemEdited );
1402 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent
&event
)
1410 // auto-grow the textctrl
1411 wxSize parentSize
= m_owner
->GetSize();
1412 wxPoint myPos
= m_text
->GetPosition();
1413 wxSize mySize
= m_text
->GetSize();
1415 m_text
->GetTextExtent(m_text
->GetValue() + _T("MM"), &sx
, &sy
);
1416 if (myPos
.x
+ sx
> parentSize
.x
)
1417 sx
= parentSize
.x
- myPos
.x
;
1420 m_text
->SetSize(sx
, wxDefaultCoord
);
1425 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent
&event
)
1427 if ( !m_finished
&& !m_aboutToFinish
)
1430 //if ( !AcceptChanges() )
1431 // m_owner->OnRenameCancelled( m_itemEdited );
1436 // We must let the native text control handle focus
1440 bool wxDataViewTextCtrlWrapper::AcceptChanges()
1442 const wxString value
= m_text
->GetValue();
1444 if ( value
== m_startValue
)
1445 // nothing changed, always accept
1448 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
1449 // vetoed by the user
1452 // accepted, do rename the item
1455 m_model
->SetValue( variant
, m_col
, m_row
);
1456 m_model
->ValueChanged( m_col
, m_row
);
1461 void wxDataViewTextCtrlWrapper::Finish()
1467 m_text
->RemoveEventHandler(this);
1468 m_owner
->FinishEditing(m_text
);
1471 wxPendingDelete
.Append( this );
1475 //-----------------------------------------------------------------------------
1476 // wxDataViewMainWindow
1477 //-----------------------------------------------------------------------------
1479 int LINKAGEMODE
wxDataViewSelectionCmp( unsigned int row1
, unsigned int row2
)
1481 if (row1
> row2
) return 1;
1482 if (row1
== row2
) return 0;
1487 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow
, wxWindow
)
1489 BEGIN_EVENT_TABLE(wxDataViewMainWindow
,wxWindow
)
1490 EVT_PAINT (wxDataViewMainWindow::OnPaint
)
1491 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse
)
1492 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus
)
1493 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus
)
1494 EVT_CHAR (wxDataViewMainWindow::OnChar
)
1497 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl
*parent
, wxWindowID id
,
1498 const wxPoint
&pos
, const wxSize
&size
, const wxString
&name
) :
1499 wxWindow( parent
, id
, pos
, size
, wxWANTS_CHARS
, name
),
1500 m_selection( wxDataViewSelectionCmp
)
1505 m_lastOnSame
= false;
1506 m_renameTimer
= new wxDataViewRenameTimer( this );
1507 m_textctrlWrapper
= NULL
;
1509 // TODO: user better initial values/nothing selected
1510 m_currentCol
= NULL
;
1513 // TODO: we need to calculate this smartly
1522 m_dragStart
= wxPoint(0,0);
1523 m_lineLastClicked
= (unsigned int) -1;
1524 m_lineBeforeLastClicked
= (unsigned int) -1;
1525 m_lineSelectSingleOnUp
= (unsigned int) -1;
1529 SetBackgroundStyle( wxBG_STYLE_CUSTOM
);
1530 SetBackgroundColour( *wxWHITE
);
1535 wxDataViewMainWindow::~wxDataViewMainWindow()
1537 delete m_renameTimer
;
1540 void wxDataViewMainWindow::OnRenameTimer()
1542 // We have to call this here because changes may just have
1543 // been made and no screen update taken place.
1549 unsigned int cols
= GetOwner()->GetNumberOfColumns();
1551 for (i
= 0; i
< cols
; i
++)
1553 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
1554 if (c
== m_currentCol
)
1556 xpos
+= c
->GetWidth();
1558 wxRect
labelRect( xpos
, m_currentRow
* m_lineHeight
,
1559 m_currentCol
->GetWidth(), m_lineHeight
);
1561 wxClassInfo
*textControlClass
= CLASSINFO(wxTextCtrl
);
1563 wxTextCtrl
* const text
= (wxTextCtrl
*)textControlClass
->CreateObject();
1564 m_textctrlWrapper
= new wxDataViewTextCtrlWrapper(this, text
, GetOwner()->GetModel(),
1565 m_currentCol
->GetModelColumn(), m_currentRow
, labelRect
);
1568 void wxDataViewMainWindow::FinishEditing( wxTextCtrl
*text
)
1571 m_textctrlWrapper
= NULL
;
1573 // SetFocusIgnoringChildren();
1576 bool wxDataViewMainWindow::RowAppended()
1581 bool wxDataViewMainWindow::RowPrepended()
1586 bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before
) )
1591 bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row
) )
1596 bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row
) )
1601 bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col
), unsigned int row
)
1603 wxRect
rect( 0, row
*m_lineHeight
, 10000, m_lineHeight
);
1604 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1605 Refresh( true, &rect
);
1610 bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order
) )
1617 bool wxDataViewMainWindow::Cleared()
1622 void wxDataViewMainWindow::UpdateDisplay()
1627 void wxDataViewMainWindow::OnInternalIdle()
1629 wxWindow::OnInternalIdle();
1633 RecalculateDisplay();
1638 void wxDataViewMainWindow::RecalculateDisplay()
1640 wxDataViewListModel
*model
= GetOwner()->GetModel();
1648 unsigned int cols
= GetOwner()->GetNumberOfColumns();
1650 for (i
= 0; i
< cols
; i
++)
1652 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
1653 width
+= col
->GetWidth();
1656 int height
= model
->GetNumberOfRows() * m_lineHeight
;
1658 SetVirtualSize( width
, height
);
1659 GetOwner()->SetScrollRate( 10, m_lineHeight
);
1664 void wxDataViewMainWindow::ScrollWindow( int dx
, int dy
, const wxRect
*rect
)
1666 wxWindow::ScrollWindow( dx
, dy
, rect
);
1667 GetOwner()->m_headerArea
->ScrollWindow( dx
, 0 );
1670 void wxDataViewMainWindow::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
1672 wxAutoBufferedPaintDC
dc( this );
1674 dc
.SetBackground(GetBackgroundColour());
1677 GetOwner()->PrepareDC( dc
);
1679 dc
.SetFont( GetFont() );
1681 wxRect update
= GetUpdateRegion().GetBox();
1682 m_owner
->CalcUnscrolledPosition( update
.x
, update
.y
, &update
.x
, &update
.y
);
1684 wxDataViewListModel
*model
= GetOwner()->GetModel();
1686 unsigned int item_start
= wxMax( 0, (update
.y
/ m_lineHeight
) );
1687 unsigned int item_count
=
1688 wxMin( (int)(((update
.y
+ update
.height
) / m_lineHeight
) - item_start
+ 1),
1689 (int)(model
->GetNumberOfRows()-item_start
) );
1692 for (item
= item_start
; item
< item_start
+item_count
; item
++)
1694 bool selected
= m_selection
.Index( item
) != wxNOT_FOUND
;
1695 if (selected
|| item
== m_currentRow
)
1697 int flags
= selected
? wxCONTROL_SELECTED
: 0;
1698 if (item
== m_currentRow
)
1699 flags
|= wxCONTROL_CURRENT
;
1701 flags
|= wxCONTROL_FOCUSED
;
1702 wxRect
rect( 0, item
*m_lineHeight
, GetEndOfLastCol(), m_lineHeight
);
1703 wxRendererNative::Get().DrawItemSelectionRect
1715 cell_rect
.height
= m_lineHeight
;
1716 unsigned int cols
= GetOwner()->GetNumberOfColumns();
1718 for (i
= 0; i
< cols
; i
++)
1720 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
1721 wxDataViewRenderer
*cell
= col
->GetRenderer();
1722 cell_rect
.width
= col
->GetWidth();
1724 if (col
->IsHidden())
1725 continue; // skipt it!
1727 for (item
= item_start
; item
< item_start
+item_count
; item
++)
1729 // get the cell value and set it into the renderer
1731 model
->GetValue( value
, col
->GetModelColumn(), item
);
1732 cell
->SetValue( value
);
1734 // update the y offset
1735 cell_rect
.y
= item
*m_lineHeight
;
1737 // cannot be bigger than allocated space
1738 wxSize size
= cell
->GetSize();
1739 size
.x
= wxMin( size
.x
, cell_rect
.width
);
1740 size
.y
= wxMin( size
.y
, cell_rect
.height
);
1742 wxRect
item_rect(cell_rect
.GetTopLeft(), size
);
1744 // horizontal alignment:
1745 if (col
->GetAlignment() & wxALIGN_CENTER_HORIZONTAL
)
1746 item_rect
.x
= cell_rect
.x
+ (cell_rect
.width
/ 2) - (size
.x
/ 2);
1747 else if (col
->GetAlignment() & wxALIGN_RIGHT
)
1748 item_rect
.x
= cell_rect
.x
+ cell_rect
.width
- size
.x
;
1749 //else: wxALIGN_LEFT is the default
1751 // vertical alignment:
1752 if (col
->GetAlignment() & wxALIGN_CENTER_VERTICAL
)
1753 item_rect
.y
= cell_rect
.y
+ (cell_rect
.height
/ 2) - (size
.y
/ 2);
1754 else if (col
->GetAlignment() & wxALIGN_BOTTOM
)
1755 item_rect
.y
= cell_rect
.y
+ cell_rect
.height
- size
.y
;
1756 //else: wxALIGN_TOP is the default
1758 item_rect
.y
= cell_rect
.y
+ (cell_rect
.height
/ 2) - (size
.y
/ 2);
1760 item_rect
.width
= size
.x
;
1761 item_rect
.height
= size
.y
;
1764 //if (item == m_currentRow) -- seems wrong to me...
1765 if (m_selection
.Index(item
) != wxNOT_FOUND
)
1766 state
|= wxDATAVIEW_CELL_SELECTED
;
1768 cell
->Render( item_rect
, &dc
, state
);
1771 cell_rect
.x
+= cell_rect
.width
;
1775 int wxDataViewMainWindow::GetCountPerPage()
1777 wxSize size
= GetClientSize();
1778 return size
.y
/ m_lineHeight
;
1781 int wxDataViewMainWindow::GetEndOfLastCol()
1785 for (i
= 0; i
< GetOwner()->GetNumberOfColumns(); i
++)
1787 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
1788 width
+= c
->GetWidth();
1793 unsigned int wxDataViewMainWindow::GetFirstVisibleRow()
1797 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
1799 return y
/ m_lineHeight
;
1802 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
1804 wxSize client_size
= GetClientSize();
1805 m_owner
->CalcUnscrolledPosition( client_size
.x
, client_size
.y
,
1806 &client_size
.x
, &client_size
.y
);
1808 return wxMin( GetRowCount()-1, ((unsigned)client_size
.y
/m_lineHeight
)+1 );
1811 unsigned int wxDataViewMainWindow::GetRowCount()
1813 return GetOwner()->GetModel()->GetNumberOfRows();
1816 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row
)
1823 void wxDataViewMainWindow::SelectAllRows( bool on
)
1830 m_selection
.Clear();
1831 for (unsigned int i
= 0; i
< GetRowCount(); i
++)
1832 m_selection
.Add( i
);
1837 unsigned int first_visible
= GetFirstVisibleRow();
1838 unsigned int last_visible
= GetLastVisibleRow();
1840 for (i
= 0; i
< m_selection
.GetCount(); i
++)
1842 unsigned int row
= m_selection
[i
];
1843 if ((row
>= first_visible
) && (row
<= last_visible
))
1846 m_selection
.Clear();
1850 void wxDataViewMainWindow::SelectRow( unsigned int row
, bool on
)
1852 if (m_selection
.Index( row
) == wxNOT_FOUND
)
1856 m_selection
.Add( row
);
1864 m_selection
.Remove( row
);
1870 void wxDataViewMainWindow::SelectRows( unsigned int from
, unsigned int to
, bool on
)
1874 unsigned int tmp
= from
;
1880 for (i
= from
; i
<= to
; i
++)
1882 if (m_selection
.Index( i
) == wxNOT_FOUND
)
1885 m_selection
.Add( i
);
1890 m_selection
.Remove( i
);
1893 RefreshRows( from
, to
);
1896 void wxDataViewMainWindow::Select( const wxArrayInt
& aSelections
)
1898 for (size_t i
=0; i
< aSelections
.GetCount(); i
++)
1900 int n
= aSelections
[i
];
1902 m_selection
.Add( n
);
1907 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row
)
1909 if (m_selection
.Index( row
) == wxNOT_FOUND
)
1910 m_selection
.Add( row
);
1912 m_selection
.Remove( row
);
1916 bool wxDataViewMainWindow::IsRowSelected( unsigned int row
)
1918 return (m_selection
.Index( row
) != wxNOT_FOUND
);
1921 void wxDataViewMainWindow::RefreshRow( unsigned int row
)
1923 wxRect
rect( 0, row
*m_lineHeight
, GetEndOfLastCol(), m_lineHeight
);
1924 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1926 wxSize client_size
= GetClientSize();
1927 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1928 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1929 if (intersect_rect
.width
> 0)
1930 Refresh( true, &intersect_rect
);
1933 void wxDataViewMainWindow::RefreshRows( unsigned int from
, unsigned int to
)
1937 unsigned int tmp
= to
;
1942 wxRect
rect( 0, from
*m_lineHeight
, GetEndOfLastCol(), (to
-from
+1) * m_lineHeight
);
1943 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1945 wxSize client_size
= GetClientSize();
1946 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1947 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1948 if (intersect_rect
.width
> 0)
1949 Refresh( true, &intersect_rect
);
1952 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow
)
1954 unsigned int count
= GetRowCount();
1955 if (firstRow
> count
)
1958 wxRect
rect( 0, firstRow
*m_lineHeight
, GetEndOfLastCol(), count
* m_lineHeight
);
1959 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
1961 wxSize client_size
= GetClientSize();
1962 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
1963 wxRect intersect_rect
= client_rect
.Intersect( rect
);
1964 if (intersect_rect
.width
> 0)
1965 Refresh( true, &intersect_rect
);
1968 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent
, const wxKeyEvent
& event
)
1970 wxCHECK_RET( newCurrent
< GetRowCount(),
1971 _T("invalid item index in OnArrowChar()") );
1973 // if there is no selection, we cannot move it anywhere
1974 if (!HasCurrentRow())
1977 unsigned int oldCurrent
= m_currentRow
;
1979 // in single selection we just ignore Shift as we can't select several
1981 if ( event
.ShiftDown() && !IsSingleSel() )
1983 RefreshRow( oldCurrent
);
1985 ChangeCurrentRow( newCurrent
);
1987 // select all the items between the old and the new one
1988 if ( oldCurrent
> newCurrent
)
1990 newCurrent
= oldCurrent
;
1991 oldCurrent
= m_currentRow
;
1994 SelectRows( oldCurrent
, newCurrent
, true );
1998 RefreshRow( oldCurrent
);
2000 // all previously selected items are unselected unless ctrl is held
2001 if ( !event
.ControlDown() )
2002 SelectAllRows(false);
2004 ChangeCurrentRow( newCurrent
);
2006 if ( !event
.ControlDown() )
2007 SelectRow( m_currentRow
, true );
2009 RefreshRow( m_currentRow
);
2015 void wxDataViewMainWindow::OnChar( wxKeyEvent
&event
)
2017 if (event
.GetKeyCode() == WXK_TAB
)
2019 wxNavigationKeyEvent nevent
;
2020 nevent
.SetWindowChange( event
.ControlDown() );
2021 nevent
.SetDirection( !event
.ShiftDown() );
2022 nevent
.SetEventObject( GetParent()->GetParent() );
2023 nevent
.SetCurrentFocus( m_parent
);
2024 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent
))
2028 // no item -> nothing to do
2029 if (!HasCurrentRow())
2035 // don't use m_linesPerPage directly as it might not be computed yet
2036 const int pageSize
= GetCountPerPage();
2037 wxCHECK_RET( pageSize
, _T("should have non zero page size") );
2039 switch ( event
.GetKeyCode() )
2042 if ( m_currentRow
> 0 )
2043 OnArrowChar( m_currentRow
- 1, event
);
2047 if ( m_currentRow
< GetRowCount() - 1 )
2048 OnArrowChar( m_currentRow
+ 1, event
);
2053 OnArrowChar( GetRowCount() - 1, event
);
2058 OnArrowChar( 0, event
);
2063 int steps
= pageSize
- 1;
2064 int index
= m_currentRow
- steps
;
2068 OnArrowChar( index
, event
);
2074 int steps
= pageSize
- 1;
2075 unsigned int index
= m_currentRow
+ steps
;
2076 unsigned int count
= GetRowCount();
2077 if ( index
>= count
)
2080 OnArrowChar( index
, event
);
2089 void wxDataViewMainWindow::OnMouse( wxMouseEvent
&event
)
2091 if (event
.GetEventType() == wxEVT_MOUSEWHEEL
)
2093 // let the base handle mouse wheel events.
2098 int x
= event
.GetX();
2099 int y
= event
.GetY();
2100 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
2102 wxDataViewColumn
*col
= NULL
;
2105 unsigned int cols
= GetOwner()->GetNumberOfColumns();
2107 for (i
= 0; i
< cols
; i
++)
2109 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
2110 if (x
< xpos
+ c
->GetWidth())
2115 xpos
+= c
->GetWidth();
2119 wxDataViewRenderer
*cell
= col
->GetRenderer();
2121 unsigned int current
= y
/ m_lineHeight
;
2123 if ((current
> GetRowCount()) || (x
> GetEndOfLastCol()))
2125 // Unselect all if below the last row ?
2129 wxDataViewListModel
*model
= GetOwner()->GetModel();
2131 if (event
.Dragging())
2133 if (m_dragCount
== 0)
2135 // we have to report the raw, physical coords as we want to be
2136 // able to call HitTest(event.m_pointDrag) from the user code to
2137 // get the item being dragged
2138 m_dragStart
= event
.GetPosition();
2143 if (m_dragCount
!= 3)
2146 if (event
.LeftIsDown())
2148 // Notify cell about drag
2157 bool forceClick
= false;
2159 if (event
.ButtonDClick())
2161 m_renameTimer
->Stop();
2162 m_lastOnSame
= false;
2165 if (event
.LeftDClick())
2167 if ( current
== m_lineLastClicked
)
2169 if (cell
->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE
)
2172 model
->GetValue( value
, col
->GetModelColumn(), current
);
2173 cell
->SetValue( value
);
2174 wxRect
cell_rect( xpos
, current
* m_lineHeight
,
2175 col
->GetWidth(), m_lineHeight
);
2176 cell
->Activate( cell_rect
, model
, col
->GetModelColumn(), current
);
2182 // The first click was on another item, so don't interpret this as
2183 // a double click, but as a simple click instead
2190 if (m_lineSelectSingleOnUp
!= (unsigned int)-1)
2192 // select single line
2193 SelectAllRows( false );
2194 SelectRow( m_lineSelectSingleOnUp
, true );
2199 if ((col
== m_currentCol
) && (current
== m_currentRow
) &&
2200 (cell
->GetMode() == wxDATAVIEW_CELL_EDITABLE
) )
2202 m_renameTimer
->Start( 100, true );
2206 m_lastOnSame
= false;
2207 m_lineSelectSingleOnUp
= (unsigned int)-1;
2211 // This is necessary, because after a DnD operation in
2212 // from and to ourself, the up event is swallowed by the
2213 // DnD code. So on next non-up event (which means here and
2214 // now) m_lineSelectSingleOnUp should be reset.
2215 m_lineSelectSingleOnUp
= (unsigned int)-1;
2218 if (event
.RightDown())
2220 m_lineBeforeLastClicked
= m_lineLastClicked
;
2221 m_lineLastClicked
= current
;
2223 // If the item is already selected, do not update the selection.
2224 // Multi-selections should not be cleared if a selected item is clicked.
2225 if (!IsRowSelected(current
))
2227 SelectAllRows(false);
2228 ChangeCurrentRow(current
);
2229 SelectRow(m_currentRow
,true);
2232 // notify cell about right click
2235 // Allow generation of context menu event
2238 else if (event
.MiddleDown())
2240 // notify cell about middle click
2243 if (event
.LeftDown() || forceClick
)
2249 m_lineBeforeLastClicked
= m_lineLastClicked
;
2250 m_lineLastClicked
= current
;
2252 unsigned int oldCurrentRow
= m_currentRow
;
2253 bool oldWasSelected
= IsRowSelected(m_currentRow
);
2255 bool cmdModifierDown
= event
.CmdDown();
2256 if ( IsSingleSel() || !(cmdModifierDown
|| event
.ShiftDown()) )
2258 if ( IsSingleSel() || !IsRowSelected(current
) )
2260 SelectAllRows( false );
2262 ChangeCurrentRow(current
);
2264 SelectRow(m_currentRow
,true);
2266 else // multi sel & current is highlighted & no mod keys
2268 m_lineSelectSingleOnUp
= current
;
2269 ChangeCurrentRow(current
); // change focus
2272 else // multi sel & either ctrl or shift is down
2274 if (cmdModifierDown
)
2276 ChangeCurrentRow(current
);
2278 ReverseRowSelection(m_currentRow
);
2280 else if (event
.ShiftDown())
2282 ChangeCurrentRow(current
);
2284 unsigned int lineFrom
= oldCurrentRow
,
2287 if ( lineTo
< lineFrom
)
2290 lineFrom
= m_currentRow
;
2293 SelectRows(lineFrom
, lineTo
, true);
2295 else // !ctrl, !shift
2297 // test in the enclosing if should make it impossible
2298 wxFAIL_MSG( _T("how did we get here?") );
2302 if (m_currentRow
!= oldCurrentRow
)
2303 RefreshRow( oldCurrentRow
);
2305 wxDataViewColumn
*oldCurrentCol
= m_currentCol
;
2307 // Update selection here...
2310 m_lastOnSame
= !forceClick
&& ((col
== oldCurrentCol
) &&
2311 (current
== oldCurrentRow
)) && oldWasSelected
;
2315 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent
&event
)
2319 if (HasCurrentRow())
2325 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent
&event
)
2329 if (HasCurrentRow())
2335 //-----------------------------------------------------------------------------
2337 //-----------------------------------------------------------------------------
2339 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl
, wxDataViewCtrlBase
)
2341 BEGIN_EVENT_TABLE(wxDataViewCtrl
, wxDataViewCtrlBase
)
2342 EVT_SIZE(wxDataViewCtrl::OnSize
)
2345 wxDataViewCtrl::~wxDataViewCtrl()
2348 GetModel()->RemoveNotifier( m_notifier
);
2351 void wxDataViewCtrl::Init()
2356 bool wxDataViewCtrl::Create(wxWindow
*parent
, wxWindowID id
,
2357 const wxPoint
& pos
, const wxSize
& size
,
2358 long style
, const wxValidator
& validator
)
2360 if (!wxControl::Create( parent
, id
, pos
, size
,
2361 style
| wxScrolledWindowStyle
|wxSUNKEN_BORDER
, validator
))
2367 MacSetClipChildren( true ) ;
2370 m_clientArea
= new wxDataViewMainWindow( this, wxID_ANY
);
2371 m_headerArea
= new wxDataViewHeaderWindow( this, wxID_ANY
);
2373 SetTargetWindow( m_clientArea
);
2375 wxBoxSizer
*sizer
= new wxBoxSizer( wxVERTICAL
);
2376 sizer
->Add( m_headerArea
, 0, wxGROW
);
2377 sizer
->Add( m_clientArea
, 1, wxGROW
);
2384 WXLRESULT
wxDataViewCtrl::MSWWindowProc(WXUINT nMsg
,
2388 WXLRESULT rc
= wxDataViewCtrlBase::MSWWindowProc(nMsg
, wParam
, lParam
);
2391 // we need to process arrows ourselves for scrolling
2392 if ( nMsg
== WM_GETDLGCODE
)
2394 rc
|= DLGC_WANTARROWS
;
2402 void wxDataViewCtrl::OnSize( wxSizeEvent
&WXUNUSED(event
) )
2404 // We need to override OnSize so that our scrolled
2405 // window a) does call Layout() to use sizers for
2406 // positioning the controls but b) does not query
2407 // the sizer for their size and use that for setting
2408 // the scrollable area as set that ourselves by
2409 // calling SetScrollbar() further down.
2416 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel
*model
)
2418 if (!wxDataViewCtrlBase::AssociateModel( model
))
2421 m_notifier
= new wxGenericDataViewListModelNotifier( m_clientArea
);
2423 model
->AddNotifier( m_notifier
);
2425 m_clientArea
->UpdateDisplay();
2430 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn
*col
)
2432 if (!wxDataViewCtrlBase::AppendColumn(col
))
2435 m_clientArea
->UpdateDisplay();
2436 m_headerArea
->UpdateDisplay();
2441 void wxDataViewCtrl::SetSelection( int row
)
2443 m_clientArea
->SelectRow(row
, true);
2446 void wxDataViewCtrl::SetSelectionRange( unsigned int from
, unsigned int to
)
2448 m_clientArea
->SelectRows(from
, to
, true);
2451 void wxDataViewCtrl::SetSelections( const wxArrayInt
& aSelections
)
2453 m_clientArea
->Select(aSelections
);
2456 void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row
) )
2461 bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row
) ) const
2468 int wxDataViewCtrl::GetSelection() const
2475 int wxDataViewCtrl::GetSelections(wxArrayInt
& WXUNUSED(aSelections
) ) const
2483 // !wxUSE_GENERICDATAVIEWCTRL
2486 // wxUSE_DATAVIEWCTRL