1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datavgen.cpp
3 // Purpose: wxDataViewCtrl generic implementation
4 // Author: Robert Roebling
5 // Modified by: Francesco Montorsi, Guru Kathiresan, Bo Yang
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"
36 #include "wx/dcscreen.h"
39 #include "wx/stockitem.h"
40 #include "wx/calctrl.h"
41 #include "wx/popupwin.h"
42 #include "wx/renderer.h"
43 #include "wx/dcbuffer.h"
46 #include "wx/listimpl.cpp"
48 //-----------------------------------------------------------------------------
50 //-----------------------------------------------------------------------------
54 static const int SCROLL_UNIT_X
= 15;
56 // the cell padding on the left/right
57 static const int PADDING_RIGHTLEFT
= 3;
59 // the expander space margin
60 static const int EXPANDER_MARGIN
= 4;
62 //-----------------------------------------------------------------------------
63 // wxDataViewHeaderWindow
64 //-----------------------------------------------------------------------------
66 // on wxMSW the header window (only that part however) can be made native!
67 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
68 #define USE_NATIVE_HEADER_WINDOW
71 //Below is the compare stuff
72 //For the generic implements, both the leaf nodes and the nodes are sorted for fast search when needed
73 static wxDataViewModel
* g_model
;
74 static int g_column
= -2;
75 static bool g_asending
= true;
77 // NB: for some reason, this class must be dllexport'ed or we get warnings from
79 class WXDLLIMPEXP_ADV wxDataViewHeaderWindowBase
: public wxControl
82 wxDataViewHeaderWindowBase()
85 bool Create(wxDataViewCtrl
*parent
, wxWindowID id
,
86 const wxPoint
&pos
, const wxSize
&size
,
89 return wxWindow::Create(parent
, id
, pos
, size
, wxNO_BORDER
, name
);
92 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
93 wxDataViewCtrl
*GetOwner() { return m_owner
; }
95 // called on column addition/removal
96 virtual void UpdateDisplay() { /* by default, do nothing */ }
98 // returns the n-th column
99 virtual wxDataViewColumn
*GetColumn(unsigned int n
)
102 wxDataViewColumn
*ret
= m_owner
->GetColumn(n
);
109 wxDataViewCtrl
*m_owner
;
111 // sends an event generated from the n-th wxDataViewColumn
112 void SendEvent(wxEventType type
, unsigned int n
);
115 #ifdef USE_NATIVE_HEADER_WINDOW
117 #define COLUMN_WIDTH_OFFSET 2
118 #define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow
120 class wxDataViewHeaderWindowMSW
: public wxDataViewHeaderWindowBase
124 wxDataViewHeaderWindowMSW( wxDataViewCtrl
*parent
,
126 const wxPoint
&pos
= wxDefaultPosition
,
127 const wxSize
&size
= wxDefaultSize
,
128 const wxString
&name
= wxT("wxdataviewctrlheaderwindow") )
130 Create(parent
, id
, pos
, size
, name
);
133 bool Create(wxDataViewCtrl
*parent
, wxWindowID id
,
134 const wxPoint
&pos
, const wxSize
&size
,
135 const wxString
&name
);
137 ~wxDataViewHeaderWindowMSW();
139 // called when any column setting is changed and/or changed
141 virtual void UpdateDisplay();
143 virtual void OnInternalIdle();
145 // called Refresh afterwards
146 virtual void ScrollWindow(int dx
, int dy
, const wxRect
*rect
= NULL
);
148 virtual wxBorder
GetDefaultBorder() const { return wxBORDER_NONE
; }
151 virtual bool MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
);
153 virtual void DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
);
155 wxSize
DoGetBestSize() const;
157 unsigned int GetColumnIdxFromHeader(NMHEADER
*nmHDR
);
159 wxDataViewColumn
*GetColumnFromHeader(NMHEADER
*nmHDR
)
160 { return GetColumn(GetColumnIdxFromHeader(nmHDR
)); }
164 bool m_delayedUpdate
;
167 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW
)
170 #else // !defined(__WXMSW__)
172 #define HEADER_WINDOW_HEIGHT 25
173 #define HEADER_HORIZ_BORDER 5
174 #define HEADER_VERT_BORDER 3
175 #define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow
177 class wxGenericDataViewHeaderWindow
: public wxDataViewHeaderWindowBase
180 wxGenericDataViewHeaderWindow( wxDataViewCtrl
*parent
,
182 const wxPoint
&pos
= wxDefaultPosition
,
183 const wxSize
&size
= wxDefaultSize
,
184 const wxString
&name
= wxT("wxdataviewctrlheaderwindow") )
187 Create(parent
, id
, pos
, size
, name
);
190 bool Create(wxDataViewCtrl
*parent
, wxWindowID id
,
191 const wxPoint
&pos
, const wxSize
&size
,
192 const wxString
&name
);
194 ~wxGenericDataViewHeaderWindow()
196 delete m_resizeCursor
;
199 virtual void UpdateDisplay() { Refresh(); }
203 void OnPaint( wxPaintEvent
&event
);
204 void OnMouse( wxMouseEvent
&event
);
205 void OnSetFocus( wxFocusEvent
&event
);
210 // vars used for column resizing:
212 wxCursor
*m_resizeCursor
;
213 const wxCursor
*m_currentCursor
;
216 bool m_dirty
; // needs refresh?
217 int m_hover
; // index of the column under the mouse
218 int m_column
; // index of the column being resized
219 int m_currentX
; // divider line position in logical (unscrolled) coords
220 int m_minX
; // minimal position beyond which the divider line
221 // can't be dragged in logical coords
223 // the pen used to draw the current column width drag line
224 // when resizing the columsn
228 // internal utilities:
232 m_currentCursor
= (wxCursor
*) NULL
;
233 m_resizeCursor
= new wxCursor( wxCURSOR_SIZEWE
);
235 m_isDragging
= false;
238 m_hover
= wxNOT_FOUND
;
239 m_column
= wxNOT_FOUND
;
243 wxColour col
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
);
244 m_penCurrent
= wxPen(col
, 1, wxSOLID
);
247 void AdjustDC(wxDC
& dc
);
250 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow
)
251 DECLARE_EVENT_TABLE()
254 #endif // defined(__WXMSW__)
256 //-----------------------------------------------------------------------------
257 // wxDataViewRenameTimer
258 //-----------------------------------------------------------------------------
260 class wxDataViewRenameTimer
: public wxTimer
263 wxDataViewMainWindow
*m_owner
;
266 wxDataViewRenameTimer( wxDataViewMainWindow
*owner
);
270 //-----------------------------------------------------------------------------
271 // wxDataViewTreeNode
272 //-----------------------------------------------------------------------------
273 class wxDataViewTreeNode
;
274 WX_DEFINE_ARRAY( wxDataViewTreeNode
*, wxDataViewTreeNodes
);
275 WX_DEFINE_ARRAY( void* , wxDataViewTreeLeaves
);
277 int LINKAGEMODE
wxGenericTreeModelNodeCmp( wxDataViewTreeNode
** node1
, wxDataViewTreeNode
** node2
);
278 int LINKAGEMODE
wxGenericTreeModelItemCmp( void ** id1
, void ** id2
);
280 class wxDataViewTreeNode
283 wxDataViewTreeNode( wxDataViewTreeNode
* parent
= NULL
)
290 m_hasChildren
= false;
294 ~wxDataViewTreeNode()
298 wxDataViewTreeNode
* GetParent() { return m_parent
; }
299 void SetParent( wxDataViewTreeNode
* parent
) { m_parent
= parent
; }
300 wxDataViewTreeNodes
& GetNodes() { return m_nodes
; }
301 wxDataViewTreeLeaves
& GetChildren() { return m_leaves
; }
303 void AddNode( wxDataViewTreeNode
* node
)
305 m_leaves
.Add( node
->GetItem().GetID() );
307 m_leaves
.Sort( &wxGenericTreeModelItemCmp
);
310 m_nodes
.Sort( &wxGenericTreeModelNodeCmp
);
312 void AddLeaf( void * leaf
)
314 m_leaves
.Add( leaf
);
316 m_leaves
.Sort( &wxGenericTreeModelItemCmp
);
319 wxDataViewItem
& GetItem() { return m_item
; }
320 void SetItem( const wxDataViewItem
& item
) { m_item
= item
; }
322 unsigned int GetChildrenNumber() { return m_leaves
.GetCount(); }
323 unsigned int GetNodeNumber() { return m_nodes
.GetCount(); }
327 wxDataViewTreeNode
* node
= this;
328 while( node
->GetParent()->GetParent() != NULL
)
330 node
= node
->GetParent();
343 int len
= m_nodes
.GetCount();
345 for ( int i
= 0 ;i
< len
; i
++)
346 sum
+= m_nodes
[i
]->GetSubTreeCount();
348 sum
+= m_leaves
.GetCount();
351 ChangeSubTreeCount(-sum
);
357 ChangeSubTreeCount(sum
);
360 bool HasChildren() { return m_hasChildren
; }
361 void SetHasChildren( bool has
){ m_hasChildren
= has
; }
363 void SetSubTreeCount( int num
) { m_subTreeCount
= num
; }
364 int GetSubTreeCount() { return m_subTreeCount
; }
365 void ChangeSubTreeCount( int num
)
369 m_subTreeCount
+= num
;
371 m_parent
->ChangeSubTreeCount(num
);
378 m_nodes
.Sort( &wxGenericTreeModelNodeCmp
);
379 int len
= m_nodes
.GetCount();
380 for (int i
= 0; i
< len
; i
++)
381 m_nodes
[i
]->Resort();
382 m_leaves
.Sort( &wxGenericTreeModelItemCmp
);
387 wxDataViewTreeNode
*m_parent
;
388 wxDataViewTreeNodes m_nodes
;
389 wxDataViewTreeLeaves m_leaves
;
390 wxDataViewItem m_item
;
396 int LINKAGEMODE
wxGenericTreeModelNodeCmp( wxDataViewTreeNode
** node1
, wxDataViewTreeNode
** node2
)
398 return g_model
->Compare( (*node1
)->GetItem(), (*node2
)->GetItem(), g_column
, g_asending
);
401 int LINKAGEMODE
wxGenericTreeModelItemCmp( void ** id1
, void ** id2
)
403 return g_model
->Compare( *id1
, *id2
, g_column
, g_asending
);
408 //-----------------------------------------------------------------------------
409 // wxDataViewMainWindow
410 //-----------------------------------------------------------------------------
412 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection
,
414 WX_DECLARE_LIST(wxDataViewItem
, ItemList
);
415 WX_DEFINE_LIST(ItemList
)
417 class wxDataViewMainWindow
: public wxWindow
420 wxDataViewMainWindow( wxDataViewCtrl
*parent
,
422 const wxPoint
&pos
= wxDefaultPosition
,
423 const wxSize
&size
= wxDefaultSize
,
424 const wxString
&name
= wxT("wxdataviewctrlmainwindow") );
425 virtual ~wxDataViewMainWindow();
427 // notifications from wxDataViewModel
428 bool ItemAdded( const wxDataViewItem
&parent
, const wxDataViewItem
&item
);
429 bool ItemDeleted( const wxDataViewItem
&parent
, const wxDataViewItem
&item
);
430 bool ItemChanged( const wxDataViewItem
&item
);
431 bool ValueChanged( const wxDataViewItem
&item
, unsigned int col
);
445 g_model
= GetOwner()->GetModel();
446 wxDataViewColumn
* col
= GetOwner()->GetSortingColumn();
449 if (g_model
->HasDefaultCompare())
457 g_column
= col
->GetModelColumn();
458 g_asending
= col
->IsSortOrderAscending();
461 void SetOwner( wxDataViewCtrl
* owner
) { m_owner
= owner
; }
462 wxDataViewCtrl
*GetOwner() { return m_owner
; }
463 const wxDataViewCtrl
*GetOwner() const { return m_owner
; }
465 void OnPaint( wxPaintEvent
&event
);
466 void OnArrowChar(unsigned int newCurrent
, const wxKeyEvent
& event
);
467 void OnChar( wxKeyEvent
&event
);
468 void OnMouse( wxMouseEvent
&event
);
469 void OnSetFocus( wxFocusEvent
&event
);
470 void OnKillFocus( wxFocusEvent
&event
);
472 void UpdateDisplay();
473 void RecalculateDisplay();
474 void OnInternalIdle();
476 void OnRenameTimer();
478 void ScrollWindow( int dx
, int dy
, const wxRect
*rect
= NULL
);
479 void ScrollTo( int rows
, int column
);
481 bool HasCurrentRow() { return m_currentRow
!= (unsigned int)-1; }
482 void ChangeCurrentRow( unsigned int row
);
484 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE
); }
485 bool IsEmpty() { return GetRowCount() == 0; }
487 int GetCountPerPage() const;
488 int GetEndOfLastCol() const;
489 unsigned int GetFirstVisibleRow() const;
490 //I change this method to un const because in the tree view, the displaying number of the tree are changing along with the expanding/collapsing of the tree nodes
491 unsigned int GetLastVisibleRow();
492 unsigned int GetRowCount() ;
494 wxDataViewItem
GetSelection() const;
495 wxDataViewSelection
GetSelections(){ return m_selection
; }
496 void SetSelections( const wxDataViewSelection
& sel
) { m_selection
= sel
; UpdateDisplay(); }
497 void Select( const wxArrayInt
& aSelections
);
498 void SelectAllRows( bool on
);
499 void SelectRow( unsigned int row
, bool on
);
500 void SelectRows( unsigned int from
, unsigned int to
, bool on
);
501 void ReverseRowSelection( unsigned int row
);
502 bool IsRowSelected( unsigned int row
);
503 void SendSelectionChangedEvent( const wxDataViewItem
& item
);
505 void RefreshRow( unsigned int row
);
506 void RefreshRows( unsigned int from
, unsigned int to
);
507 void RefreshRowsAfter( unsigned int firstRow
);
509 // returns the colour to be used for drawing the rules
510 wxColour
GetRuleColour() const
512 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
);
515 wxRect
GetLineRect( unsigned int row
) const;
517 //Some useful functions for row and item mapping
518 wxDataViewItem
GetItemByRow( unsigned int row
) const;
519 int GetRowByItem( const wxDataViewItem
& item
);
521 //Methods for building the mapping tree
522 void BuildTree( wxDataViewModel
* model
);
524 void HitTest( const wxPoint
& point
, wxDataViewItem
& item
, wxDataViewColumn
* &column
);
525 wxRect
GetItemRect( const wxDataViewItem
& item
, const wxDataViewColumn
* column
);
527 void Expand( unsigned int row
) { OnExpanding( row
); }
528 void Collapse( unsigned int row
) { OnCollapsing( row
); }
530 wxDataViewTreeNode
* GetTreeNodeByRow( unsigned int row
);
531 //We did not need this temporarily
532 //wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
534 int RecalculateCount() ;
536 wxDataViewEvent
SendExpanderEvent( wxEventType type
, const wxDataViewItem
& item
);
537 void OnExpanding( unsigned int row
);
538 void OnCollapsing( unsigned int row
);
540 wxDataViewTreeNode
* FindNode( const wxDataViewItem
& item
);
543 wxDataViewCtrl
*m_owner
;
547 wxDataViewColumn
*m_currentCol
;
548 unsigned int m_currentRow
;
549 wxDataViewSelection m_selection
;
551 wxDataViewRenameTimer
*m_renameTimer
;
559 // for double click logic
560 unsigned int m_lineLastClicked
,
561 m_lineBeforeLastClicked
,
562 m_lineSelectSingleOnUp
;
564 // the pen used to draw horiz/vertical rules
567 // the pen used to draw the expander and the lines
570 //This is the tree structure of the model
571 wxDataViewTreeNode
* m_root
;
573 //This is the tree node under the cursor
574 wxDataViewTreeNode
* m_underMouse
;
576 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow
)
577 DECLARE_EVENT_TABLE()
580 // ---------------------------------------------------------
581 // wxGenericDataViewModelNotifier
582 // ---------------------------------------------------------
584 class wxGenericDataViewModelNotifier
: public wxDataViewModelNotifier
587 wxGenericDataViewModelNotifier( wxDataViewMainWindow
*mainWindow
)
588 { m_mainWindow
= mainWindow
; }
590 virtual bool ItemAdded( const wxDataViewItem
& parent
, const wxDataViewItem
& item
)
591 { return m_mainWindow
->ItemAdded( parent
, item
); }
592 virtual bool ItemDeleted( const wxDataViewItem
&parent
, const wxDataViewItem
&item
)
593 { return m_mainWindow
->ItemDeleted( parent
, item
); }
594 virtual bool ItemChanged( const wxDataViewItem
& item
)
595 { return m_mainWindow
->ItemChanged(item
); }
596 virtual bool ValueChanged( const wxDataViewItem
& item
, unsigned int col
)
597 { return m_mainWindow
->ValueChanged( item
, col
); }
598 virtual bool Cleared()
599 { return m_mainWindow
->Cleared(); }
600 virtual void Resort()
601 { m_mainWindow
->Resort(); }
603 wxDataViewMainWindow
*m_mainWindow
;
606 // ---------------------------------------------------------
607 // wxDataViewRenderer
608 // ---------------------------------------------------------
610 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer
, wxDataViewRendererBase
)
612 wxDataViewRenderer::wxDataViewRenderer( const wxString
&varianttype
,
613 wxDataViewCellMode mode
,
615 wxDataViewRendererBase( varianttype
, mode
, align
)
624 wxDataViewRenderer::~wxDataViewRenderer()
630 wxDC
*wxDataViewRenderer::GetDC()
634 if (GetOwner() == NULL
)
636 if (GetOwner()->GetOwner() == NULL
)
638 m_dc
= new wxClientDC( GetOwner()->GetOwner() );
644 void wxDataViewRenderer::SetAlignment( int align
)
649 int wxDataViewRenderer::GetAlignment() const
654 int wxDataViewRenderer::CalculateAlignment() const
656 if (m_align
== wxDVR_DEFAULT_ALIGNMENT
)
658 if (GetOwner() == NULL
)
659 return wxALIGN_LEFT
| wxALIGN_CENTRE_VERTICAL
;
661 return GetOwner()->GetAlignment() | wxALIGN_CENTRE_VERTICAL
;
667 // ---------------------------------------------------------
668 // wxDataViewCustomRenderer
669 // ---------------------------------------------------------
671 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer
, wxDataViewRenderer
)
673 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString
&varianttype
,
674 wxDataViewCellMode mode
, int align
) :
675 wxDataViewRenderer( varianttype
, mode
, align
)
679 void wxDataViewCustomRenderer::RenderText( const wxString
&text
, int xoffset
, wxRect cell
, wxDC
*dc
, int state
)
681 wxDataViewCtrl
*view
= GetOwner()->GetOwner();
682 wxColour col
= (state
& wxDATAVIEW_CELL_SELECTED
) ?
683 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
) :
684 view
->GetForegroundColour();
685 dc
->SetTextForeground(col
);
686 dc
->DrawText( text
, cell
.x
+ xoffset
, cell
.y
+ ((cell
.height
- dc
->GetCharHeight()) / 2));
689 // ---------------------------------------------------------
690 // wxDataViewTextRenderer
691 // ---------------------------------------------------------
693 IMPLEMENT_CLASS(wxDataViewTextRenderer
, wxDataViewCustomRenderer
)
695 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString
&varianttype
,
696 wxDataViewCellMode mode
, int align
) :
697 wxDataViewCustomRenderer( varianttype
, mode
, align
)
701 bool wxDataViewTextRenderer::SetValue( const wxVariant
&value
)
703 m_text
= value
.GetString();
708 bool wxDataViewTextRenderer::GetValue( wxVariant
& WXUNUSED(value
) ) const
713 bool wxDataViewTextRenderer::HasEditorCtrl()
718 wxControl
* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow
*parent
,
719 wxRect labelRect
, const wxVariant
&value
)
721 return new wxTextCtrl( parent
, wxID_ANY
, value
,
722 wxPoint(labelRect
.x
,labelRect
.y
),
723 wxSize(labelRect
.width
,labelRect
.height
) );
726 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl
*editor
, wxVariant
&value
)
728 wxTextCtrl
*text
= (wxTextCtrl
*) editor
;
729 value
= text
->GetValue();
733 bool wxDataViewTextRenderer::Render( wxRect cell
, wxDC
*dc
, int state
)
735 RenderText( m_text
, 0, cell
, dc
, state
);
739 wxSize
wxDataViewTextRenderer::GetSize() const
741 const wxDataViewCtrl
*view
= GetView();
745 view
->GetTextExtent( m_text
, &x
, &y
);
746 return wxSize( x
, y
);
748 return wxSize(80,20);
751 // ---------------------------------------------------------
752 // wxDataViewTextRendererAttr
753 // ---------------------------------------------------------
755 IMPLEMENT_CLASS(wxDataViewTextRendererAttr
, wxDataViewTextRenderer
)
757 wxDataViewTextRendererAttr::wxDataViewTextRendererAttr( const wxString
&varianttype
,
758 wxDataViewCellMode mode
, int align
) :
759 wxDataViewTextRenderer( varianttype
, mode
, align
)
764 bool wxDataViewTextRendererAttr::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
771 if (m_attr
.HasColour())
773 colour
= dc
->GetTextForeground();
774 dc
->SetTextForeground( m_attr
.GetColour() );
777 if (m_attr
.GetBold() || m_attr
.GetItalic())
779 font
= dc
->GetFont();
780 wxFont myfont
= font
;
781 if (m_attr
.GetBold())
782 myfont
.SetWeight( wxFONTWEIGHT_BOLD
);
783 if (m_attr
.GetItalic())
784 myfont
.SetStyle( wxFONTSTYLE_ITALIC
);
785 dc
->SetFont( myfont
);
789 dc
->DrawText( m_text
, cell
.x
, cell
.y
+ ((cell
.height
- dc
->GetCharHeight()) / 2));
794 if (m_attr
.HasColour())
795 dc
->SetTextForeground( colour
);
797 if (m_attr
.GetBold() || m_attr
.GetItalic())
805 // ---------------------------------------------------------
806 // wxDataViewBitmapRenderer
807 // ---------------------------------------------------------
809 IMPLEMENT_CLASS(wxDataViewBitmapRenderer
, wxDataViewCustomRenderer
)
811 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString
&varianttype
,
812 wxDataViewCellMode mode
, int align
) :
813 wxDataViewCustomRenderer( varianttype
, mode
, align
)
817 bool wxDataViewBitmapRenderer::SetValue( const wxVariant
&value
)
819 if (value
.GetType() == wxT("wxBitmap"))
821 if (value
.GetType() == wxT("wxIcon"))
827 bool wxDataViewBitmapRenderer::GetValue( wxVariant
& WXUNUSED(value
) ) const
832 bool wxDataViewBitmapRenderer::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
835 dc
->DrawBitmap( m_bitmap
, cell
.x
, cell
.y
);
836 else if (m_icon
.Ok())
837 dc
->DrawIcon( m_icon
, cell
.x
, cell
.y
);
842 wxSize
wxDataViewBitmapRenderer::GetSize() const
845 return wxSize( m_bitmap
.GetWidth(), m_bitmap
.GetHeight() );
846 else if (m_icon
.Ok())
847 return wxSize( m_icon
.GetWidth(), m_icon
.GetHeight() );
849 return wxSize(16,16);
852 // ---------------------------------------------------------
853 // wxDataViewToggleRenderer
854 // ---------------------------------------------------------
856 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer
, wxDataViewCustomRenderer
)
858 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString
&varianttype
,
859 wxDataViewCellMode mode
, int align
) :
860 wxDataViewCustomRenderer( varianttype
, mode
, align
)
865 bool wxDataViewToggleRenderer::SetValue( const wxVariant
&value
)
867 m_toggle
= value
.GetBool();
872 bool wxDataViewToggleRenderer::GetValue( wxVariant
&WXUNUSED(value
) ) const
877 bool wxDataViewToggleRenderer::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
879 // User wxRenderer here
882 rect
.x
= cell
.x
+ cell
.width
/2 - 10;
884 rect
.y
= cell
.y
+ cell
.height
/2 - 10;
889 flags
|= wxCONTROL_CHECKED
;
890 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE
)
891 flags
|= wxCONTROL_DISABLED
;
893 wxRendererNative::Get().DrawCheckBox(
894 GetOwner()->GetOwner(),
902 bool wxDataViewToggleRenderer::Activate( wxRect
WXUNUSED(cell
),
903 wxDataViewModel
*model
,
904 const wxDataViewItem
& item
, unsigned int col
)
906 bool value
= !m_toggle
;
907 wxVariant variant
= value
;
908 model
->SetValue( variant
, item
, col
);
909 model
->ValueChanged( item
, col
);
913 wxSize
wxDataViewToggleRenderer::GetSize() const
915 return wxSize(20,20);
918 // ---------------------------------------------------------
919 // wxDataViewProgressRenderer
920 // ---------------------------------------------------------
922 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer
, wxDataViewCustomRenderer
)
924 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString
&label
,
925 const wxString
&varianttype
, wxDataViewCellMode mode
, int align
) :
926 wxDataViewCustomRenderer( varianttype
, mode
, align
)
932 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
936 bool wxDataViewProgressRenderer::SetValue( const wxVariant
&value
)
938 m_value
= (long) value
;
940 if (m_value
< 0) m_value
= 0;
941 if (m_value
> 100) m_value
= 100;
946 bool wxDataViewProgressRenderer::GetValue( wxVariant
&value
) const
948 value
= (long) m_value
;
952 bool wxDataViewProgressRenderer::Render( wxRect cell
, wxDC
*dc
, int WXUNUSED(state
) )
954 double pct
= (double)m_value
/ 100.0;
956 bar
.width
= (int)(cell
.width
* pct
);
957 dc
->SetPen( *wxTRANSPARENT_PEN
);
958 dc
->SetBrush( *wxBLUE_BRUSH
);
959 dc
->DrawRectangle( bar
);
961 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
962 dc
->SetPen( *wxBLACK_PEN
);
963 dc
->DrawRectangle( cell
);
968 wxSize
wxDataViewProgressRenderer::GetSize() const
970 return wxSize(40,12);
973 // ---------------------------------------------------------
974 // wxDataViewDateRenderer
975 // ---------------------------------------------------------
977 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
979 #if wxUSE_DATE_RENDERER_POPUP
981 class wxDataViewDateRendererPopupTransient
: public wxPopupTransientWindow
984 wxDataViewDateRendererPopupTransient( wxWindow
* parent
, wxDateTime
*value
,
985 wxDataViewModel
*model
, const wxDataViewItem
& item
, unsigned int col
) :
986 wxPopupTransientWindow( parent
, wxBORDER_SIMPLE
),
991 m_cal
= new wxCalendarCtrl( this, wxID_ANY
, *value
);
992 wxBoxSizer
*sizer
= new wxBoxSizer( wxHORIZONTAL
);
993 sizer
->Add( m_cal
, 1, wxGROW
);
998 void OnCalendar( wxCalendarEvent
&event
);
1000 wxCalendarCtrl
*m_cal
;
1001 wxDataViewModel
*m_model
;
1003 const wxDataViewItem
& m_item
;
1006 virtual void OnDismiss()
1011 DECLARE_EVENT_TABLE()
1014 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient
,wxPopupTransientWindow
)
1015 EVT_CALENDAR( wxID_ANY
, wxDataViewDateRendererPopupTransient::OnCalendar
)
1018 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent
&event
)
1020 wxDateTime date
= event
.GetDate();
1021 wxVariant value
= date
;
1022 m_model
->SetValue( value
, m_item
, m_col
);
1023 m_model
->ValueChanged( m_item
, m_col
);
1027 #endif // wxUSE_DATE_RENDERER_POPUP
1029 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer
, wxDataViewCustomRenderer
)
1031 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString
&varianttype
,
1032 wxDataViewCellMode mode
, int align
) :
1033 wxDataViewCustomRenderer( varianttype
, mode
, align
)
1037 bool wxDataViewDateRenderer::SetValue( const wxVariant
&value
)
1039 m_date
= value
.GetDateTime();
1044 bool wxDataViewDateRenderer::GetValue( wxVariant
&value
) const
1050 bool wxDataViewDateRenderer::Render( wxRect cell
, wxDC
*dc
, int state
)
1052 wxString tmp
= m_date
.FormatDate();
1053 RenderText( tmp
, 0, cell
, dc
, state
);
1057 wxSize
wxDataViewDateRenderer::GetSize() const
1059 const wxDataViewCtrl
* view
= GetView();
1060 wxString tmp
= m_date
.FormatDate();
1062 view
->GetTextExtent( tmp
, &x
, &y
, &d
);
1063 return wxSize(x
,y
+d
);
1066 bool wxDataViewDateRenderer::Activate( wxRect
WXUNUSED(cell
), wxDataViewModel
*model
,
1067 const wxDataViewItem
& item
, unsigned int col
)
1070 model
->GetValue( variant
, item
, col
);
1071 wxDateTime value
= variant
.GetDateTime();
1073 #if wxUSE_DATE_RENDERER_POPUP
1074 wxDataViewDateRendererPopupTransient
*popup
= new wxDataViewDateRendererPopupTransient(
1075 GetOwner()->GetOwner()->GetParent(), &value
, model
, item
, col
);
1076 wxPoint pos
= wxGetMousePosition();
1079 popup
->Popup( popup
->m_cal
);
1080 #else // !wxUSE_DATE_RENDERER_POPUP
1081 wxMessageBox(value
.Format());
1082 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
1086 // ---------------------------------------------------------
1087 // wxDataViewIconTextRenderer
1088 // ---------------------------------------------------------
1090 IMPLEMENT_CLASS(wxDataViewIconTextRenderer
, wxDataViewCustomRenderer
)
1092 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(
1093 const wxString
&varianttype
, wxDataViewCellMode mode
, int align
) :
1094 wxDataViewCustomRenderer( varianttype
, mode
, align
)
1097 SetAlignment(align
);
1100 wxDataViewIconTextRenderer::~wxDataViewIconTextRenderer()
1104 bool wxDataViewIconTextRenderer::SetValue( const wxVariant
&value
)
1110 bool wxDataViewIconTextRenderer::GetValue( wxVariant
& WXUNUSED(value
) ) const
1115 bool wxDataViewIconTextRenderer::Render( wxRect cell
, wxDC
*dc
, int state
)
1118 const wxIcon
&icon
= m_value
.GetIcon();
1121 dc
->DrawIcon( icon
, cell
.x
, cell
.y
+ ((cell
.height
- icon
.GetHeight()) / 2));
1122 xoffset
= icon
.GetWidth()+4;
1125 RenderText( m_value
.GetText(), xoffset
, cell
, dc
, state
);
1130 wxSize
wxDataViewIconTextRenderer::GetSize() const
1132 const wxDataViewCtrl
*view
= GetView();
1133 if (!m_value
.GetText().empty())
1136 view
->GetTextExtent( m_value
.GetText(), &x
, &y
);
1138 if (m_value
.GetIcon().IsOk())
1139 x
+= m_value
.GetIcon().GetWidth() + 4;
1140 return wxSize( x
, y
);
1142 return wxSize(80,20);
1146 wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow
* WXUNUSED(parent
),
1147 wxRect
WXUNUSED(labelRect
),
1148 const wxVariant
& WXUNUSED(value
))
1154 wxDataViewIconTextRenderer::GetValueFromEditorCtrl(wxControl
* WXUNUSED(editor
),
1155 wxVariant
& WXUNUSED(value
))
1160 // ---------------------------------------------------------
1162 // ---------------------------------------------------------
1164 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn
, wxDataViewColumnBase
)
1166 wxDataViewColumn::wxDataViewColumn( const wxString
&title
, wxDataViewRenderer
*cell
,
1167 unsigned int model_column
,
1168 int width
, wxAlignment align
, int flags
) :
1169 wxDataViewColumnBase( title
, cell
, model_column
, width
, align
, flags
)
1171 SetAlignment(align
);
1175 m_autosize
= width
< 0; // TODO
1177 Init(width
< 0 ? wxDVC_DEFAULT_WIDTH
: width
);
1180 wxDataViewColumn::wxDataViewColumn( const wxBitmap
&bitmap
, wxDataViewRenderer
*cell
,
1181 unsigned int model_column
,
1182 int width
, wxAlignment align
, int flags
) :
1183 wxDataViewColumnBase( bitmap
, cell
, model_column
, width
, align
, flags
)
1185 SetAlignment(align
);
1188 Init(width
< 0 ? wxDVC_TOGGLE_DEFAULT_WIDTH
: width
);
1191 wxDataViewColumn::~wxDataViewColumn()
1195 void wxDataViewColumn::Init( int width
)
1198 m_minWidth
= wxDVC_DEFAULT_MINWIDTH
;
1202 void wxDataViewColumn::SetResizeable( bool resizeable
)
1205 m_flags
|= wxDATAVIEW_COL_RESIZABLE
;
1207 m_flags
&= ~wxDATAVIEW_COL_RESIZABLE
;
1210 void wxDataViewColumn::SetHidden( bool hidden
)
1213 m_flags
|= wxDATAVIEW_COL_HIDDEN
;
1215 m_flags
&= ~wxDATAVIEW_COL_HIDDEN
;
1217 // tell our owner to e.g. update its scrollbars:
1219 GetOwner()->OnColumnChange();
1222 void wxDataViewColumn::SetSortable( bool sortable
)
1225 m_flags
|= wxDATAVIEW_COL_SORTABLE
;
1227 m_flags
&= ~wxDATAVIEW_COL_SORTABLE
;
1229 // Update header button
1231 GetOwner()->OnColumnChange();
1234 void wxDataViewColumn::SetReorderable( bool reorderable
)
1237 m_flags
|= wxDATAVIEW_COL_REORDERABLE
;
1239 m_flags
&= ~wxDATAVIEW_COL_REORDERABLE
;
1242 void wxDataViewColumn::SetSortOrder( bool ascending
)
1244 m_ascending
= ascending
;
1246 // Update header button
1248 GetOwner()->OnColumnChange();
1251 bool wxDataViewColumn::IsSortOrderAscending() const
1256 void wxDataViewColumn::SetInternalWidth( int width
)
1260 // the scrollbars of the wxDataViewCtrl needs to be recalculated!
1261 if (m_owner
&& m_owner
->m_clientArea
)
1262 m_owner
->m_clientArea
->RecalculateDisplay();
1265 void wxDataViewColumn::SetWidth( int width
)
1267 if (m_owner
->m_headerArea
) m_owner
->m_headerArea
->UpdateDisplay();
1269 SetInternalWidth(width
);
1273 //-----------------------------------------------------------------------------
1274 // wxDataViewHeaderWindowBase
1275 //-----------------------------------------------------------------------------
1277 void wxDataViewHeaderWindowBase::SendEvent(wxEventType type
, unsigned int n
)
1279 wxWindow
*parent
= GetParent();
1280 wxDataViewEvent
le(type
, parent
->GetId());
1282 le
.SetEventObject(parent
);
1284 le
.SetDataViewColumn(GetColumn(n
));
1285 le
.SetModel(GetOwner()->GetModel());
1287 // for events created by wxDataViewHeaderWindow the
1288 // row / value fields are not valid
1290 parent
->GetEventHandler()->ProcessEvent(le
);
1293 #ifdef USE_NATIVE_HEADER_WINDOW
1295 #ifndef HDS_DRAGDROP
1296 #define HDS_DRAGDROP 0x0040
1298 #ifndef HDS_FULLDRAG
1299 #define HDS_FULLDRAG 0x0080
1302 // implemented in msw/listctrl.cpp:
1303 int WXDLLIMPEXP_CORE
wxMSWGetColumnClicked(NMHDR
*nmhdr
, POINT
*ptClick
);
1305 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW
, wxWindow
)
1307 bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl
*parent
, wxWindowID id
,
1308 const wxPoint
&pos
, const wxSize
&size
,
1309 const wxString
&name
)
1313 m_scrollOffsetX
= 0;
1314 m_delayedUpdate
= false;
1315 m_buttonHeight
= wxRendererNative::Get().GetHeaderButtonHeight( this );
1317 int x
= pos
.x
== wxDefaultCoord
? 0 : pos
.x
,
1318 y
= pos
.y
== wxDefaultCoord
? 0 : pos
.y
,
1319 w
= size
.x
== wxDefaultCoord
? 1 : size
.x
,
1322 wxSize
new_size(w
,h
);
1324 if ( !CreateControl(parent
, id
, pos
, new_size
, 0, wxDefaultValidator
, name
) )
1327 // create the native WC_HEADER window:
1328 WXHWND hwndParent
= (HWND
)parent
->GetHandle();
1329 WXDWORD msStyle
= WS_CHILD
| HDS_DRAGDROP
| HDS_BUTTONS
| HDS_HORZ
| HDS_HOTTRACK
| HDS_FULLDRAG
;
1332 msStyle
|= WS_VISIBLE
;
1334 m_hWnd
= CreateWindowEx(0,
1345 wxLogLastError(_T("CreateWindowEx"));
1349 // we need to subclass the m_hWnd to force wxWindow::HandleNotify
1350 // to call wxDataViewHeaderWindow::MSWOnNotify
1351 SubclassWin(m_hWnd
);
1353 // the following is required to get the default win's font for
1354 // header windows and must be done befor sending the HDM_LAYOUT msg
1360 wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
1365 wxSize
wxDataViewHeaderWindowMSW::DoGetBestSize() const
1367 return wxSize( 80, m_buttonHeight
+2 );
1370 void wxDataViewHeaderWindowMSW::OnInternalIdle()
1372 if (m_delayedUpdate
)
1374 m_delayedUpdate
= false;
1379 void wxDataViewHeaderWindowMSW::UpdateDisplay()
1381 // remove old columns
1382 for (int j
=0, max
=Header_GetItemCount((HWND
)m_hWnd
); j
< max
; j
++)
1383 Header_DeleteItem((HWND
)m_hWnd
, 0);
1385 // add the updated array of columns to the header control
1386 unsigned int cols
= GetOwner()->GetColumnCount();
1387 unsigned int added
= 0;
1388 for (unsigned int i
= 0; i
< cols
; i
++)
1390 wxDataViewColumn
*col
= GetColumn( i
);
1391 if (col
->IsHidden())
1392 continue; // don't add it!
1395 hdi
.mask
= HDI_TEXT
| HDI_FORMAT
| HDI_WIDTH
;
1396 if (col
->GetBitmap().IsOk())
1398 hdi
.mask
|= HDI_BITMAP
;
1399 hdi
.hbm
= (HBITMAP
) col
->GetBitmap().GetHBITMAP();
1401 hdi
.pszText
= (wxChar
*) col
->GetTitle().wx_str();
1402 hdi
.cxy
= col
->GetWidth();
1403 hdi
.cchTextMax
= sizeof(hdi
.pszText
)/sizeof(hdi
.pszText
[0]);
1404 hdi
.fmt
= HDF_LEFT
| HDF_STRING
;
1405 if (col
->GetBitmap().IsOk())
1406 hdi
.fmt
|= HDF_BITMAP
;
1408 //hdi.fmt &= ~(HDF_SORTDOWN|HDF_SORTUP);
1410 if (col
->IsSortable() && GetOwner()->GetSortingColumn() == col
)
1412 //The Microsoft Comctrl32.dll 6.0 support SORTUP/SORTDOWN, but they are not default
1413 //see http://msdn2.microsoft.com/en-us/library/ms649534.aspx for more detail
1414 // VZ: works with 5.81
1415 hdi
.fmt
|= col
->IsSortOrderAscending() ? HDF_SORTUP
: HDF_SORTDOWN
;
1418 // lParam is reserved for application's use:
1419 // we store there the column index to use it later in MSWOnNotify
1420 // (since columns may have been hidden)
1421 hdi
.lParam
= (LPARAM
)i
;
1423 // the native wxMSW implementation of the header window
1424 // draws the column separator COLUMN_WIDTH_OFFSET pixels
1425 // on the right: to correct this effect we make the column
1426 // exactly COLUMN_WIDTH_OFFSET wider (for the first column):
1428 hdi
.cxy
+= COLUMN_WIDTH_OFFSET
;
1430 switch (col
->GetAlignment())
1433 hdi
.fmt
|= HDF_LEFT
;
1435 case wxALIGN_CENTER
:
1436 case wxALIGN_CENTER_HORIZONTAL
:
1437 hdi
.fmt
|= HDF_CENTER
;
1440 hdi
.fmt
|= HDF_RIGHT
;
1444 // such alignment is not allowed for the column header!
1448 SendMessage((HWND
)m_hWnd
, HDM_INSERTITEM
,
1449 (WPARAM
)added
, (LPARAM
)&hdi
);
1454 unsigned int wxDataViewHeaderWindowMSW::GetColumnIdxFromHeader(NMHEADER
*nmHDR
)
1458 // NOTE: we don't just return nmHDR->iItem because when there are
1459 // hidden columns, nmHDR->iItem may be different from
1460 // nmHDR->pitem->lParam
1462 if (nmHDR
->pitem
&& nmHDR
->pitem
->mask
& HDI_LPARAM
)
1464 idx
= (unsigned int)nmHDR
->pitem
->lParam
;
1469 item
.mask
= HDI_LPARAM
;
1470 Header_GetItem((HWND
)m_hWnd
, nmHDR
->iItem
, &item
);
1472 return (unsigned int)item
.lParam
;
1475 bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
1477 NMHDR
*nmhdr
= (NMHDR
*)lParam
;
1479 // is it a message from the header?
1480 if ( nmhdr
->hwndFrom
!= (HWND
)m_hWnd
)
1481 return wxWindow::MSWOnNotify(idCtrl
, lParam
, result
);
1483 NMHEADER
*nmHDR
= (NMHEADER
*)nmhdr
;
1484 switch ( nmhdr
->code
)
1486 case HDN_BEGINTRACK
:
1487 // user has started to resize a column:
1488 // do we need to veto it?
1489 if (!GetColumn(nmHDR
->iItem
)->IsResizeable())
1497 // user has started to reorder a column
1498 if ((nmHDR
->iItem
!= -1) && (!GetColumn(nmHDR
->iItem
)->IsReorderable()))
1505 case HDN_ENDDRAG
: // user has finished reordering a column
1507 wxDataViewColumn
*col
= GetColumn(nmHDR
->iItem
);
1508 unsigned int new_pos
= nmHDR
->pitem
->iOrder
;
1509 m_owner
->ColumnMoved( col
, new_pos
);
1510 m_delayedUpdate
= true;
1514 case HDN_ITEMCHANGING
:
1515 if (nmHDR
->pitem
!= NULL
&&
1516 (nmHDR
->pitem
->mask
& HDI_WIDTH
) != 0)
1518 int minWidth
= GetColumnFromHeader(nmHDR
)->GetMinWidth();
1519 if (nmHDR
->pitem
->cxy
< minWidth
)
1521 // do not allow the user to resize this column under
1522 // its minimal width:
1528 case HDN_ITEMCHANGED
: // user is resizing a column
1529 case HDN_ENDTRACK
: // user has finished resizing a column
1531 // update the width of the modified column:
1532 if (nmHDR
->pitem
!= NULL
&&
1533 (nmHDR
->pitem
->mask
& HDI_WIDTH
) != 0)
1535 unsigned int idx
= GetColumnIdxFromHeader(nmHDR
);
1536 unsigned int w
= nmHDR
->pitem
->cxy
;
1537 wxDataViewColumn
*col
= GetColumn(idx
);
1539 // see UpdateDisplay() for more info about COLUMN_WIDTH_OFFSET
1540 if (idx
== 0 && w
> COLUMN_WIDTH_OFFSET
)
1541 w
-= COLUMN_WIDTH_OFFSET
;
1543 if (w
>= (unsigned)col
->GetMinWidth())
1544 col
->SetInternalWidth(w
);
1550 unsigned int idx
= GetColumnIdxFromHeader(nmHDR
);
1551 wxDataViewModel
* model
= GetOwner()->GetModel();
1553 if(nmHDR
->iButton
== 0)
1555 wxDataViewColumn
*col
= GetColumn(idx
);
1556 if(col
->IsSortable())
1558 if(model
&& m_owner
->GetSortingColumn() == col
)
1560 bool order
= col
->IsSortOrderAscending();
1561 col
->SetSortOrder(!order
);
1565 m_owner
->SetSortingColumn(col
);
1573 wxEventType evt
= nmHDR
->iButton
== 0 ?
1574 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK
:
1575 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK
;
1576 SendEvent(evt
, idx
);
1582 // NOTE: for some reason (i.e. for a bug in Windows)
1583 // the HDN_ITEMCLICK notification is not sent on
1584 // right clicks, so we need to handle NM_RCLICK
1587 int column
= wxMSWGetColumnClicked(nmhdr
, &ptClick
);
1588 if (column
!= wxNOT_FOUND
)
1591 item
.mask
= HDI_LPARAM
;
1592 Header_GetItem((HWND
)m_hWnd
, column
, &item
);
1594 // 'idx' may be different from 'column' if there are
1595 // hidden columns...
1596 unsigned int idx
= (unsigned int)item
.lParam
;
1597 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK
,
1603 case HDN_GETDISPINFOW
:
1604 // see wxListCtrl::MSWOnNotify for more info!
1607 case HDN_ITEMDBLCLICK
:
1609 unsigned int idx
= GetColumnIdxFromHeader(nmHDR
);
1610 int w
= GetOwner()->GetBestColumnWidth(idx
);
1612 // update the native control:
1614 ZeroMemory(&hd
, sizeof(hd
));
1615 hd
.mask
= HDI_WIDTH
;
1617 Header_SetItem(GetHwnd(),
1618 nmHDR
->iItem
, // NOTE: we don't want 'idx' here!
1621 // update the wxDataViewColumn class:
1622 GetColumn(idx
)->SetInternalWidth(w
);
1627 return wxWindow::MSWOnNotify(idCtrl
, lParam
, result
);
1633 void wxDataViewHeaderWindowMSW::ScrollWindow(int dx
, int WXUNUSED(dy
),
1634 const wxRect
* WXUNUSED(rect
))
1636 m_scrollOffsetX
+= dx
;
1638 GetParent()->Layout();
1641 void wxDataViewHeaderWindowMSW::DoSetSize(int x
, int y
,
1645 // TODO: why is there a border + 2px around it?
1646 wxControl::DoSetSize( x
+m_scrollOffsetX
+1, y
+1, w
-m_scrollOffsetX
-2, h
-2, f
);
1649 #else // !defined(__WXMSW__)
1651 IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow
, wxWindow
)
1652 BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow
, wxWindow
)
1653 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint
)
1654 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse
)
1655 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus
)
1658 bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl
*parent
, wxWindowID id
,
1659 const wxPoint
&pos
, const wxSize
&size
,
1660 const wxString
&name
)
1664 if (!wxDataViewHeaderWindowBase::Create(parent
, id
, pos
, size
, name
))
1667 wxVisualAttributes attr
= wxPanel::GetClassDefaultAttributes();
1668 SetBackgroundStyle( wxBG_STYLE_CUSTOM
);
1669 SetOwnForegroundColour( attr
.colFg
);
1670 SetOwnBackgroundColour( attr
.colBg
);
1672 SetOwnFont( attr
.font
);
1674 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1675 // a wxBoxSizer and in order to avoid super-big header windows,
1676 // we need to set our height as fixed
1677 SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT
));
1678 SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT
));
1683 void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
1686 GetClientSize( &w
, &h
);
1688 wxAutoBufferedPaintDC
dc( this );
1690 dc
.SetBackground(GetBackgroundColour());
1694 m_owner
->GetScrollPixelsPerUnit( &xpix
, NULL
);
1697 m_owner
->GetViewStart( &x
, NULL
);
1699 // account for the horz scrollbar offset
1700 dc
.SetDeviceOrigin( -x
* xpix
, 0 );
1702 dc
.SetFont( GetFont() );
1704 unsigned int cols
= GetOwner()->GetColumnCount();
1707 for (i
= 0; i
< cols
; i
++)
1709 wxDataViewColumn
*col
= GetColumn( i
);
1710 if (col
->IsHidden())
1711 continue; // skip it!
1713 int cw
= col
->GetWidth();
1716 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
;
1717 if (col
->IsSortable() && GetOwner()->GetSortingColumn() == col
)
1719 if (col
->IsSortOrderAscending())
1720 sortArrow
= wxHDR_SORT_ICON_UP
;
1722 sortArrow
= wxHDR_SORT_ICON_DOWN
;
1726 if (m_parent
->IsEnabled())
1728 if ((int) i
== m_hover
)
1729 state
= wxCONTROL_CURRENT
;
1733 state
= (int) wxCONTROL_DISABLED
;
1736 wxRendererNative::Get().DrawHeaderButton
1740 wxRect(xpos
, 0, cw
, ch
-1),
1745 // align as required the column title:
1747 wxSize titleSz
= dc
.GetTextExtent(col
->GetTitle());
1748 switch (col
->GetAlignment())
1751 x
+= HEADER_HORIZ_BORDER
;
1754 x
+= cw
- titleSz
.GetWidth() - HEADER_HORIZ_BORDER
;
1757 case wxALIGN_CENTER
:
1758 case wxALIGN_CENTER_HORIZONTAL
:
1759 x
+= (cw
- titleSz
.GetWidth() - 2 * HEADER_HORIZ_BORDER
)/2;
1763 // always center the title vertically:
1764 int y
= wxMax((ch
- titleSz
.GetHeight()) / 2, HEADER_VERT_BORDER
);
1766 dc
.SetClippingRegion( xpos
+HEADER_HORIZ_BORDER
,
1768 wxMax(cw
- 2 * HEADER_HORIZ_BORDER
, 1), // width
1769 wxMax(ch
- 2 * HEADER_VERT_BORDER
, 1)); // height
1770 dc
.DrawText( col
->GetTitle(), x
, y
);
1771 dc
.DestroyClippingRegion();
1777 void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent
&event
)
1779 GetParent()->SetFocus();
1783 void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent
&event
)
1785 // we want to work with logical coords
1787 m_owner
->CalcUnscrolledPosition(event
.GetX(), 0, &x
, NULL
);
1788 int y
= event
.GetY();
1792 // we don't draw the line beyond our window,
1793 // but we allow dragging it there
1795 GetClientSize( &w
, NULL
);
1796 m_owner
->CalcUnscrolledPosition(w
, 0, &w
, NULL
);
1799 if (event
.ButtonUp())
1801 m_isDragging
= false;
1807 m_currentX
= wxMax(m_minX
+ 7, x
);
1811 GetColumn(m_column
)->SetWidth(m_currentX
- m_minX
);
1813 GetOwner()->Refresh();
1817 else // not dragging
1820 m_column
= wxNOT_FOUND
;
1822 bool hit_border
= false;
1824 // end of the current column
1827 // find the column where this event occured
1828 int countCol
= m_owner
->GetColumnCount();
1829 for (int column
= 0; column
< countCol
; column
++)
1831 wxDataViewColumn
*p
= GetColumn(column
);
1834 continue; // skip if not shown
1836 xpos
+= p
->GetWidth();
1838 if ((abs(x
-xpos
) < 3) && (y
< 22))
1846 // inside the column
1853 int old_hover
= m_hover
;
1855 if (event
.Leaving())
1856 m_hover
= wxNOT_FOUND
;
1857 if (old_hover
!= m_hover
)
1860 if (m_column
== wxNOT_FOUND
)
1863 bool resizeable
= GetColumn(m_column
)->IsResizeable();
1864 if (event
.LeftDClick() && resizeable
)
1866 GetColumn(m_column
)->SetWidth(GetOwner()->GetBestColumnWidth(m_column
));
1869 else if (event
.LeftDown() || event
.RightUp())
1871 if (hit_border
&& event
.LeftDown() && resizeable
)
1873 m_isDragging
= true;
1877 else // click on a column
1879 wxDataViewModel
* model
= GetOwner()->GetModel();
1880 wxEventType evt
= event
.LeftDown() ?
1881 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK
:
1882 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK
;
1883 SendEvent(evt
, m_column
);
1885 //Left click the header
1886 if(event
.LeftDown())
1888 wxDataViewColumn
*col
= GetColumn(m_column
);
1889 if(col
->IsSortable())
1891 wxDataViewColumn
* sortCol
= m_owner
->GetSortingColumn();
1892 if(model
&& sortCol
== col
)
1894 bool order
= col
->IsSortOrderAscending();
1895 col
->SetSortOrder(!order
);
1899 m_owner
->SetSortingColumn(col
);
1905 //Send the column sorted event
1906 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED
, m_column
);
1910 else if (event
.Moving())
1912 if (hit_border
&& resizeable
)
1913 m_currentCursor
= m_resizeCursor
;
1915 m_currentCursor
= wxSTANDARD_CURSOR
;
1917 SetCursor(*m_currentCursor
);
1922 void wxGenericDataViewHeaderWindow::AdjustDC(wxDC
& dc
)
1926 m_owner
->GetScrollPixelsPerUnit( &xpix
, NULL
);
1927 m_owner
->GetViewStart( &x
, NULL
);
1929 // shift the DC origin to match the position of the main window horizontal
1930 // scrollbar: this allows us to always use logical coords
1931 dc
.SetDeviceOrigin( -x
* xpix
, 0 );
1934 #endif // defined(__WXMSW__)
1936 //-----------------------------------------------------------------------------
1937 // wxDataViewRenameTimer
1938 //-----------------------------------------------------------------------------
1940 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow
*owner
)
1945 void wxDataViewRenameTimer::Notify()
1947 m_owner
->OnRenameTimer();
1950 //-----------------------------------------------------------------------------
1951 // wxDataViewMainWindow
1952 //-----------------------------------------------------------------------------
1954 //The tree building helper, declared firstly
1955 static void BuildTreeHelper( wxDataViewModel
* model
, wxDataViewItem
& item
, wxDataViewTreeNode
* node
);
1957 int LINKAGEMODE
wxDataViewSelectionCmp( unsigned int row1
, unsigned int row2
)
1959 if (row1
> row2
) return 1;
1960 if (row1
== row2
) return 0;
1965 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow
, wxWindow
)
1967 BEGIN_EVENT_TABLE(wxDataViewMainWindow
,wxWindow
)
1968 EVT_PAINT (wxDataViewMainWindow::OnPaint
)
1969 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse
)
1970 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus
)
1971 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus
)
1972 EVT_CHAR (wxDataViewMainWindow::OnChar
)
1975 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl
*parent
, wxWindowID id
,
1976 const wxPoint
&pos
, const wxSize
&size
, const wxString
&name
) :
1977 wxWindow( parent
, id
, pos
, size
, wxWANTS_CHARS
|wxBORDER_NONE
, name
),
1978 m_selection( wxDataViewSelectionCmp
)
1983 m_lastOnSame
= false;
1984 m_renameTimer
= new wxDataViewRenameTimer( this );
1986 // TODO: user better initial values/nothing selected
1987 m_currentCol
= NULL
;
1990 m_lineHeight
= wxMax( 17, GetCharHeight() + 2 ); // 17 = mini icon height + 1
1993 m_dragStart
= wxPoint(0,0);
1994 m_lineLastClicked
= (unsigned int) -1;
1995 m_lineBeforeLastClicked
= (unsigned int) -1;
1996 m_lineSelectSingleOnUp
= (unsigned int) -1;
2000 SetBackgroundColour( *wxWHITE
);
2002 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
2004 m_penRule
= wxPen(GetRuleColour());
2006 //Here I compose a pen can draw black lines, maybe there are something system colour to use
2007 m_penExpander
= wxPen(wxColour(0,0,0));
2008 //Some new added code to deal with the tree structure
2009 m_root
= new wxDataViewTreeNode( NULL
);
2010 m_root
->SetHasChildren(true);
2012 //Make m_count = -1 will cause the class recaculate the real displaying number of rows.
2014 m_underMouse
= NULL
;
2018 wxDataViewMainWindow::~wxDataViewMainWindow()
2021 delete m_renameTimer
;
2024 void wxDataViewMainWindow::OnPaint( wxPaintEvent
&WXUNUSED(event
) )
2026 wxDataViewModel
*model
= GetOwner()->GetModel();
2027 wxAutoBufferedPaintDC
dc( this );
2030 dc
.SetPen( *wxTRANSPARENT_PEN
);
2031 dc
.SetBrush( wxBrush( GetBackgroundColour()) );
2032 dc
.SetBrush( *wxWHITE_BRUSH
);
2033 wxSize
size( GetClientSize() );
2034 dc
.DrawRectangle( 0,0,size
.x
,size
.y
);
2038 GetOwner()->PrepareDC( dc
);
2039 dc
.SetFont( GetFont() );
2041 wxRect update
= GetUpdateRegion().GetBox();
2042 m_owner
->CalcUnscrolledPosition( update
.x
, update
.y
, &update
.x
, &update
.y
);
2044 // compute which items needs to be redrawn
2045 unsigned int item_start
= wxMax( 0, (update
.y
/ m_lineHeight
) );
2046 unsigned int item_count
=
2047 wxMin( (int)(((update
.y
+ update
.height
) / m_lineHeight
) - item_start
+ 1),
2048 (int)(GetRowCount( ) - item_start
));
2049 unsigned int item_last
= item_start
+ item_count
;
2051 // compute which columns needs to be redrawn
2052 unsigned int cols
= GetOwner()->GetColumnCount();
2053 unsigned int col_start
= 0;
2054 unsigned int x_start
= 0;
2055 for (x_start
= 0; col_start
< cols
; col_start
++)
2057 wxDataViewColumn
*col
= GetOwner()->GetColumn(col_start
);
2058 if (col
->IsHidden())
2059 continue; // skip it!
2061 unsigned int w
= col
->GetWidth();
2062 if (x_start
+w
>= (unsigned int)update
.x
)
2068 unsigned int col_last
= col_start
;
2069 unsigned int x_last
= x_start
;
2070 for (; col_last
< cols
; col_last
++)
2072 wxDataViewColumn
*col
= GetOwner()->GetColumn(col_last
);
2073 if (col
->IsHidden())
2074 continue; // skip it!
2076 if (x_last
> (unsigned int)update
.GetRight())
2079 x_last
+= col
->GetWidth();
2082 // Draw horizontal rules if required
2083 if ( m_owner
->HasFlag(wxDV_HORIZ_RULES
) )
2085 dc
.SetPen(m_penRule
);
2086 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
2088 for (unsigned int i
= item_start
; i
<= item_last
+1; i
++)
2090 int y
= i
* m_lineHeight
;
2091 dc
.DrawLine(x_start
, y
, x_last
, y
);
2095 // Draw vertical rules if required
2096 if ( m_owner
->HasFlag(wxDV_VERT_RULES
) )
2098 dc
.SetPen(m_penRule
);
2099 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
2102 for (unsigned int i
= col_start
; i
< col_last
; i
++)
2104 wxDataViewColumn
*col
= GetOwner()->GetColumn(i
);
2105 if (col
->IsHidden())
2106 continue; // skip it
2108 dc
.DrawLine(x
, item_start
* m_lineHeight
,
2109 x
, item_last
* m_lineHeight
);
2111 x
+= col
->GetWidth();
2114 // Draw last vertical rule
2115 dc
.DrawLine(x
, item_start
* m_lineHeight
,
2116 x
, item_last
* m_lineHeight
);
2119 // redraw the background for the items which are selected/current
2120 for (unsigned int item
= item_start
; item
< item_last
; item
++)
2122 bool selected
= m_selection
.Index( item
) != wxNOT_FOUND
;
2123 if (selected
|| item
== m_currentRow
)
2125 int flags
= selected
? (int)wxCONTROL_SELECTED
: 0;
2126 if (item
== m_currentRow
)
2127 flags
|= wxCONTROL_CURRENT
;
2129 flags
|= wxCONTROL_FOCUSED
;
2131 wxRect
rect( x_start
, item
*m_lineHeight
, x_last
, m_lineHeight
);
2132 wxRendererNative::Get().DrawItemSelectionRect
2142 wxDataViewColumn
*expander
= GetOwner()->GetExpanderColumn();
2145 // TODO: last column for RTL support
2146 expander
= GetOwner()->GetColumn( 0 );
2147 GetOwner()->SetExpanderColumn(expander
);
2150 // redraw all cells for all rows which must be repainted and for all columns
2152 cell_rect
.x
= x_start
;
2153 cell_rect
.height
= m_lineHeight
; // -1 is for the horizontal rules
2154 for (unsigned int i
= col_start
; i
< col_last
; i
++)
2156 wxDataViewColumn
*col
= GetOwner()->GetColumn( i
);
2157 wxDataViewRenderer
*cell
= col
->GetRenderer();
2158 cell_rect
.width
= col
->GetWidth();
2160 if (col
->IsHidden())
2161 continue; // skipt it!
2164 for (unsigned int item
= item_start
; item
< item_last
; item
++)
2166 // get the cell value and set it into the renderer
2168 wxDataViewTreeNode
*node
= NULL
;
2169 wxDataViewItem dataitem
;
2173 node
= GetTreeNodeByRow(item
);
2177 dataitem
= node
->GetItem();
2179 if ((i
> 0) && model
->IsContainer(dataitem
) && !model
->HasContainerColumns(dataitem
))
2184 dataitem
= wxDataViewItem( (void*) item
);
2187 model
->GetValue( value
, dataitem
, col
->GetModelColumn());
2188 cell
->SetValue( value
);
2190 if (cell
->GetWantsAttr())
2192 wxDataViewItemAttr attr
;
2193 bool ret
= model
->GetAttr( dataitem
, col
->GetModelColumn(), attr
);
2195 cell
->SetAttr( attr
);
2196 cell
->SetHasAttr( ret
);
2199 // update the y offset
2200 cell_rect
.y
= item
* m_lineHeight
;
2202 //Draw the expander here.
2204 if ((m_root
) && (col
== expander
))
2206 indent
= node
->GetIndentLevel();
2208 //Calculate the indent first
2209 indent
= cell_rect
.x
+ GetOwner()->GetIndent() * indent
;
2211 int expander_width
= m_lineHeight
- 2*EXPANDER_MARGIN
;
2212 // change the cell_rect.x to the appropriate pos
2213 int expander_x
= indent
+ EXPANDER_MARGIN
, expander_y
= cell_rect
.y
+ EXPANDER_MARGIN
;
2214 indent
= indent
+ m_lineHeight
; //try to use the m_lineHeight as the expander space
2215 dc
.SetPen( m_penExpander
);
2216 dc
.SetBrush( wxNullBrush
);
2217 if( node
->HasChildren() )
2219 wxRect
rect( expander_x
, expander_y
, expander_width
, expander_width
);
2221 if (m_underMouse
== node
)
2223 flag
|= wxCONTROL_CURRENT
;
2225 if( node
->IsOpen() )
2226 wxRendererNative::Get().DrawTreeItemButton( this, dc
, rect
, flag
|wxCONTROL_EXPANDED
);
2228 wxRendererNative::Get().DrawTreeItemButton( this, dc
, rect
, flag
);
2230 //force the expander column to left-center align
2231 cell
->SetAlignment( wxALIGN_CENTER_VERTICAL
);
2233 if (node
&& !node
->HasChildren())
2235 // Yes, if the node does not have any child, it must be a leaf which
2236 // mean that it is a temporarily created by GetTreeNodeByRow
2240 // cannot be bigger than allocated space
2241 wxSize size
= cell
->GetSize();
2242 // Because of the tree structure indent, here we should minus the width of the cell for drawing
2243 size
.x
= wxMin( size
.x
+ 2*PADDING_RIGHTLEFT
, cell_rect
.width
- indent
);
2244 // size.y = wxMin( size.y, cell_rect.height );
2245 size
.y
= cell_rect
.height
;
2247 wxRect
item_rect(cell_rect
.GetTopLeft(), size
);
2248 int align
= cell
->CalculateAlignment();
2250 // horizontal alignment:
2251 item_rect
.x
= cell_rect
.x
;
2252 if (align
& wxALIGN_CENTER_HORIZONTAL
)
2253 item_rect
.x
= cell_rect
.x
+ (cell_rect
.width
/ 2) - (size
.x
/ 2);
2254 else if (align
& wxALIGN_RIGHT
)
2255 item_rect
.x
= cell_rect
.x
+ cell_rect
.width
- size
.x
;
2256 //else: wxALIGN_LEFT is the default
2258 // vertical alignment:
2259 item_rect
.y
= cell_rect
.y
;
2260 if (align
& wxALIGN_CENTER_VERTICAL
)
2261 item_rect
.y
= cell_rect
.y
+ (cell_rect
.height
/ 2) - (size
.y
/ 2);
2262 else if (align
& wxALIGN_BOTTOM
)
2263 item_rect
.y
= cell_rect
.y
+ cell_rect
.height
- size
.y
;
2264 //else: wxALIGN_TOP is the default
2267 item_rect
.x
+= PADDING_RIGHTLEFT
;
2268 item_rect
.width
= size
.x
- 2 * PADDING_RIGHTLEFT
;
2270 //Here we add the tree indent
2271 item_rect
.x
+= indent
;
2274 if (m_hasFocus
&& (m_selection
.Index(item
) != wxNOT_FOUND
))
2275 state
|= wxDATAVIEW_CELL_SELECTED
;
2277 // TODO: it would be much more efficient to create a clipping
2278 // region for the entire column being rendered (in the OnPaint
2279 // of wxDataViewMainWindow) instead of a single clip region for
2280 // each cell. However it would mean that each renderer should
2281 // respect the given wxRect's top & bottom coords, eventually
2282 // violating only the left & right coords - however the user can
2283 // make its own renderer and thus we cannot be sure of that.
2284 dc
.SetClippingRegion( item_rect
);
2285 cell
->Render( item_rect
, &dc
, state
);
2286 dc
.DestroyClippingRegion();
2289 cell_rect
.x
+= cell_rect
.width
;
2293 void wxDataViewMainWindow::OnRenameTimer()
2295 // We have to call this here because changes may just have
2296 // been made and no screen update taken place.
2301 unsigned int cols
= GetOwner()->GetColumnCount();
2303 for (i
= 0; i
< cols
; i
++)
2305 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
2307 continue; // skip it!
2309 if (c
== m_currentCol
)
2311 xpos
+= c
->GetWidth();
2313 wxRect
labelRect( xpos
, m_currentRow
* m_lineHeight
,
2314 m_currentCol
->GetWidth(), m_lineHeight
);
2316 GetOwner()->CalcScrolledPosition( labelRect
.x
, labelRect
.y
,
2317 &labelRect
.x
, &labelRect
.y
);
2319 wxDataViewItem item
= GetItemByRow( m_currentRow
);
2320 m_currentCol
->GetRenderer()->StartEditing( item
, labelRect
);
2324 //------------------------------------------------------------------
2325 // Helper class for do operation on the tree node
2326 //------------------------------------------------------------------
2333 //The return value control how the tree-walker tranverse the tree
2334 // 0: Job done, stop tranverse and return
2335 // 1: Ignore the current node's subtree and continue
2336 // 2: Job not done, continue
2337 enum { OK
= 0 , IGR
= 1, CONT
= 2 };
2338 virtual int operator() ( wxDataViewTreeNode
* node
) = 0 ;
2339 virtual int operator() ( void * n
) = 0;
2342 bool Walker( wxDataViewTreeNode
* node
, DoJob
& func
)
2347 switch( func( node
) )
2358 wxDataViewTreeNodes nodes
= node
->GetNodes();
2359 wxDataViewTreeLeaves leaves
= node
->GetChildren();
2361 int len_nodes
= nodes
.GetCount();
2362 int len
= leaves
.GetCount();
2363 int i
= 0, nodes_i
= 0;
2365 for( ; i
< len
; i
++ )
2367 void * n
= leaves
[i
];
2368 if( nodes_i
< len_nodes
&& n
== nodes
[nodes_i
]->GetItem().GetID() )
2370 wxDataViewTreeNode
* nd
= nodes
[nodes_i
];
2373 if( Walker( nd
, func
) )
2392 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem
& parent
, const wxDataViewItem
& item
)
2403 wxDataViewTreeNode
* node
;
2404 node
= FindNode(parent
);
2409 node
->SetHasChildren( true );
2411 if( g_model
->IsContainer( item
) )
2413 wxDataViewTreeNode
* newnode
= new wxDataViewTreeNode( node
);
2414 newnode
->SetItem(item
);
2415 newnode
->SetHasChildren( true );
2416 node
->AddNode( newnode
);
2419 node
->AddLeaf( item
.GetID() );
2421 node
->ChangeSubTreeCount(1);
2429 static void DestroyTreeHelper( wxDataViewTreeNode
* node
);
2431 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem
& parent
,
2432 const wxDataViewItem
& item
)
2437 if( m_currentRow
> GetRowCount() )
2438 m_currentRow
= m_count
- 1;
2440 m_selection
.Empty();
2447 wxDataViewTreeNode
* node
= FindNode(parent
);
2449 wxCHECK_MSG( node
!= NULL
, false, "item not found" );
2450 wxCHECK_MSG( node
->GetChildren().Index( item
.GetID() ) != wxNOT_FOUND
, false, "item not found" );
2453 node
->GetChildren().Remove( item
.GetID() );
2454 //Manuplate selection
2455 if( m_selection
.GetCount() > 1 )
2457 m_selection
.Empty();
2459 bool isContainer
= false;
2460 wxDataViewTreeNodes nds
= node
->GetNodes();
2461 for (size_t i
= 0; i
< nds
.GetCount(); i
++)
2463 if (nds
[i
]->GetItem() == item
)
2471 wxDataViewTreeNode
* n
= NULL
;
2472 wxDataViewTreeNodes nodes
= node
->GetNodes();
2473 int len
= nodes
.GetCount();
2474 for( int i
= 0 ; i
< len
; i
++)
2476 if( nodes
[i
]->GetItem() == item
)
2483 wxCHECK_MSG( n
!= NULL
, false, "item not found" );
2485 node
->GetNodes().Remove( n
);
2486 sub
-= n
->GetSubTreeCount();
2487 ::DestroyTreeHelper(n
);
2489 //Make the row number invalid and get a new valid one when user call GetRowCount
2491 node
->ChangeSubTreeCount(sub
);
2493 //Change the current row to the last row if the current exceed the max row number
2494 if( m_currentRow
> GetRowCount() )
2495 m_currentRow
= m_count
- 1;
2502 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem
& item
)
2508 wxWindow
*parent
= GetParent();
2509 wxDataViewEvent
le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED
, parent
->GetId());
2510 le
.SetEventObject(parent
);
2511 le
.SetModel(GetOwner()->GetModel());
2513 parent
->GetEventHandler()->ProcessEvent(le
);
2518 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem
& item
, unsigned int col
)
2520 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
2521 /*#define MAX_VIRTUAL_WIDTH 100000
2523 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
2524 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2525 Refresh( true, &rect );
2533 wxWindow
*parent
= GetParent();
2534 wxDataViewEvent
le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED
, parent
->GetId());
2535 le
.SetEventObject(parent
);
2536 le
.SetModel(GetOwner()->GetModel());
2539 le
.SetDataViewColumn(GetOwner()->GetColumn(col
));
2540 parent
->GetEventHandler()->ProcessEvent(le
);
2545 bool wxDataViewMainWindow::Cleared()
2550 BuildTree( GetOwner()->GetModel() );
2557 void wxDataViewMainWindow::UpdateDisplay()
2562 void wxDataViewMainWindow::OnInternalIdle()
2564 wxWindow::OnInternalIdle();
2568 RecalculateDisplay();
2573 void wxDataViewMainWindow::RecalculateDisplay()
2575 wxDataViewModel
*model
= GetOwner()->GetModel();
2582 int width
= GetEndOfLastCol();
2583 int height
= GetRowCount() * m_lineHeight
;
2585 SetVirtualSize( width
, height
);
2586 GetOwner()->SetScrollRate( 10, m_lineHeight
);
2591 void wxDataViewMainWindow::ScrollWindow( int dx
, int dy
, const wxRect
*rect
)
2593 wxWindow::ScrollWindow( dx
, dy
, rect
);
2595 if (GetOwner()->m_headerArea
)
2596 GetOwner()->m_headerArea
->ScrollWindow( dx
, 0 );
2599 void wxDataViewMainWindow::ScrollTo( int rows
, int column
)
2602 m_owner
->GetScrollPixelsPerUnit( &x
, &y
);
2603 int sy
= rows
*m_lineHeight
/y
;
2607 wxRect rect
= GetClientRect();
2609 int x_start
= 0, x_end
= 0, w
= 0;
2611 m_owner
->CalcUnscrolledPosition( rect
.x
, rect
.y
, &xx
, &yy
);
2612 for (x_start
= 0; colnum
< column
; colnum
++)
2614 wxDataViewColumn
*col
= GetOwner()->GetColumn(colnum
);
2615 if (col
->IsHidden())
2616 continue; // skip it!
2618 w
= col
->GetWidth();
2622 x_end
= x_start
+ w
;
2623 xe
= xx
+ rect
.width
;
2626 sx
= ( xx
+ x_end
- xe
)/x
;
2633 m_owner
->Scroll( sx
, sy
);
2636 int wxDataViewMainWindow::GetCountPerPage() const
2638 wxSize size
= GetClientSize();
2639 return size
.y
/ m_lineHeight
;
2642 int wxDataViewMainWindow::GetEndOfLastCol() const
2646 for (i
= 0; i
< GetOwner()->GetColumnCount(); i
++)
2648 const wxDataViewColumn
*c
=
2649 wx_const_cast(wxDataViewCtrl
*, GetOwner())->GetColumn( i
);
2652 width
+= c
->GetWidth();
2657 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
2661 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
2663 return y
/ m_lineHeight
;
2666 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
2668 wxSize client_size
= GetClientSize();
2669 m_owner
->CalcUnscrolledPosition( client_size
.x
, client_size
.y
,
2670 &client_size
.x
, &client_size
.y
);
2672 //we should deal with the pixel here
2673 unsigned int row
= ((client_size
.y
)/m_lineHeight
) - 1;
2675 return wxMin( GetRowCount()-1, row
);
2678 unsigned int wxDataViewMainWindow::GetRowCount()
2680 if ( m_count
== -1 )
2682 m_count
= RecalculateCount();
2684 GetVirtualSize( &width
, &height
);
2685 height
= m_count
* m_lineHeight
;
2687 SetVirtualSize( width
, height
);
2692 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row
)
2699 void wxDataViewMainWindow::SelectAllRows( bool on
)
2706 m_selection
.Clear();
2707 for (unsigned int i
= 0; i
< GetRowCount(); i
++)
2708 m_selection
.Add( i
);
2713 unsigned int first_visible
= GetFirstVisibleRow();
2714 unsigned int last_visible
= GetLastVisibleRow();
2716 for (i
= 0; i
< m_selection
.GetCount(); i
++)
2718 unsigned int row
= m_selection
[i
];
2719 if ((row
>= first_visible
) && (row
<= last_visible
))
2722 m_selection
.Clear();
2726 void wxDataViewMainWindow::SelectRow( unsigned int row
, bool on
)
2728 if (m_selection
.Index( row
) == wxNOT_FOUND
)
2732 m_selection
.Add( row
);
2740 m_selection
.Remove( row
);
2746 void wxDataViewMainWindow::SelectRows( unsigned int from
, unsigned int to
, bool on
)
2750 unsigned int tmp
= from
;
2756 for (i
= from
; i
<= to
; i
++)
2758 if (m_selection
.Index( i
) == wxNOT_FOUND
)
2761 m_selection
.Add( i
);
2766 m_selection
.Remove( i
);
2769 RefreshRows( from
, to
);
2772 void wxDataViewMainWindow::Select( const wxArrayInt
& aSelections
)
2774 for (size_t i
=0; i
< aSelections
.GetCount(); i
++)
2776 int n
= aSelections
[i
];
2778 m_selection
.Add( n
);
2783 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row
)
2785 if (m_selection
.Index( row
) == wxNOT_FOUND
)
2786 m_selection
.Add( row
);
2788 m_selection
.Remove( row
);
2792 bool wxDataViewMainWindow::IsRowSelected( unsigned int row
)
2794 return (m_selection
.Index( row
) != wxNOT_FOUND
);
2797 void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem
& item
)
2799 wxWindow
*parent
= GetParent();
2800 wxDataViewEvent
le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED
, parent
->GetId());
2802 le
.SetEventObject(parent
);
2803 le
.SetModel(GetOwner()->GetModel());
2806 parent
->GetEventHandler()->ProcessEvent(le
);
2809 void wxDataViewMainWindow::RefreshRow( unsigned int row
)
2811 wxRect
rect( 0, row
*m_lineHeight
, GetEndOfLastCol(), m_lineHeight
);
2812 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
2814 wxSize client_size
= GetClientSize();
2815 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
2816 wxRect intersect_rect
= client_rect
.Intersect( rect
);
2817 if (intersect_rect
.width
> 0)
2818 Refresh( true, &intersect_rect
);
2821 void wxDataViewMainWindow::RefreshRows( unsigned int from
, unsigned int to
)
2825 unsigned int tmp
= to
;
2830 wxRect
rect( 0, from
*m_lineHeight
, GetEndOfLastCol(), (to
-from
+1) * m_lineHeight
);
2831 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
2833 wxSize client_size
= GetClientSize();
2834 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
2835 wxRect intersect_rect
= client_rect
.Intersect( rect
);
2836 if (intersect_rect
.width
> 0)
2837 Refresh( true, &intersect_rect
);
2840 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow
)
2842 unsigned int count
= GetRowCount();
2843 if (firstRow
> count
)
2846 wxRect
rect( 0, firstRow
*m_lineHeight
, GetEndOfLastCol(), count
* m_lineHeight
);
2847 m_owner
->CalcScrolledPosition( rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
2849 wxSize client_size
= GetClientSize();
2850 wxRect
client_rect( 0, 0, client_size
.x
, client_size
.y
);
2851 wxRect intersect_rect
= client_rect
.Intersect( rect
);
2852 if (intersect_rect
.width
> 0)
2853 Refresh( true, &intersect_rect
);
2856 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent
, const wxKeyEvent
& event
)
2858 wxCHECK_RET( newCurrent
< GetRowCount(),
2859 _T("invalid item index in OnArrowChar()") );
2861 // if there is no selection, we cannot move it anywhere
2862 if (!HasCurrentRow())
2865 unsigned int oldCurrent
= m_currentRow
;
2867 // in single selection we just ignore Shift as we can't select several
2869 if ( event
.ShiftDown() && !IsSingleSel() )
2871 RefreshRow( oldCurrent
);
2873 ChangeCurrentRow( newCurrent
);
2875 // select all the items between the old and the new one
2876 if ( oldCurrent
> newCurrent
)
2878 newCurrent
= oldCurrent
;
2879 oldCurrent
= m_currentRow
;
2882 SelectRows( oldCurrent
, newCurrent
, true );
2883 if (oldCurrent
!=newCurrent
)
2884 SendSelectionChangedEvent(GetItemByRow(m_selection
[0]));
2888 RefreshRow( oldCurrent
);
2890 // all previously selected items are unselected unless ctrl is held
2891 if ( !event
.ControlDown() )
2892 SelectAllRows(false);
2894 ChangeCurrentRow( newCurrent
);
2896 if ( !event
.ControlDown() )
2898 SelectRow( m_currentRow
, true );
2899 SendSelectionChangedEvent(GetItemByRow(m_currentRow
));
2902 RefreshRow( m_currentRow
);
2905 GetOwner()->EnsureVisible( m_currentRow
, -1 );
2908 wxRect
wxDataViewMainWindow::GetLineRect( unsigned int row
) const
2912 rect
.y
= m_lineHeight
* row
;
2913 rect
.width
= GetEndOfLastCol();
2914 rect
.height
= m_lineHeight
;
2919 class RowToItemJob
: public DoJob
2922 RowToItemJob( unsigned int row
, int current
) { this->row
= row
; this->current
= current
;}
2923 virtual ~RowToItemJob(){};
2925 virtual int operator() ( wxDataViewTreeNode
* node
)
2928 if( current
== static_cast<int>(row
))
2930 ret
= node
->GetItem() ;
2934 if( node
->GetSubTreeCount() + current
< static_cast<int>(row
) )
2936 current
+= node
->GetSubTreeCount();
2941 //If the current has no child node, we can find the desired item of the row number directly.
2942 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
2943 if( node
->GetNodes().GetCount() == 0)
2945 int index
= static_cast<int>(row
) - current
- 1;
2946 ret
= node
->GetChildren().Item( index
);
2953 virtual int operator() ( void * n
)
2956 if( current
== static_cast<int>(row
))
2958 ret
= wxDataViewItem( n
) ;
2963 wxDataViewItem
GetResult(){ return ret
; }
2970 wxDataViewItem
wxDataViewMainWindow::GetItemByRow(unsigned int row
) const
2974 return wxDataViewItem( (void*) row
);
2978 RowToItemJob
job( row
, -2 );
2979 Walker( m_root
, job
);
2980 return job
.GetResult();
2984 class RowToTreeNodeJob
: public DoJob
2987 RowToTreeNodeJob( unsigned int row
, int current
, wxDataViewTreeNode
* node
)
2990 this->current
= current
;
2994 virtual ~RowToTreeNodeJob(){};
2996 virtual int operator() ( wxDataViewTreeNode
* node
)
2999 if( current
== static_cast<int>(row
))
3005 if( node
->GetSubTreeCount() + current
< static_cast<int>(row
) )
3007 current
+= node
->GetSubTreeCount();
3013 //If the current has no child node, we can find the desired item of the row number directly.
3014 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
3015 if( node
->GetNodes().GetCount() == 0)
3017 int index
= static_cast<int>(row
) - current
- 1;
3018 void * n
= node
->GetChildren().Item( index
);
3019 ret
= new wxDataViewTreeNode( parent
) ;
3020 ret
->SetItem( wxDataViewItem( n
));
3021 ret
->SetHasChildren(false);
3030 virtual int operator() ( void * n
)
3033 if( current
== static_cast<int>(row
))
3035 ret
= new wxDataViewTreeNode( parent
) ;
3036 ret
->SetItem( wxDataViewItem( n
));
3037 ret
->SetHasChildren(false);
3043 wxDataViewTreeNode
* GetResult(){ return ret
; }
3047 wxDataViewTreeNode
* ret
;
3048 wxDataViewTreeNode
* parent
;
3052 wxDataViewTreeNode
* wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row
)
3060 RowToTreeNodeJob
job( row
, -2, m_root
);
3061 Walker( m_root
, job
);
3062 return job
.GetResult();
3066 wxDataViewEvent
wxDataViewMainWindow::SendExpanderEvent( wxEventType type
, const wxDataViewItem
& item
)
3068 wxWindow
*parent
= GetParent();
3069 wxDataViewEvent
le(type
, parent
->GetId());
3071 le
.SetEventObject(parent
);
3072 le
.SetModel(GetOwner()->GetModel());
3075 parent
->GetEventHandler()->ProcessEvent(le
);
3079 void wxDataViewMainWindow::OnExpanding( unsigned int row
)
3081 wxDataViewTreeNode
* node
= GetTreeNodeByRow(row
);
3084 if( node
->HasChildren())
3086 if( !node
->IsOpen())
3088 wxDataViewEvent e
= SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING
,node
->GetItem());
3089 //Check if the user prevent expanding
3090 if( e
.GetSkipped() )
3094 //Here I build the children of current node
3095 if( node
->GetChildrenNumber() == 0 )
3098 ::BuildTreeHelper(GetOwner()->GetModel(), node
->GetItem(), node
);
3102 //Send the expanded event
3103 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED
,node
->GetItem());
3107 SelectRow( row
, false );
3108 SelectRow( row
+ 1, true );
3109 ChangeCurrentRow( row
+ 1 );
3110 SendSelectionChangedEvent( GetItemByRow(row
+1));
3118 void wxDataViewMainWindow::OnCollapsing(unsigned int row
)
3120 wxDataViewTreeNode
* node
= GetTreeNodeByRow(row
);
3123 wxDataViewTreeNode
* nd
= node
;
3125 if( node
->HasChildren() && node
->IsOpen() )
3127 wxDataViewEvent e
= SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING
,node
->GetItem());
3128 if( e
.GetSkipped() )
3133 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED
,nd
->GetItem());
3137 node
= node
->GetParent();
3140 int parent
= GetRowByItem( node
->GetItem() ) ;
3143 SelectRow( row
, false);
3144 SelectRow(parent
, true );
3145 ChangeCurrentRow( parent
);
3146 SendSelectionChangedEvent( node
->GetItem() );
3150 if( !nd
->HasChildren())
3155 wxDataViewTreeNode
* wxDataViewMainWindow::FindNode( const wxDataViewItem
& item
)
3157 wxDataViewModel
* model
= GetOwner()->GetModel();
3161 //Compose the a parent-chain of the finding item
3163 list
.DeleteContents( true );
3164 wxDataViewItem
it( item
);
3167 wxDataViewItem
* pItem
= new wxDataViewItem( it
);
3168 list
.Insert( pItem
);
3169 it
= model
->GetParent( it
);
3172 //Find the item along the parent-chain.
3173 //This algorithm is designed to speed up the node-finding method
3174 wxDataViewTreeNode
* node
= m_root
;
3175 for( ItemList::const_iterator iter
= list
.begin(); iter
!=list
.end() ; iter
++ )
3177 if( node
->HasChildren() )
3179 if( node
->GetChildrenNumber() == 0 )
3182 ::BuildTreeHelper(model
, node
->GetItem(), node
);
3185 wxDataViewTreeNodes nodes
= node
->GetNodes();
3189 for (i
= 0; i
< nodes
.GetCount(); i
++)
3191 if (nodes
[i
]->GetItem() == (**iter
))
3193 if (nodes
[i
]->GetItem() == item
)
3210 void wxDataViewMainWindow::HitTest( const wxPoint
& point
, wxDataViewItem
& item
, wxDataViewColumn
* &column
)
3212 wxDataViewColumn
*col
= NULL
;
3213 unsigned int cols
= GetOwner()->GetColumnCount();
3214 unsigned int colnum
= 0;
3215 unsigned int x_start
= 0;
3217 m_owner
->CalcUnscrolledPosition( point
.x
, point
.y
, &x
, &y
);
3218 for (x_start
= 0; colnum
< cols
; colnum
++)
3220 col
= GetOwner()->GetColumn(colnum
);
3221 if (col
->IsHidden())
3222 continue; // skip it!
3224 unsigned int w
= col
->GetWidth();
3225 if (x_start
+w
>= (unsigned int)x
)
3232 item
= GetItemByRow( y
/m_lineHeight
);
3235 wxRect
wxDataViewMainWindow::GetItemRect( const wxDataViewItem
& item
, const wxDataViewColumn
* column
)
3237 int row
= GetRowByItem(item
);
3238 int y
= row
*m_lineHeight
;
3239 int h
= m_lineHeight
;
3241 wxDataViewColumn
*col
= NULL
;
3242 for( int i
= 0, cols
= GetOwner()->GetColumnCount(); i
< cols
; i
++ )
3244 col
= GetOwner()->GetColumn( i
);
3245 x
+= col
->GetWidth();
3246 if( GetOwner()->GetColumn(i
+1) == column
)
3249 int w
= col
->GetWidth();
3250 m_owner
->CalcScrolledPosition( x
, y
, &x
, &y
);
3251 return wxRect(x
, y
, w
, h
);
3254 int wxDataViewMainWindow::RecalculateCount()
3258 wxDataViewIndexListModel
*list_model
= (wxDataViewIndexListModel
*) GetOwner()->GetModel();
3260 return list_model
->GetLastIndex() + 1;
3262 return list_model
->GetLastIndex() - 1;
3267 return m_root
->GetSubTreeCount();
3271 class ItemToRowJob
: public DoJob
3274 ItemToRowJob(const wxDataViewItem
& item
, ItemList::const_iterator iter
)
3275 { this->item
= item
; ret
= -1 ; m_iter
= iter
; }
3276 virtual ~ItemToRowJob(){};
3278 //Maybe binary search will help to speed up this process
3279 virtual int operator() ( wxDataViewTreeNode
* node
)
3282 if( node
->GetItem() == item
)
3287 if( node
->GetItem() == **m_iter
)
3294 ret
+= node
->GetSubTreeCount();
3300 virtual int operator() ( void * n
)
3303 if( n
== item
.GetID() )
3307 //the row number is begin from zero
3308 int GetResult(){ return ret
-1 ; }
3310 ItemList::const_iterator m_iter
;
3311 wxDataViewItem item
;
3316 int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem
& item
)
3318 wxDataViewModel
* model
= GetOwner()->GetModel();
3324 return wxPtrToUInt( item
.GetID() );
3331 //Compose the a parent-chain of the finding item
3333 wxDataViewItem
* pItem
= NULL
;
3334 list
.DeleteContents( true );
3335 wxDataViewItem
it( item
);
3338 pItem
= new wxDataViewItem( it
);
3339 list
.Insert( pItem
);
3340 it
= model
->GetParent( it
);
3342 pItem
= new wxDataViewItem( );
3343 list
.Insert( pItem
);
3345 ItemToRowJob
job( item
, list
.begin() );
3346 Walker(m_root
, job
);
3347 return job
.GetResult();
3351 static void BuildTreeHelper( wxDataViewModel
* model
, wxDataViewItem
& item
, wxDataViewTreeNode
* node
)
3353 if( !model
->IsContainer( item
) )
3356 wxDataViewItemArray children
;
3357 unsigned int num
= model
->GetChildren( item
, children
);
3358 unsigned int index
= 0;
3359 while( index
< num
)
3361 if( model
->IsContainer( children
[index
] ) )
3363 wxDataViewTreeNode
* n
= new wxDataViewTreeNode( node
);
3364 n
->SetItem(children
[index
]);
3365 n
->SetHasChildren( true ) ;
3370 node
->AddLeaf( children
[index
].GetID() );
3374 node
->SetSubTreeCount( num
);
3375 wxDataViewTreeNode
* n
= node
->GetParent();
3377 n
->ChangeSubTreeCount(num
);
3381 void wxDataViewMainWindow::BuildTree(wxDataViewModel
* model
)
3385 if (GetOwner()->GetModel()->IsVirtualListModel())
3391 m_root
= new wxDataViewTreeNode( NULL
);
3392 m_root
->SetHasChildren(true);
3394 //First we define a invalid item to fetch the top-level elements
3395 wxDataViewItem item
;
3397 BuildTreeHelper( model
, item
, m_root
);
3401 static void DestroyTreeHelper( wxDataViewTreeNode
* node
)
3403 if( node
->GetNodeNumber() != 0 )
3405 int len
= node
->GetNodeNumber();
3407 wxDataViewTreeNodes
& nodes
= node
->GetNodes();
3408 for( ; i
< len
; i
++ )
3410 DestroyTreeHelper(nodes
[i
]);
3416 void wxDataViewMainWindow::DestroyTree()
3420 ::DestroyTreeHelper(m_root
);
3426 void wxDataViewMainWindow::OnChar( wxKeyEvent
&event
)
3428 if ( HandleAsNavigationKey(event
) )
3431 // no item -> nothing to do
3432 if (!HasCurrentRow())
3438 // don't use m_linesPerPage directly as it might not be computed yet
3439 const int pageSize
= GetCountPerPage();
3440 wxCHECK_RET( pageSize
, _T("should have non zero page size") );
3442 switch ( event
.GetKeyCode() )
3446 if (m_currentRow
> 0)
3448 wxWindow
*parent
= GetParent();
3449 wxDataViewEvent
le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED
, parent
->GetId());
3450 le
.SetItem( GetItemByRow(m_currentRow
) );
3451 le
.SetEventObject(parent
);
3452 le
.SetModel(GetOwner()->GetModel());
3454 parent
->GetEventHandler()->ProcessEvent(le
);
3459 if ( m_currentRow
> 0 )
3460 OnArrowChar( m_currentRow
- 1, event
);
3464 if ( m_currentRow
< GetRowCount() - 1 )
3465 OnArrowChar( m_currentRow
+ 1, event
);
3467 //Add the process for tree expanding/collapsing
3469 OnCollapsing(m_currentRow
);
3472 OnExpanding( m_currentRow
);
3476 OnArrowChar( GetRowCount() - 1, event
);
3481 OnArrowChar( 0, event
);
3486 int steps
= pageSize
- 1;
3487 int index
= m_currentRow
- steps
;
3491 OnArrowChar( index
, event
);
3497 int steps
= pageSize
- 1;
3498 unsigned int index
= m_currentRow
+ steps
;
3499 unsigned int count
= GetRowCount();
3500 if ( index
>= count
)
3503 OnArrowChar( index
, event
);
3512 void wxDataViewMainWindow::OnMouse( wxMouseEvent
&event
)
3514 if (event
.GetEventType() == wxEVT_MOUSEWHEEL
)
3516 // let the base handle mouse wheel events.
3521 int x
= event
.GetX();
3522 int y
= event
.GetY();
3523 m_owner
->CalcUnscrolledPosition( x
, y
, &x
, &y
);
3524 wxDataViewColumn
*col
= NULL
;
3527 unsigned int cols
= GetOwner()->GetColumnCount();
3529 for (i
= 0; i
< cols
; i
++)
3531 wxDataViewColumn
*c
= GetOwner()->GetColumn( i
);
3533 continue; // skip it!
3535 if (x
< xpos
+ c
->GetWidth())
3540 xpos
+= c
->GetWidth();
3545 wxDataViewRenderer
*cell
= col
->GetRenderer();
3546 unsigned int current
= y
/ m_lineHeight
;
3547 if ((current
> GetRowCount()) || (x
> GetEndOfLastCol()))
3549 // Unselect all if below the last row ?
3553 //Test whether the mouse is hovered on the tree item button
3555 if ((m_root
) && (GetOwner()->GetExpanderColumn() == col
))
3557 wxDataViewTreeNode
* node
= GetTreeNodeByRow(current
);
3558 if( node
!=NULL
&& node
->HasChildren() )
3560 int indent
= node
->GetIndentLevel();
3561 indent
= GetOwner()->GetIndent()*indent
;
3562 wxRect
rect( xpos
+ indent
+ EXPANDER_MARGIN
, current
* m_lineHeight
+ EXPANDER_MARGIN
, m_lineHeight
-2*EXPANDER_MARGIN
,m_lineHeight
-2*EXPANDER_MARGIN
);
3563 if( rect
.Contains( x
, y
) )
3565 //So the mouse is over the expander
3567 if (m_underMouse
&& m_underMouse
!= node
)
3569 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
3570 RefreshRow(GetRowByItem(m_underMouse
->GetItem()));
3572 if (m_underMouse
!= node
)
3574 //wxLogMessage("Do the row: %d", current);
3575 RefreshRow(current
);
3577 m_underMouse
= node
;
3580 if (node
!=NULL
&& !node
->HasChildren())
3585 if (m_underMouse
!= NULL
)
3587 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
3588 RefreshRow(GetRowByItem(m_underMouse
->GetItem()));
3589 m_underMouse
= NULL
;
3593 wxDataViewModel
*model
= GetOwner()->GetModel();
3595 if (event
.Dragging())
3597 if (m_dragCount
== 0)
3599 // we have to report the raw, physical coords as we want to be
3600 // able to call HitTest(event.m_pointDrag) from the user code to
3601 // get the item being dragged
3602 m_dragStart
= event
.GetPosition();
3607 if (m_dragCount
!= 3)
3610 if (event
.LeftIsDown())
3612 // Notify cell about drag
3621 bool forceClick
= false;
3623 if (event
.ButtonDClick())
3625 m_renameTimer
->Stop();
3626 m_lastOnSame
= false;
3629 wxDataViewItem item
= GetItemByRow(current
);
3630 bool ignore_other_columns
=
3631 ((GetOwner()->GetExpanderColumn() != col
) &&
3632 (model
->IsContainer(item
)) &&
3633 (!model
->HasContainerColumns(item
)));
3635 if (event
.LeftDClick())
3637 if ( current
== m_lineLastClicked
)
3639 if ((!ignore_other_columns
) && (cell
->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE
))
3642 model
->GetValue( value
, item
, col
->GetModelColumn() );
3643 cell
->SetValue( value
);
3644 wxRect
cell_rect( xpos
, current
* m_lineHeight
,
3645 col
->GetWidth(), m_lineHeight
);
3646 cell
->Activate( cell_rect
, model
, item
, col
->GetModelColumn() );
3651 wxWindow
*parent
= GetParent();
3652 wxDataViewEvent
le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED
, parent
->GetId());
3654 le
.SetEventObject(parent
);
3655 le
.SetModel(GetOwner()->GetModel());
3657 parent
->GetEventHandler()->ProcessEvent(le
);
3663 // The first click was on another item, so don't interpret this as
3664 // a double click, but as a simple click instead
3671 if (m_lineSelectSingleOnUp
!= (unsigned int)-1)
3673 // select single line
3674 SelectAllRows( false );
3675 SelectRow( m_lineSelectSingleOnUp
, true );
3678 //Process the event of user clicking the expander
3679 bool expander
= false;
3680 if ((m_root
) && (GetOwner()->GetExpanderColumn() == col
))
3682 wxDataViewTreeNode
* node
= GetTreeNodeByRow(current
);
3683 if( node
!=NULL
&& node
->HasChildren() )
3685 int indent
= node
->GetIndentLevel();
3686 indent
= GetOwner()->GetIndent()*indent
;
3687 wxRect
rect( xpos
+ indent
+ EXPANDER_MARGIN
, current
* m_lineHeight
+ EXPANDER_MARGIN
, m_lineHeight
-2*EXPANDER_MARGIN
,m_lineHeight
-2*EXPANDER_MARGIN
);
3688 if( rect
.Contains( x
, y
) )
3691 if( node
->IsOpen() )
3692 OnCollapsing(current
);
3694 OnExpanding( current
);
3697 if (node
&& !node
->HasChildren())
3700 //If the user click the expander, we do not do editing even if the column with expander are editable
3701 if (m_lastOnSame
&& !expander
&& !ignore_other_columns
)
3703 if ((col
== m_currentCol
) && (current
== m_currentRow
) &&
3704 (cell
->GetMode() & wxDATAVIEW_CELL_EDITABLE
) )
3706 m_renameTimer
->Start( 100, true );
3710 m_lastOnSame
= false;
3711 m_lineSelectSingleOnUp
= (unsigned int)-1;
3715 // This is necessary, because after a DnD operation in
3716 // from and to ourself, the up event is swallowed by the
3717 // DnD code. So on next non-up event (which means here and
3718 // now) m_lineSelectSingleOnUp should be reset.
3719 m_lineSelectSingleOnUp
= (unsigned int)-1;
3722 if (event
.RightDown())
3724 m_lineBeforeLastClicked
= m_lineLastClicked
;
3725 m_lineLastClicked
= current
;
3727 // If the item is already selected, do not update the selection.
3728 // Multi-selections should not be cleared if a selected item is clicked.
3729 if (!IsRowSelected(current
))
3731 SelectAllRows(false);
3732 ChangeCurrentRow(current
);
3733 SelectRow(m_currentRow
,true);
3734 SendSelectionChangedEvent(GetItemByRow( m_currentRow
) );
3738 model
->GetValue( value
, item
, col
->GetModelColumn() );
3739 wxWindow
*parent
= GetParent();
3740 wxDataViewEvent
le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU
, parent
->GetId());
3742 le
.SetEventObject(parent
);
3743 le
.SetModel(GetOwner()->GetModel());
3745 parent
->GetEventHandler()->ProcessEvent(le
);
3747 else if (event
.MiddleDown())
3750 if (event
.LeftDown() || forceClick
)
3754 m_lineBeforeLastClicked
= m_lineLastClicked
;
3755 m_lineLastClicked
= current
;
3757 unsigned int oldCurrentRow
= m_currentRow
;
3758 bool oldWasSelected
= IsRowSelected(m_currentRow
);
3760 bool cmdModifierDown
= event
.CmdDown();
3761 if ( IsSingleSel() || !(cmdModifierDown
|| event
.ShiftDown()) )
3763 if ( IsSingleSel() || !IsRowSelected(current
) )
3765 SelectAllRows( false );
3766 ChangeCurrentRow(current
);
3767 SelectRow(m_currentRow
,true);
3768 SendSelectionChangedEvent(GetItemByRow( m_currentRow
) );
3770 else // multi sel & current is highlighted & no mod keys
3772 m_lineSelectSingleOnUp
= current
;
3773 ChangeCurrentRow(current
); // change focus
3776 else // multi sel & either ctrl or shift is down
3778 if (cmdModifierDown
)
3780 ChangeCurrentRow(current
);
3781 ReverseRowSelection(m_currentRow
);
3782 SendSelectionChangedEvent(GetItemByRow(m_selection
[0]) );
3784 else if (event
.ShiftDown())
3786 ChangeCurrentRow(current
);
3788 unsigned int lineFrom
= oldCurrentRow
,
3791 if ( lineTo
< lineFrom
)
3794 lineFrom
= m_currentRow
;
3797 SelectRows(lineFrom
, lineTo
, true);
3798 SendSelectionChangedEvent(GetItemByRow(m_selection
[0]) );
3800 else // !ctrl, !shift
3802 // test in the enclosing if should make it impossible
3803 wxFAIL_MSG( _T("how did we get here?") );
3807 if (m_currentRow
!= oldCurrentRow
)
3808 RefreshRow( oldCurrentRow
);
3810 wxDataViewColumn
*oldCurrentCol
= m_currentCol
;
3812 // Update selection here...
3815 m_lastOnSame
= !forceClick
&& ((col
== oldCurrentCol
) &&
3816 (current
== oldCurrentRow
)) && oldWasSelected
;
3818 // Call LeftClick after everything else as under GTK+
3819 if (cell
->GetMode() & wxDATAVIEW_CELL_ACTIVATABLE
)
3821 // notify cell about right click
3823 model
->GetValue( value
, item
, col
->GetModelColumn() );
3824 cell
->SetValue( value
);
3825 wxRect
cell_rect( xpos
, current
* m_lineHeight
,
3826 col
->GetWidth(), m_lineHeight
);
3827 /* ignore ret */ cell
->LeftClick( event
.GetPosition(), cell_rect
, model
, item
, col
->GetModelColumn());
3832 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent
&event
)
3836 if (HasCurrentRow())
3842 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent
&event
)
3846 if (HasCurrentRow())
3852 wxDataViewItem
wxDataViewMainWindow::GetSelection() const
3854 if( m_selection
.GetCount() != 1 )
3855 return wxDataViewItem();
3857 return GetItemByRow( m_selection
.Item(0));
3860 //-----------------------------------------------------------------------------
3862 //-----------------------------------------------------------------------------
3863 WX_DEFINE_LIST(wxDataViewColumnList
)
3865 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl
, wxDataViewCtrlBase
)
3867 BEGIN_EVENT_TABLE(wxDataViewCtrl
, wxDataViewCtrlBase
)
3868 EVT_SIZE(wxDataViewCtrl::OnSize
)
3871 wxDataViewCtrl::~wxDataViewCtrl()
3874 GetModel()->RemoveNotifier( m_notifier
);
3876 wxDataViewColumnList::const_iterator iter
;
3877 for (iter
= m_cols
.begin(); iter
!=m_cols
.end(); iter
++)
3883 void wxDataViewCtrl::Init()
3888 bool wxDataViewCtrl::Create(wxWindow
*parent
, wxWindowID id
,
3889 const wxPoint
& pos
, const wxSize
& size
,
3890 long style
, const wxValidator
& validator
)
3892 if ( (style
& wxBORDER_MASK
) == 0)
3893 style
|= wxBORDER_SUNKEN
;
3895 if (!wxControl::Create( parent
, id
, pos
, size
,
3896 style
| wxScrolledWindowStyle
, validator
))
3899 SetInitialSize(size
);
3904 MacSetClipChildren( true ) ;
3907 m_clientArea
= new wxDataViewMainWindow( this, wxID_ANY
);
3909 if (HasFlag(wxDV_NO_HEADER
))
3910 m_headerArea
= NULL
;
3912 m_headerArea
= new wxDataViewHeaderWindow( this, wxID_ANY
);
3914 SetTargetWindow( m_clientArea
);
3916 wxBoxSizer
*sizer
= new wxBoxSizer( wxVERTICAL
);
3918 sizer
->Add( m_headerArea
, 0, wxGROW
);
3919 sizer
->Add( m_clientArea
, 1, wxGROW
);
3926 WXLRESULT
wxDataViewCtrl::MSWWindowProc(WXUINT nMsg
,
3930 WXLRESULT rc
= wxDataViewCtrlBase::MSWWindowProc(nMsg
, wParam
, lParam
);
3933 // we need to process arrows ourselves for scrolling
3934 if ( nMsg
== WM_GETDLGCODE
)
3936 rc
|= DLGC_WANTARROWS
;
3944 void wxDataViewCtrl::OnSize( wxSizeEvent
&WXUNUSED(event
) )
3946 // We need to override OnSize so that our scrolled
3947 // window a) does call Layout() to use sizers for
3948 // positioning the controls but b) does not query
3949 // the sizer for their size and use that for setting
3950 // the scrollable area as set that ourselves by
3951 // calling SetScrollbar() further down.
3958 bool wxDataViewCtrl::AssociateModel( wxDataViewModel
*model
)
3960 if (!wxDataViewCtrlBase::AssociateModel( model
))
3963 m_notifier
= new wxGenericDataViewModelNotifier( m_clientArea
);
3965 model
->AddNotifier( m_notifier
);
3967 m_clientArea
->DestroyTree();
3969 m_clientArea
->BuildTree(model
);
3971 m_clientArea
->UpdateDisplay();
3976 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn
*col
)
3978 if (!wxDataViewCtrlBase::AppendColumn(col
))
3981 m_cols
.Append( col
);
3986 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn
*col
)
3988 if (!wxDataViewCtrlBase::PrependColumn(col
))
3991 m_cols
.Insert( col
);
3996 void wxDataViewCtrl::OnColumnChange()
3999 m_headerArea
->UpdateDisplay();
4001 m_clientArea
->UpdateDisplay();
4004 void wxDataViewCtrl::DoSetExpanderColumn()
4006 m_clientArea
->UpdateDisplay();
4009 void wxDataViewCtrl::DoSetIndent()
4011 m_clientArea
->UpdateDisplay();
4014 unsigned int wxDataViewCtrl::GetColumnCount() const
4016 return m_cols
.GetCount();
4019 wxDataViewColumn
* wxDataViewCtrl::GetColumn( unsigned int pos
) const
4021 wxDataViewColumnList::const_iterator iter
;
4023 for (iter
= m_cols
.begin(); iter
!=m_cols
.end(); iter
++)
4028 if ((*iter
)->IsHidden())
4035 void wxDataViewCtrl::ColumnMoved( wxDataViewColumn
* col
, unsigned int new_pos
)
4037 if (new_pos
> m_cols
.GetCount()) return;
4038 m_cols
.DeleteObject( col
);
4039 m_cols
.Insert( new_pos
, col
);
4041 m_clientArea
->UpdateDisplay();
4044 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn
*column
)
4046 wxDataViewColumnList::compatibility_iterator ret
= m_cols
.Find( column
);
4057 bool wxDataViewCtrl::ClearColumns()
4064 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn
*column
) const
4066 int ret
= 0, dead
= 0;
4067 int len
= GetColumnCount();
4068 for (int i
=0; i
<len
; i
++)
4070 wxDataViewColumn
* col
= GetColumn(i
);
4071 if (col
->IsHidden())
4073 ret
+= col
->GetWidth();
4076 CalcScrolledPosition( ret
, dead
, &ret
, &dead
);
4083 wxDataViewColumn
*wxDataViewCtrl::GetSortingColumn() const
4088 //Selection code with wxDataViewItem as parameters
4089 wxDataViewItem
wxDataViewCtrl::GetSelection() const
4091 return m_clientArea
->GetSelection();
4094 int wxDataViewCtrl::GetSelections( wxDataViewItemArray
& sel
) const
4097 wxDataViewSelection selection
= m_clientArea
->GetSelections();
4098 int len
= selection
.GetCount();
4099 for( int i
= 0; i
< len
; i
++)
4101 unsigned int row
= selection
[i
];
4102 sel
.Add( m_clientArea
->GetItemByRow( row
) );
4107 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray
& sel
)
4109 wxDataViewSelection
selection(wxDataViewSelectionCmp
) ;
4110 int len
= sel
.GetCount();
4111 for( int i
= 0; i
< len
; i
++ )
4113 int row
= m_clientArea
->GetRowByItem( sel
[i
] );
4115 selection
.Add( static_cast<unsigned int>(row
) );
4117 m_clientArea
->SetSelections( selection
);
4120 void wxDataViewCtrl::Select( const wxDataViewItem
& item
)
4122 int row
= m_clientArea
->GetRowByItem( item
);
4125 //Unselect all rows before select another in the single select mode
4126 if (m_clientArea
->IsSingleSel())
4127 m_clientArea
->SelectAllRows(false);
4128 m_clientArea
->SelectRow(row
, true);
4132 void wxDataViewCtrl::Unselect( const wxDataViewItem
& item
)
4134 int row
= m_clientArea
->GetRowByItem( item
);
4136 m_clientArea
->SelectRow(row
, false);
4139 bool wxDataViewCtrl::IsSelected( const wxDataViewItem
& item
) const
4141 int row
= m_clientArea
->GetRowByItem( item
);
4144 return m_clientArea
->IsRowSelected(row
);
4149 //Selection code with row number as parameter
4150 int wxDataViewCtrl::GetSelections( wxArrayInt
& sel
) const
4153 wxDataViewSelection selection
= m_clientArea
->GetSelections();
4154 int len
= selection
.GetCount();
4155 for( int i
= 0; i
< len
; i
++)
4157 unsigned int row
= selection
[i
];
4163 void wxDataViewCtrl::SetSelections( const wxArrayInt
& sel
)
4165 wxDataViewSelection
selection(wxDataViewSelectionCmp
) ;
4166 int len
= sel
.GetCount();
4167 for( int i
= 0; i
< len
; i
++ )
4171 selection
.Add( static_cast<unsigned int>(row
) );
4173 m_clientArea
->SetSelections( selection
);
4176 void wxDataViewCtrl::Select( int row
)
4180 if (m_clientArea
->IsSingleSel())
4181 m_clientArea
->SelectAllRows(false);
4182 m_clientArea
->SelectRow( row
, true );
4186 void wxDataViewCtrl::Unselect( int row
)
4189 m_clientArea
->SelectRow(row
, false);
4192 bool wxDataViewCtrl::IsSelected( int row
) const
4195 return m_clientArea
->IsRowSelected(row
);
4199 void wxDataViewCtrl::SelectRange( int from
, int to
)
4202 for( int i
= from
; i
< to
; i
++ )
4204 m_clientArea
->Select(sel
);
4207 void wxDataViewCtrl::UnselectRange( int from
, int to
)
4209 wxDataViewSelection sel
= m_clientArea
->GetSelections();
4210 for( int i
= from
; i
< to
; i
++ )
4211 if( sel
.Index( i
) != wxNOT_FOUND
)
4213 m_clientArea
->SetSelections(sel
);
4216 void wxDataViewCtrl::SelectAll()
4218 m_clientArea
->SelectAllRows(true);
4221 void wxDataViewCtrl::UnselectAll()
4223 m_clientArea
->SelectAllRows(false);
4226 void wxDataViewCtrl::EnsureVisible( int row
, int column
)
4230 if( row
> (int) m_clientArea
->GetRowCount() )
4231 row
= m_clientArea
->GetRowCount();
4233 int first
= m_clientArea
->GetFirstVisibleRow();
4234 int last
= m_clientArea
->GetLastVisibleRow();
4236 m_clientArea
->ScrollTo( row
, column
);
4237 else if( row
> last
)
4238 m_clientArea
->ScrollTo( row
- last
+ first
, column
);
4240 m_clientArea
->ScrollTo( first
, column
);
4243 void wxDataViewCtrl::EnsureVisible( const wxDataViewItem
& item
, const wxDataViewColumn
* column
)
4245 int row
= m_clientArea
->GetRowByItem(item
);
4248 if( column
== NULL
)
4249 EnsureVisible(row
, -1);
4253 int len
= GetColumnCount();
4254 for( int i
= 0; i
< len
; i
++ )
4255 if( GetColumn(i
) == column
)
4260 EnsureVisible( row
, col
);
4266 void wxDataViewCtrl::HitTest( const wxPoint
& point
, wxDataViewItem
& item
, wxDataViewColumn
* &column
) const
4268 m_clientArea
->HitTest(point
, item
, column
);
4271 wxRect
wxDataViewCtrl::GetItemRect( const wxDataViewItem
& item
, const wxDataViewColumn
* column
) const
4273 return m_clientArea
->GetItemRect(item
, column
);
4276 wxDataViewItem
wxDataViewCtrl::GetItemByRow( unsigned int row
) const
4278 return m_clientArea
->GetItemByRow( row
);
4281 int wxDataViewCtrl::GetRowByItem( const wxDataViewItem
& item
) const
4283 return m_clientArea
->GetRowByItem( item
);
4286 void wxDataViewCtrl::Expand( const wxDataViewItem
& item
)
4288 int row
= m_clientArea
->GetRowByItem( item
);
4290 m_clientArea
->Expand(row
);
4293 void wxDataViewCtrl::Collapse( const wxDataViewItem
& item
)
4295 int row
= m_clientArea
->GetRowByItem( item
);
4297 m_clientArea
->Collapse(row
);
4301 // !wxUSE_GENERICDATAVIEWCTRL
4304 // wxUSE_DATAVIEWCTRL