]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
af1c5ee2752886466fa3eb4a77225b7a0fb6e2d7
[wxWidgets.git] / src / generic / datavgen.cpp
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
6 // Id: $Id$
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_DATAVIEWCTRL
19
20 #include "wx/dataview.h"
21
22 #ifdef wxUSE_GENERICDATAVIEWCTRL
23
24 #ifndef WX_PRECOMP
25 #ifdef __WXMSW__
26 #include "wx/msw/private.h"
27 #include "wx/msw/wrapwin.h"
28 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
29 #endif
30 #include "wx/sizer.h"
31 #include "wx/log.h"
32 #include "wx/dcclient.h"
33 #include "wx/timer.h"
34 #include "wx/settings.h"
35 #include "wx/msgdlg.h"
36 #include "wx/dcscreen.h"
37 #endif
38
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"
44 #include "wx/icon.h"
45 #include "wx/list.h"
46 #include "wx/listimpl.cpp"
47 #include "wx/imaglist.h"
48 #include "wx/headerctrl.h"
49
50 //-----------------------------------------------------------------------------
51 // classes
52 //-----------------------------------------------------------------------------
53
54 class wxDataViewCtrl;
55
56 static const int SCROLL_UNIT_X = 15;
57
58 // the cell padding on the left/right
59 static const int PADDING_RIGHTLEFT = 3;
60
61 // the expander space margin
62 static const int EXPANDER_MARGIN = 4;
63
64 #ifdef __WXMSW__
65 static const int EXPANDER_OFFSET = 4;
66 #else
67 static const int EXPANDER_OFFSET = 1;
68 #endif
69
70 //Below is the compare stuff
71 //For the generic implements, both the leaf nodes and the nodes are sorted for fast search when needed
72 static wxDataViewModel * g_model;
73 static int g_column = -2;
74 static bool g_asending = true;
75
76 //-----------------------------------------------------------------------------
77 // wxDataViewHeaderWindow
78 //-----------------------------------------------------------------------------
79
80 class wxDataViewHeaderWindow : public wxHeaderCtrl
81 {
82 public:
83 wxDataViewHeaderWindow(wxDataViewCtrl *parent)
84 : wxHeaderCtrl(parent)
85 {
86 }
87
88 wxDataViewCtrl *GetOwner() const
89 { return static_cast<wxDataViewCtrl *>(GetParent()); }
90
91 protected:
92 // implement/override wxHeaderCtrl functions by forwarding them to the main
93 // control
94 virtual wxHeaderColumnBase& GetColumn(unsigned int idx)
95 {
96 return *(GetOwner()->GetColumn(idx));
97 }
98
99 virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
100 {
101 wxDataViewCtrl * const owner = GetOwner();
102
103 int widthContents = owner->GetBestColumnWidth(idx);
104 owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents));
105 owner->OnColumnChange(idx);
106
107 return true;
108 }
109
110 private:
111 bool SendEvent(wxEventType type, unsigned int n)
112 {
113 wxDataViewCtrl * const owner = GetOwner();
114 wxDataViewEvent event(type, owner->GetId());
115
116 event.SetEventObject(owner);
117 event.SetColumn(n);
118 event.SetDataViewColumn(owner->GetColumn(n));
119 event.SetModel(owner->GetModel());
120
121 // for events created by wxDataViewHeaderWindow the
122 // row / value fields are not valid
123 return owner->GetEventHandler()->ProcessEvent(event);
124 }
125
126 void OnClick(wxHeaderCtrlEvent& event)
127 {
128 if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK,
129 event.GetColumn()) )
130 event.Skip();
131 }
132
133 void OnRClick(wxHeaderCtrlEvent& event)
134 {
135 if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
136 event.GetColumn()) )
137 event.Skip();
138 }
139
140 void OnBeginDrag(wxHeaderCtrlEvent& event)
141 {
142 if ( !GetColumn(event.GetColumn()).IsResizeable() )
143 event.Veto();
144 }
145
146 void OnDragging(wxHeaderCtrlEvent& event)
147 {
148 const wxHeaderColumnBase& col = GetColumn(event.GetColumn());
149
150 const int minWidth = col.GetMinWidth();
151 if ( event.GetWidth() < minWidth )
152 event.Veto();
153 }
154
155 void OnEndDrag(wxHeaderCtrlEvent& event)
156 {
157 if ( !event.IsCancelled() )
158 {
159 const unsigned col = event.GetColumn();
160 GetColumn(col).SetWidth(event.GetWidth());
161 GetOwner()->OnColumnChange(col);
162 }
163 }
164
165 DECLARE_EVENT_TABLE()
166 DECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow)
167 };
168
169 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
170 EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick)
171 EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
172
173 EVT_HEADER_BEGIN_DRAG(wxID_ANY, wxDataViewHeaderWindow::OnBeginDrag)
174 EVT_HEADER_DRAGGING(wxID_ANY, wxDataViewHeaderWindow::OnDragging)
175 EVT_HEADER_END_DRAG(wxID_ANY, wxDataViewHeaderWindow::OnEndDrag)
176 END_EVENT_TABLE()
177
178 //-----------------------------------------------------------------------------
179 // wxDataViewRenameTimer
180 //-----------------------------------------------------------------------------
181
182 class wxDataViewRenameTimer: public wxTimer
183 {
184 private:
185 wxDataViewMainWindow *m_owner;
186
187 public:
188 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
189 void Notify();
190 };
191
192 //-----------------------------------------------------------------------------
193 // wxDataViewTreeNode
194 //-----------------------------------------------------------------------------
195 class wxDataViewTreeNode;
196 WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
197 WX_DEFINE_ARRAY( void* , wxDataViewTreeLeaves);
198
199 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1, wxDataViewTreeNode ** node2);
200 int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2);
201
202 class wxDataViewTreeNode
203 {
204 public:
205 wxDataViewTreeNode( wxDataViewTreeNode * parent = NULL )
206 {
207 m_parent = parent;
208 if (!parent)
209 m_open = true;
210 else
211 m_open = false;
212 m_hasChildren = false;
213 m_subTreeCount = 0;
214 }
215
216 ~wxDataViewTreeNode()
217 {
218 }
219
220 wxDataViewTreeNode * GetParent() const { return m_parent; }
221 void SetParent( wxDataViewTreeNode * parent ) { m_parent = parent; }
222 wxDataViewTreeNodes & GetNodes() { return m_nodes; }
223 wxDataViewTreeLeaves & GetChildren() { return m_leaves; }
224
225 void AddNode( wxDataViewTreeNode * node )
226 {
227 m_leaves.Add( node->GetItem().GetID() );
228 if (g_column >= -1)
229 m_leaves.Sort( &wxGenericTreeModelItemCmp );
230 m_nodes.Add( node );
231 if (g_column >= -1)
232 m_nodes.Sort( &wxGenericTreeModelNodeCmp );
233 }
234 void AddLeaf( void * leaf )
235 {
236 m_leaves.Add( leaf );
237 if (g_column >= -1)
238 m_leaves.Sort( &wxGenericTreeModelItemCmp );
239 }
240
241 wxDataViewItem & GetItem() { return m_item; }
242 const wxDataViewItem & GetItem() const { return m_item; }
243 void SetItem( const wxDataViewItem & item ) { m_item = item; }
244
245 unsigned int GetChildrenNumber() const { return m_leaves.GetCount(); }
246 unsigned int GetNodeNumber() const { return m_nodes.GetCount(); }
247 int GetIndentLevel() const
248 {
249 int ret = 0;
250 const wxDataViewTreeNode * node = this;
251 while( node->GetParent()->GetParent() != NULL )
252 {
253 node = node->GetParent();
254 ret ++;
255 }
256 return ret;
257 }
258
259 bool IsOpen() const
260 {
261 return m_open;
262 }
263
264 void ToggleOpen()
265 {
266 int len = m_nodes.GetCount();
267 int sum = 0;
268 for ( int i = 0;i < len; i ++)
269 sum += m_nodes[i]->GetSubTreeCount();
270
271 sum += m_leaves.GetCount();
272 if (m_open)
273 {
274 ChangeSubTreeCount(-sum);
275 m_open = !m_open;
276 }
277 else
278 {
279 m_open = !m_open;
280 ChangeSubTreeCount(sum);
281 }
282 }
283 bool HasChildren() const { return m_hasChildren; }
284 void SetHasChildren( bool has ){ m_hasChildren = has; }
285
286 void SetSubTreeCount( int num ) { m_subTreeCount = num; }
287 int GetSubTreeCount() const { return m_subTreeCount; }
288 void ChangeSubTreeCount( int num )
289 {
290 if( !m_open )
291 return;
292 m_subTreeCount += num;
293 if( m_parent )
294 m_parent->ChangeSubTreeCount(num);
295 }
296
297 void Resort()
298 {
299 if (g_column >= -1)
300 {
301 m_nodes.Sort( &wxGenericTreeModelNodeCmp );
302 int len = m_nodes.GetCount();
303 for (int i = 0; i < len; i ++)
304 m_nodes[i]->Resort();
305 m_leaves.Sort( &wxGenericTreeModelItemCmp );
306 }
307 }
308
309 private:
310 wxDataViewTreeNode *m_parent;
311 wxDataViewTreeNodes m_nodes;
312 wxDataViewTreeLeaves m_leaves;
313 wxDataViewItem m_item;
314 bool m_open;
315 bool m_hasChildren;
316 int m_subTreeCount;
317 };
318
319 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1, wxDataViewTreeNode ** node2)
320 {
321 return g_model->Compare( (*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending );
322 }
323
324 int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2)
325 {
326 return g_model->Compare( *id1, *id2, g_column, g_asending );
327 }
328
329
330
331 //-----------------------------------------------------------------------------
332 // wxDataViewMainWindow
333 //-----------------------------------------------------------------------------
334
335 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
336 WXDLLIMPEXP_ADV);
337 WX_DECLARE_LIST(wxDataViewItem, ItemList);
338 WX_DEFINE_LIST(ItemList)
339
340 class wxDataViewMainWindow: public wxWindow
341 {
342 public:
343 wxDataViewMainWindow( wxDataViewCtrl *parent,
344 wxWindowID id,
345 const wxPoint &pos = wxDefaultPosition,
346 const wxSize &size = wxDefaultSize,
347 const wxString &name = wxT("wxdataviewctrlmainwindow") );
348 virtual ~wxDataViewMainWindow();
349
350 bool IsVirtualList() const { return m_root == NULL; }
351
352 // notifications from wxDataViewModel
353 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
354 bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item );
355 bool ItemChanged( const wxDataViewItem &item );
356 bool ValueChanged( const wxDataViewItem &item, unsigned int col );
357 bool Cleared();
358 void Resort()
359 {
360 if (!IsVirtualList())
361 {
362 SortPrepare();
363 m_root->Resort();
364 }
365 UpdateDisplay();
366 }
367
368 void SortPrepare()
369 {
370 g_model = GetOwner()->GetModel();
371 wxDataViewColumn* col = GetOwner()->GetSortingColumn();
372 if( !col )
373 {
374 if (g_model->HasDefaultCompare())
375 g_column = -1;
376 else
377 g_column = -2;
378
379 g_asending = true;
380 return;
381 }
382 g_column = col->GetModelColumn();
383 g_asending = col->IsSortOrderAscending();
384 }
385
386 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
387 wxDataViewCtrl *GetOwner() { return m_owner; }
388 const wxDataViewCtrl *GetOwner() const { return m_owner; }
389
390 void OnPaint( wxPaintEvent &event );
391 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
392 void OnChar( wxKeyEvent &event );
393 void OnMouse( wxMouseEvent &event );
394 void OnSetFocus( wxFocusEvent &event );
395 void OnKillFocus( wxFocusEvent &event );
396
397 void UpdateDisplay();
398 void RecalculateDisplay();
399 void OnInternalIdle();
400
401 void OnRenameTimer();
402
403 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
404 void ScrollTo( int rows, int column );
405
406 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
407 void ChangeCurrentRow( unsigned int row );
408
409 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
410 bool IsEmpty() { return GetRowCount() == 0; }
411
412 int GetCountPerPage() const;
413 int GetEndOfLastCol() const;
414 unsigned int GetFirstVisibleRow() const;
415 //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
416 unsigned int GetLastVisibleRow();
417 unsigned int GetRowCount();
418
419 wxDataViewItem GetSelection() const;
420 wxDataViewSelection GetSelections(){ return m_selection; }
421 void SetSelections( const wxDataViewSelection & sel ) { m_selection = sel; UpdateDisplay(); }
422 void Select( const wxArrayInt& aSelections );
423 void SelectAllRows( bool on );
424 void SelectRow( unsigned int row, bool on );
425 void SelectRows( unsigned int from, unsigned int to, bool on );
426 void ReverseRowSelection( unsigned int row );
427 bool IsRowSelected( unsigned int row );
428 void SendSelectionChangedEvent( const wxDataViewItem& item);
429
430 void RefreshRow( unsigned int row );
431 void RefreshRows( unsigned int from, unsigned int to );
432 void RefreshRowsAfter( unsigned int firstRow );
433
434 // returns the colour to be used for drawing the rules
435 wxColour GetRuleColour() const
436 {
437 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
438 }
439
440 wxRect GetLineRect( unsigned int row ) const;
441
442 int GetLineStart( unsigned int row ) const; // row * m_lineHeight in fixed mode
443 int GetLineHeight( unsigned int row ) const; // m_lineHeight in fixed mode
444 int GetLineAt( unsigned int y ) const; // y / m_lineHeight in fixed mode
445
446 //Some useful functions for row and item mapping
447 wxDataViewItem GetItemByRow( unsigned int row ) const;
448 int GetRowByItem( const wxDataViewItem & item ) const;
449
450 //Methods for building the mapping tree
451 void BuildTree( wxDataViewModel * model );
452 void DestroyTree();
453 void HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column );
454 wxRect GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column );
455
456 void Expand( unsigned int row ) { OnExpanding( row ); }
457 void Collapse( unsigned int row ) { OnCollapsing( row ); }
458 private:
459 wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row ) const;
460 //We did not need this temporarily
461 //wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
462
463 int RecalculateCount();
464
465 wxDataViewEvent SendExpanderEvent( wxEventType type, const wxDataViewItem & item );
466 void OnExpanding( unsigned int row );
467 void OnCollapsing( unsigned int row );
468
469 wxDataViewTreeNode * FindNode( const wxDataViewItem & item );
470
471 private:
472 wxDataViewCtrl *m_owner;
473 int m_lineHeight;
474 bool m_dirty;
475
476 wxDataViewColumn *m_currentCol;
477 unsigned int m_currentRow;
478 wxDataViewSelection m_selection;
479
480 wxDataViewRenameTimer *m_renameTimer;
481 bool m_lastOnSame;
482
483 bool m_hasFocus;
484
485 int m_dragCount;
486 wxPoint m_dragStart;
487
488 // for double click logic
489 unsigned int m_lineLastClicked,
490 m_lineBeforeLastClicked,
491 m_lineSelectSingleOnUp;
492
493 // the pen used to draw horiz/vertical rules
494 wxPen m_penRule;
495
496 // the pen used to draw the expander and the lines
497 wxPen m_penExpander;
498
499 //This is the tree structure of the model
500 wxDataViewTreeNode * m_root;
501 int m_count;
502 //This is the tree node under the cursor
503 wxDataViewTreeNode * m_underMouse;
504 private:
505 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
506 DECLARE_EVENT_TABLE()
507 };
508
509 // ---------------------------------------------------------
510 // wxGenericDataViewModelNotifier
511 // ---------------------------------------------------------
512
513 class wxGenericDataViewModelNotifier: public wxDataViewModelNotifier
514 {
515 public:
516 wxGenericDataViewModelNotifier( wxDataViewMainWindow *mainWindow )
517 { m_mainWindow = mainWindow; }
518
519 virtual bool ItemAdded( const wxDataViewItem & parent, const wxDataViewItem & item )
520 { return m_mainWindow->ItemAdded( parent , item ); }
521 virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
522 { return m_mainWindow->ItemDeleted( parent, item ); }
523 virtual bool ItemChanged( const wxDataViewItem & item )
524 { return m_mainWindow->ItemChanged(item); }
525 virtual bool ValueChanged( const wxDataViewItem & item , unsigned int col )
526 { return m_mainWindow->ValueChanged( item, col ); }
527 virtual bool Cleared()
528 { return m_mainWindow->Cleared(); }
529 virtual void Resort()
530 { m_mainWindow->Resort(); }
531
532 wxDataViewMainWindow *m_mainWindow;
533 };
534
535 // ---------------------------------------------------------
536 // wxDataViewRenderer
537 // ---------------------------------------------------------
538
539 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
540
541 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
542 wxDataViewCellMode mode,
543 int align) :
544 wxDataViewRendererBase( varianttype, mode, align )
545 {
546 m_dc = NULL;
547 m_align = align;
548 m_mode = mode;
549 m_wantsAttr = false;
550 m_hasAttr = false;
551 }
552
553 wxDataViewRenderer::~wxDataViewRenderer()
554 {
555 if (m_dc)
556 delete m_dc;
557 }
558
559 wxDC *wxDataViewRenderer::GetDC()
560 {
561 if (m_dc == NULL)
562 {
563 if (GetOwner() == NULL)
564 return NULL;
565 if (GetOwner()->GetOwner() == NULL)
566 return NULL;
567 m_dc = new wxClientDC( GetOwner()->GetOwner() );
568 }
569
570 return m_dc;
571 }
572
573 void wxDataViewRenderer::SetAlignment( int align )
574 {
575 m_align=align;
576 }
577
578 int wxDataViewRenderer::GetAlignment() const
579 {
580 return m_align;
581 }
582
583 int wxDataViewRenderer::CalculateAlignment() const
584 {
585 if (m_align == wxDVR_DEFAULT_ALIGNMENT)
586 {
587 if (GetOwner() == NULL)
588 return wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL;
589
590 return GetOwner()->GetAlignment() | wxALIGN_CENTRE_VERTICAL;
591 }
592
593 return m_align;
594 }
595
596 // ---------------------------------------------------------
597 // wxDataViewCustomRenderer
598 // ---------------------------------------------------------
599
600 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
601
602 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
603 wxDataViewCellMode mode, int align ) :
604 wxDataViewRenderer( varianttype, mode, align )
605 {
606 }
607
608 void wxDataViewCustomRenderer::RenderText( const wxString &text, int xoffset, wxRect cell, wxDC *dc, int state )
609 {
610 wxDataViewCtrl *view = GetOwner()->GetOwner();
611 wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ?
612 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) :
613 view->GetForegroundColour();
614 dc->SetTextForeground(col);
615 dc->DrawText( text, cell.x + xoffset, cell.y + ((cell.height - dc->GetCharHeight()) / 2));
616 }
617
618 // ---------------------------------------------------------
619 // wxDataViewTextRenderer
620 // ---------------------------------------------------------
621
622 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
623
624 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
625 wxDataViewCellMode mode, int align ) :
626 wxDataViewCustomRenderer( varianttype, mode, align )
627 {
628 }
629
630 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
631 {
632 m_text = value.GetString();
633
634 return true;
635 }
636
637 bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
638 {
639 return false;
640 }
641
642 bool wxDataViewTextRenderer::HasEditorCtrl()
643 {
644 return true;
645 }
646
647 wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
648 wxRect labelRect, const wxVariant &value )
649 {
650 return new wxTextCtrl( parent, wxID_ANY, value,
651 wxPoint(labelRect.x,labelRect.y),
652 wxSize(labelRect.width,labelRect.height) );
653 }
654
655 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
656 {
657 wxTextCtrl *text = (wxTextCtrl*) editor;
658 value = text->GetValue();
659 return true;
660 }
661
662 bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state )
663 {
664 RenderText( m_text, 0, cell, dc, state );
665 return true;
666 }
667
668 wxSize wxDataViewTextRenderer::GetSize() const
669 {
670 const wxDataViewCtrl *view = GetView();
671 if (!m_text.empty())
672 {
673 int x,y;
674 view->GetTextExtent( m_text, &x, &y );
675 return wxSize( x, y );
676 }
677 return wxSize(80,20);
678 }
679
680 // ---------------------------------------------------------
681 // wxDataViewTextRendererAttr
682 // ---------------------------------------------------------
683
684 IMPLEMENT_CLASS(wxDataViewTextRendererAttr, wxDataViewTextRenderer)
685
686 wxDataViewTextRendererAttr::wxDataViewTextRendererAttr( const wxString &varianttype,
687 wxDataViewCellMode mode, int align ) :
688 wxDataViewTextRenderer( varianttype, mode, align )
689 {
690 m_wantsAttr = true;
691 }
692
693 bool wxDataViewTextRendererAttr::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
694 {
695 wxFont font;
696 wxColour colour;
697
698 if (m_hasAttr)
699 {
700 if (m_attr.HasColour())
701 {
702 colour = dc->GetTextForeground();
703 dc->SetTextForeground( m_attr.GetColour() );
704 }
705
706 if (m_attr.GetBold() || m_attr.GetItalic())
707 {
708 font = dc->GetFont();
709 wxFont myfont = font;
710 if (m_attr.GetBold())
711 myfont.SetWeight( wxFONTWEIGHT_BOLD );
712 if (m_attr.GetItalic())
713 myfont.SetStyle( wxFONTSTYLE_ITALIC );
714 dc->SetFont( myfont );
715 }
716 }
717
718 dc->DrawText( m_text, cell.x, cell.y + ((cell.height - dc->GetCharHeight()) / 2));
719
720 // restore dc
721 if (m_hasAttr)
722 {
723 if (m_attr.HasColour())
724 dc->SetTextForeground( colour );
725
726 if (m_attr.GetBold() || m_attr.GetItalic())
727 dc->SetFont( font );
728 }
729
730 return true;
731 }
732
733
734 // ---------------------------------------------------------
735 // wxDataViewBitmapRenderer
736 // ---------------------------------------------------------
737
738 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
739
740 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
741 wxDataViewCellMode mode, int align ) :
742 wxDataViewCustomRenderer( varianttype, mode, align )
743 {
744 }
745
746 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
747 {
748 if (value.GetType() == wxT("wxBitmap"))
749 m_bitmap << value;
750 if (value.GetType() == wxT("wxIcon"))
751 m_icon << value;
752
753 return true;
754 }
755
756 bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
757 {
758 return false;
759 }
760
761 bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
762 {
763 if (m_bitmap.Ok())
764 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
765 else if (m_icon.Ok())
766 dc->DrawIcon( m_icon, cell.x, cell.y );
767
768 return true;
769 }
770
771 wxSize wxDataViewBitmapRenderer::GetSize() const
772 {
773 if (m_bitmap.Ok())
774 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
775 else if (m_icon.Ok())
776 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
777
778 return wxSize(16,16);
779 }
780
781 // ---------------------------------------------------------
782 // wxDataViewToggleRenderer
783 // ---------------------------------------------------------
784
785 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
786
787 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
788 wxDataViewCellMode mode, int align ) :
789 wxDataViewCustomRenderer( varianttype, mode, align )
790 {
791 m_toggle = false;
792 }
793
794 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
795 {
796 m_toggle = value.GetBool();
797
798 return true;
799 }
800
801 bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
802 {
803 return false;
804 }
805
806 bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
807 {
808 // User wxRenderer here
809
810 wxRect rect;
811 rect.x = cell.x + cell.width/2 - 10;
812 rect.width = 20;
813 rect.y = cell.y + cell.height/2 - 10;
814 rect.height = 20;
815
816 int flags = 0;
817 if (m_toggle)
818 flags |= wxCONTROL_CHECKED;
819 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
820 flags |= wxCONTROL_DISABLED;
821
822 wxRendererNative::Get().DrawCheckBox(
823 GetOwner()->GetOwner(),
824 *dc,
825 rect,
826 flags );
827
828 return true;
829 }
830
831 bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
832 wxDataViewModel *model,
833 const wxDataViewItem & item, unsigned int col)
834 {
835 bool value = !m_toggle;
836 wxVariant variant = value;
837 model->SetValue( variant, item, col);
838 model->ValueChanged( item, col );
839 return true;
840 }
841
842 wxSize wxDataViewToggleRenderer::GetSize() const
843 {
844 return wxSize(20,20);
845 }
846
847 // ---------------------------------------------------------
848 // wxDataViewProgressRenderer
849 // ---------------------------------------------------------
850
851 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
852
853 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
854 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
855 wxDataViewCustomRenderer( varianttype, mode, align )
856 {
857 m_label = label;
858 m_value = 0;
859 }
860
861 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
862 {
863 }
864
865 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
866 {
867 m_value = (long) value;
868
869 if (m_value < 0) m_value = 0;
870 if (m_value > 100) m_value = 100;
871
872 return true;
873 }
874
875 bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
876 {
877 value = (long) m_value;
878 return true;
879 }
880
881 bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
882 {
883 double pct = (double)m_value / 100.0;
884 wxRect bar = cell;
885 bar.width = (int)(cell.width * pct);
886 dc->SetPen( *wxTRANSPARENT_PEN );
887 dc->SetBrush( *wxBLUE_BRUSH );
888 dc->DrawRectangle( bar );
889
890 dc->SetBrush( *wxTRANSPARENT_BRUSH );
891 dc->SetPen( *wxBLACK_PEN );
892 dc->DrawRectangle( cell );
893
894 return true;
895 }
896
897 wxSize wxDataViewProgressRenderer::GetSize() const
898 {
899 return wxSize(40,12);
900 }
901
902 // ---------------------------------------------------------
903 // wxDataViewDateRenderer
904 // ---------------------------------------------------------
905
906 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
907
908 #if wxUSE_DATE_RENDERER_POPUP
909
910 class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
911 {
912 public:
913 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
914 wxDataViewModel *model, const wxDataViewItem & item, unsigned int col) :
915 wxPopupTransientWindow( parent, wxBORDER_SIMPLE ),
916 m_item( item )
917 {
918 m_model = model;
919 m_col = col;
920 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
921 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
922 sizer->Add( m_cal, 1, wxGROW );
923 SetSizer( sizer );
924 sizer->Fit( this );
925 }
926
927 void OnCalendar( wxCalendarEvent &event );
928
929 wxCalendarCtrl *m_cal;
930 wxDataViewModel *m_model;
931 unsigned int m_col;
932 const wxDataViewItem & m_item;
933
934 protected:
935 virtual void OnDismiss()
936 {
937 }
938
939 private:
940 DECLARE_EVENT_TABLE()
941 };
942
943 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
944 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
945 END_EVENT_TABLE()
946
947 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
948 {
949 wxDateTime date = event.GetDate();
950 wxVariant value = date;
951 m_model->SetValue( value, m_item, m_col );
952 m_model->ValueChanged( m_item, m_col );
953 DismissAndNotify();
954 }
955
956 #endif // wxUSE_DATE_RENDERER_POPUP
957
958 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
959
960 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
961 wxDataViewCellMode mode, int align ) :
962 wxDataViewCustomRenderer( varianttype, mode, align )
963 {
964 }
965
966 bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
967 {
968 m_date = value.GetDateTime();
969
970 return true;
971 }
972
973 bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
974 {
975 value = m_date;
976 return true;
977 }
978
979 bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state )
980 {
981 wxString tmp = m_date.FormatDate();
982 RenderText( tmp, 0, cell, dc, state );
983 return true;
984 }
985
986 wxSize wxDataViewDateRenderer::GetSize() const
987 {
988 const wxDataViewCtrl* view = GetView();
989 wxString tmp = m_date.FormatDate();
990 wxCoord x,y,d;
991 view->GetTextExtent( tmp, &x, &y, &d );
992 return wxSize(x,y+d);
993 }
994
995 bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewModel *model,
996 const wxDataViewItem & item, unsigned int col )
997 {
998 wxVariant variant;
999 model->GetValue( variant, item, col );
1000 wxDateTime value = variant.GetDateTime();
1001
1002 #if wxUSE_DATE_RENDERER_POPUP
1003 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
1004 GetOwner()->GetOwner()->GetParent(), &value, model, item, col);
1005 wxPoint pos = wxGetMousePosition();
1006 popup->Move( pos );
1007 popup->Layout();
1008 popup->Popup( popup->m_cal );
1009 #else // !wxUSE_DATE_RENDERER_POPUP
1010 wxMessageBox(value.Format());
1011 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
1012 return true;
1013 }
1014
1015 // ---------------------------------------------------------
1016 // wxDataViewIconTextRenderer
1017 // ---------------------------------------------------------
1018
1019 IMPLEMENT_CLASS(wxDataViewIconTextRenderer, wxDataViewCustomRenderer)
1020
1021 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(
1022 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
1023 wxDataViewCustomRenderer( varianttype, mode, align )
1024 {
1025 SetMode(mode);
1026 SetAlignment(align);
1027 }
1028
1029 wxDataViewIconTextRenderer::~wxDataViewIconTextRenderer()
1030 {
1031 }
1032
1033 bool wxDataViewIconTextRenderer::SetValue( const wxVariant &value )
1034 {
1035 m_value << value;
1036 return true;
1037 }
1038
1039 bool wxDataViewIconTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
1040 {
1041 return false;
1042 }
1043
1044 bool wxDataViewIconTextRenderer::Render( wxRect cell, wxDC *dc, int state )
1045 {
1046 int xoffset = 0;
1047 const wxIcon &icon = m_value.GetIcon();
1048 if (icon.IsOk())
1049 {
1050 dc->DrawIcon( icon, cell.x, cell.y + ((cell.height - icon.GetHeight()) / 2));
1051 xoffset = icon.GetWidth()+4;
1052 }
1053
1054 RenderText( m_value.GetText(), xoffset, cell, dc, state );
1055
1056 return true;
1057 }
1058
1059 wxSize wxDataViewIconTextRenderer::GetSize() const
1060 {
1061 const wxDataViewCtrl *view = GetView();
1062 if (!m_value.GetText().empty())
1063 {
1064 int x,y;
1065 view->GetTextExtent( m_value.GetText(), &x, &y );
1066
1067 if (m_value.GetIcon().IsOk())
1068 x += m_value.GetIcon().GetWidth() + 4;
1069 return wxSize( x, y );
1070 }
1071 return wxSize(80,20);
1072 }
1073
1074 wxControl *
1075 wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow * WXUNUSED(parent),
1076 wxRect WXUNUSED(labelRect),
1077 const wxVariant& WXUNUSED(value))
1078 {
1079 return NULL;
1080 }
1081
1082 bool
1083 wxDataViewIconTextRenderer::GetValueFromEditorCtrl(wxControl* WXUNUSED(editor),
1084 wxVariant& WXUNUSED(value))
1085 {
1086 return false;
1087 }
1088
1089 //-----------------------------------------------------------------------------
1090 // wxDataViewRenameTimer
1091 //-----------------------------------------------------------------------------
1092
1093 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1094 {
1095 m_owner = owner;
1096 }
1097
1098 void wxDataViewRenameTimer::Notify()
1099 {
1100 m_owner->OnRenameTimer();
1101 }
1102
1103 //-----------------------------------------------------------------------------
1104 // wxDataViewMainWindow
1105 //-----------------------------------------------------------------------------
1106
1107 //The tree building helper, declared firstly
1108 static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node);
1109
1110 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
1111 {
1112 if (row1 > row2) return 1;
1113 if (row1 == row2) return 0;
1114 return -1;
1115 }
1116
1117
1118 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1119
1120 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1121 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1122 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1123 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
1124 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1125 EVT_CHAR (wxDataViewMainWindow::OnChar)
1126 END_EVENT_TABLE()
1127
1128 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1129 const wxPoint &pos, const wxSize &size, const wxString &name ) :
1130 wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE, name ),
1131 m_selection( wxDataViewSelectionCmp )
1132
1133 {
1134 SetOwner( parent );
1135
1136 m_lastOnSame = false;
1137 m_renameTimer = new wxDataViewRenameTimer( this );
1138
1139 // TODO: user better initial values/nothing selected
1140 m_currentCol = NULL;
1141 m_currentRow = 0;
1142
1143 m_lineHeight = wxMax( 17, GetCharHeight() + 2 ); // 17 = mini icon height + 1
1144
1145 m_dragCount = 0;
1146 m_dragStart = wxPoint(0,0);
1147 m_lineLastClicked = (unsigned int) -1;
1148 m_lineBeforeLastClicked = (unsigned int) -1;
1149 m_lineSelectSingleOnUp = (unsigned int) -1;
1150
1151 m_hasFocus = false;
1152
1153 SetBackgroundColour( *wxWHITE );
1154
1155 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
1156
1157 m_penRule = wxPen(GetRuleColour());
1158
1159 //Here I compose a pen can draw black lines, maybe there are something system colour to use
1160 m_penExpander = wxPen(wxColour(0,0,0));
1161 //Some new added code to deal with the tree structure
1162 m_root = new wxDataViewTreeNode( NULL );
1163 m_root->SetHasChildren(true);
1164
1165 //Make m_count = -1 will cause the class recaculate the real displaying number of rows.
1166 m_count = -1;
1167 m_underMouse = NULL;
1168 UpdateDisplay();
1169 }
1170
1171 wxDataViewMainWindow::~wxDataViewMainWindow()
1172 {
1173 DestroyTree();
1174 delete m_renameTimer;
1175 }
1176
1177 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1178 {
1179 wxDataViewModel *model = GetOwner()->GetModel();
1180 wxAutoBufferedPaintDC dc( this );
1181
1182 #ifdef __WXMSW__
1183 dc.SetPen( *wxTRANSPARENT_PEN );
1184 dc.SetBrush( wxBrush( GetBackgroundColour()) );
1185 dc.SetBrush( *wxWHITE_BRUSH );
1186 wxSize size( GetClientSize() );
1187 dc.DrawRectangle( 0,0,size.x,size.y );
1188 #endif
1189
1190 // prepare the DC
1191 GetOwner()->PrepareDC( dc );
1192 dc.SetFont( GetFont() );
1193
1194 wxRect update = GetUpdateRegion().GetBox();
1195 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1196
1197 // compute which items needs to be redrawn
1198 unsigned int item_start = GetLineAt( wxMax(0,update.y) );
1199 unsigned int item_count =
1200 wxMin( (int)( GetLineAt( wxMax(0,update.y+update.height) ) - item_start + 1),
1201 (int)(GetRowCount( ) - item_start));
1202 unsigned int item_last = item_start + item_count;
1203
1204 // compute which columns needs to be redrawn
1205 unsigned int cols = GetOwner()->GetColumnCount();
1206 unsigned int col_start = 0;
1207 unsigned int x_start;
1208 for (x_start = 0; col_start < cols; col_start++)
1209 {
1210 wxDataViewColumn *col = GetOwner()->GetColumn(col_start);
1211 if (col->IsHidden())
1212 continue; // skip it!
1213
1214 unsigned int w = col->GetWidth();
1215 if (x_start+w >= (unsigned int)update.x)
1216 break;
1217
1218 x_start += w;
1219 }
1220
1221 unsigned int col_last = col_start;
1222 unsigned int x_last = x_start;
1223 for (; col_last < cols; col_last++)
1224 {
1225 wxDataViewColumn *col = GetOwner()->GetColumn(col_last);
1226 if (col->IsHidden())
1227 continue; // skip it!
1228
1229 if (x_last > (unsigned int)update.GetRight())
1230 break;
1231
1232 x_last += col->GetWidth();
1233 }
1234
1235 // Draw horizontal rules if required
1236 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1237 {
1238 dc.SetPen(m_penRule);
1239 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1240
1241 for (unsigned int i = item_start; i <= item_last+1; i++)
1242 {
1243 int y = GetLineStart( i );
1244 dc.DrawLine(x_start, y, x_last, y);
1245 }
1246 }
1247
1248 // Draw vertical rules if required
1249 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1250 {
1251 dc.SetPen(m_penRule);
1252 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1253
1254 int x = x_start;
1255 for (unsigned int i = col_start; i < col_last; i++)
1256 {
1257 wxDataViewColumn *col = GetOwner()->GetColumn(i);
1258 if (col->IsHidden())
1259 continue; // skip it
1260
1261 dc.DrawLine(x, GetLineStart( item_start ),
1262 x, GetLineStart( item_last ) );
1263
1264 x += col->GetWidth();
1265 }
1266
1267 // Draw last vertical rule
1268 dc.DrawLine(x, GetLineStart( item_start ),
1269 x, GetLineStart( item_last ) );
1270 }
1271
1272 // redraw the background for the items which are selected/current
1273 for (unsigned int item = item_start; item < item_last; item++)
1274 {
1275 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1276 if (selected || item == m_currentRow)
1277 {
1278 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
1279 if (item == m_currentRow)
1280 flags |= wxCONTROL_CURRENT;
1281 if (m_hasFocus)
1282 flags |= wxCONTROL_FOCUSED;
1283
1284 wxRect rect( x_start, GetLineStart( item ), x_last, GetLineHeight( item ) );
1285 wxRendererNative::Get().DrawItemSelectionRect
1286 (
1287 this,
1288 dc,
1289 rect,
1290 flags
1291 );
1292 }
1293 }
1294
1295 wxDataViewColumn *expander = GetOwner()->GetExpanderColumn();
1296 if (!expander)
1297 {
1298 // TODO: last column for RTL support
1299 expander = GetOwner()->GetColumn( 0 );
1300 GetOwner()->SetExpanderColumn(expander);
1301 }
1302
1303 // redraw all cells for all rows which must be repainted and all columns
1304 wxRect cell_rect;
1305 cell_rect.x = x_start;
1306 for (unsigned int i = col_start; i < col_last; i++)
1307 {
1308
1309 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1310 wxDataViewRenderer *cell = col->GetRenderer();
1311 cell_rect.width = col->GetWidth();
1312
1313 if (col->IsHidden())
1314 continue; // skip it!
1315
1316 for (unsigned int item = item_start; item < item_last; item++)
1317 {
1318 // get the cell value and set it into the renderer
1319 wxVariant value;
1320 wxDataViewTreeNode *node = NULL;
1321 wxDataViewItem dataitem;
1322
1323 if (!IsVirtualList())
1324 {
1325 node = GetTreeNodeByRow(item);
1326 if( node == NULL )
1327 continue;
1328
1329 dataitem = node->GetItem();
1330
1331 if ((i > 0) && model->IsContainer(dataitem) && !model->HasContainerColumns(dataitem))
1332 continue;
1333 }
1334 else
1335 {
1336 dataitem = wxDataViewItem( wxUIntToPtr(item) );
1337 }
1338
1339 model->GetValue( value, dataitem, col->GetModelColumn());
1340 cell->SetValue( value );
1341
1342 if (cell->GetWantsAttr())
1343 {
1344 wxDataViewItemAttr attr;
1345 bool ret = model->GetAttr( dataitem, col->GetModelColumn(), attr );
1346 if (ret)
1347 cell->SetAttr( attr );
1348 cell->SetHasAttr( ret );
1349 }
1350
1351 // update cell_rect
1352 cell_rect.y = GetLineStart( item );
1353 cell_rect.height = GetLineHeight( item ); // -1 is for the horizontal rules (?)
1354
1355 //Draw the expander here.
1356 int indent = 0;
1357 if ((!IsVirtualList()) && (col == expander))
1358 {
1359 indent = node->GetIndentLevel();
1360
1361 //Calculate the indent first
1362 indent = cell_rect.x + GetOwner()->GetIndent() * indent;
1363
1364 int expander_width = m_lineHeight - 2*EXPANDER_MARGIN;
1365 // change the cell_rect.x to the appropriate pos
1366 int expander_x = indent + EXPANDER_MARGIN;
1367 int expander_y = cell_rect.y + EXPANDER_MARGIN + (GetLineHeight(item) / 2) - (expander_width/2) - EXPANDER_OFFSET;
1368 indent = indent + m_lineHeight; //try to use the m_lineHeight as the expander space
1369 dc.SetPen( m_penExpander );
1370 dc.SetBrush( wxNullBrush );
1371 if( node->HasChildren() )
1372 {
1373 wxRect rect( expander_x , expander_y, expander_width, expander_width);
1374 int flag = 0;
1375 if (m_underMouse == node)
1376 {
1377 flag |= wxCONTROL_CURRENT;
1378 }
1379 if( node->IsOpen() )
1380 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, flag|wxCONTROL_EXPANDED );
1381 else
1382 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, flag);
1383 }
1384 //force the expander column to left-center align
1385 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
1386 }
1387 if (node && !node->HasChildren())
1388 {
1389 // Yes, if the node does not have any child, it must be a leaf which
1390 // mean that it is a temporarily created by GetTreeNodeByRow
1391 wxDELETE(node);
1392 }
1393
1394 // cannot be bigger than allocated space
1395 wxSize size = cell->GetSize();
1396 // Because of the tree structure indent, here we should minus the width of the cell for drawing
1397 size.x = wxMin( size.x + 2*PADDING_RIGHTLEFT, cell_rect.width - indent );
1398 // size.y = wxMin( size.y, cell_rect.height );
1399 size.y = cell_rect.height;
1400
1401 wxRect item_rect(cell_rect.GetTopLeft(), size);
1402 int align = cell->CalculateAlignment();
1403
1404 // horizontal alignment:
1405 item_rect.x = cell_rect.x;
1406 if (align & wxALIGN_CENTER_HORIZONTAL)
1407 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1408 else if (align & wxALIGN_RIGHT)
1409 item_rect.x = cell_rect.x + cell_rect.width - size.x;
1410 //else: wxALIGN_LEFT is the default
1411
1412 // vertical alignment:
1413 item_rect.y = cell_rect.y;
1414 if (align & wxALIGN_CENTER_VERTICAL)
1415 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1416 else if (align & wxALIGN_BOTTOM)
1417 item_rect.y = cell_rect.y + cell_rect.height - size.y;
1418 //else: wxALIGN_TOP is the default
1419
1420 // add padding
1421 item_rect.x += PADDING_RIGHTLEFT;
1422 item_rect.width = size.x - 2 * PADDING_RIGHTLEFT;
1423
1424 //Here we add the tree indent
1425 item_rect.x += indent;
1426
1427 int state = 0;
1428 if (m_hasFocus && (m_selection.Index(item) != wxNOT_FOUND))
1429 state |= wxDATAVIEW_CELL_SELECTED;
1430
1431 // TODO: it would be much more efficient to create a clipping
1432 // region for the entire column being rendered (in the OnPaint
1433 // of wxDataViewMainWindow) instead of a single clip region for
1434 // each cell. However it would mean that each renderer should
1435 // respect the given wxRect's top & bottom coords, eventually
1436 // violating only the left & right coords - however the user can
1437 // make its own renderer and thus we cannot be sure of that.
1438 dc.SetClippingRegion( item_rect );
1439 cell->Render( item_rect, &dc, state );
1440 dc.DestroyClippingRegion();
1441 }
1442
1443 cell_rect.x += cell_rect.width;
1444 }
1445 }
1446
1447 void wxDataViewMainWindow::OnRenameTimer()
1448 {
1449 // We have to call this here because changes may just have
1450 // been made and no screen update taken place.
1451 if ( m_dirty )
1452 wxSafeYield();
1453
1454 int xpos = 0;
1455 unsigned int cols = GetOwner()->GetColumnCount();
1456 unsigned int i;
1457 for (i = 0; i < cols; i++)
1458 {
1459 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1460 if (c->IsHidden())
1461 continue; // skip it!
1462
1463 if (c == m_currentCol)
1464 break;
1465 xpos += c->GetWidth();
1466 }
1467 wxRect labelRect( xpos,
1468 GetLineStart( m_currentRow ),
1469 m_currentCol->GetWidth(),
1470 GetLineHeight( m_currentRow ) );
1471
1472 GetOwner()->CalcScrolledPosition( labelRect.x, labelRect.y,
1473 &labelRect.x, &labelRect.y);
1474
1475 wxDataViewItem item = GetItemByRow( m_currentRow );
1476 m_currentCol->GetRenderer()->StartEditing( item, labelRect );
1477
1478 }
1479
1480 //------------------------------------------------------------------
1481 // Helper class for do operation on the tree node
1482 //------------------------------------------------------------------
1483 class DoJob
1484 {
1485 public:
1486 DoJob() { }
1487 virtual ~DoJob() { }
1488
1489 //The return value control how the tree-walker tranverse the tree
1490 // 0: Job done, stop tranverse and return
1491 // 1: Ignore the current node's subtree and continue
1492 // 2: Job not done, continue
1493 enum { OK = 0 , IGR = 1, CONT = 2 };
1494 virtual int operator() ( wxDataViewTreeNode * node ) = 0;
1495 virtual int operator() ( void * n ) = 0;
1496 };
1497
1498 bool Walker( wxDataViewTreeNode * node, DoJob & func )
1499 {
1500 if( node==NULL )
1501 return false;
1502
1503 switch( func( node ) )
1504 {
1505 case DoJob::OK :
1506 return true;
1507 case DoJob::IGR:
1508 return false;
1509 case DoJob::CONT:
1510 default:
1511 ;
1512 }
1513
1514 wxDataViewTreeNodes nodes = node->GetNodes();
1515 wxDataViewTreeLeaves leaves = node->GetChildren();
1516
1517 int len_nodes = nodes.GetCount();
1518 int len = leaves.GetCount();
1519 int i = 0, nodes_i = 0;
1520
1521 for(; i < len; i ++ )
1522 {
1523 void * n = leaves[i];
1524 if( nodes_i < len_nodes && n == nodes[nodes_i]->GetItem().GetID() )
1525 {
1526 wxDataViewTreeNode * nd = nodes[nodes_i];
1527 nodes_i++;
1528
1529 if( Walker( nd , func ) )
1530 return true;
1531
1532 }
1533 else
1534 switch( func( n ) )
1535 {
1536 case DoJob::OK :
1537 return true;
1538 case DoJob::IGR:
1539 continue;
1540 case DoJob::CONT:
1541 default:
1542 ;
1543 }
1544 }
1545 return false;
1546 }
1547
1548 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
1549 {
1550 if (!m_root)
1551 {
1552 m_count++;
1553 UpdateDisplay();
1554 return true;
1555 }
1556
1557 SortPrepare();
1558
1559 wxDataViewTreeNode * node;
1560 node = FindNode(parent);
1561
1562 if( node == NULL )
1563 return false;
1564
1565 node->SetHasChildren( true );
1566
1567 if( g_model->IsContainer( item ) )
1568 {
1569 wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node );
1570 newnode->SetItem(item);
1571 newnode->SetHasChildren( true );
1572 node->AddNode( newnode);
1573 }
1574 else
1575 node->AddLeaf( item.GetID() );
1576
1577 node->ChangeSubTreeCount(1);
1578
1579 m_count = -1;
1580 UpdateDisplay();
1581
1582 return true;
1583 }
1584
1585 static void DestroyTreeHelper( wxDataViewTreeNode * node);
1586
1587 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
1588 const wxDataViewItem& item)
1589 {
1590 if (!m_root)
1591 {
1592 m_count--;
1593 if( m_currentRow > GetRowCount() )
1594 m_currentRow = m_count - 1;
1595
1596 m_selection.Empty();
1597
1598 UpdateDisplay();
1599
1600 return true;
1601 }
1602
1603 wxDataViewTreeNode * node = FindNode(parent);
1604
1605 wxCHECK_MSG( node != NULL, false, "item not found" );
1606 wxCHECK_MSG( node->GetChildren().Index( item.GetID() ) != wxNOT_FOUND, false, "item not found" );
1607
1608 int sub = -1;
1609 node->GetChildren().Remove( item.GetID() );
1610 //Manuplate selection
1611 if( m_selection.GetCount() > 1 )
1612 {
1613 m_selection.Empty();
1614 }
1615 bool isContainer = false;
1616 wxDataViewTreeNodes nds = node->GetNodes();
1617 for (size_t i = 0; i < nds.GetCount(); i ++)
1618 {
1619 if (nds[i]->GetItem() == item)
1620 {
1621 isContainer = true;
1622 break;
1623 }
1624 }
1625 if( isContainer )
1626 {
1627 wxDataViewTreeNode * n = NULL;
1628 wxDataViewTreeNodes nodes = node->GetNodes();
1629 int len = nodes.GetCount();
1630 for( int i = 0; i < len; i ++)
1631 {
1632 if( nodes[i]->GetItem() == item )
1633 {
1634 n = nodes[i];
1635 break;
1636 }
1637 }
1638
1639 wxCHECK_MSG( n != NULL, false, "item not found" );
1640
1641 node->GetNodes().Remove( n );
1642 sub -= n->GetSubTreeCount();
1643 ::DestroyTreeHelper(n);
1644 }
1645 //Make the row number invalid and get a new valid one when user call GetRowCount
1646 m_count = -1;
1647 node->ChangeSubTreeCount(sub);
1648
1649 //Change the current row to the last row if the current exceed the max row number
1650 if( m_currentRow > GetRowCount() )
1651 m_currentRow = m_count - 1;
1652
1653 UpdateDisplay();
1654
1655 return true;
1656 }
1657
1658 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
1659 {
1660 SortPrepare();
1661 g_model->Resort();
1662
1663 //Send event
1664 wxWindow *parent = GetParent();
1665 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
1666 le.SetEventObject(parent);
1667 le.SetModel(GetOwner()->GetModel());
1668 le.SetItem(item);
1669 parent->GetEventHandler()->ProcessEvent(le);
1670
1671 return true;
1672 }
1673
1674 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int col )
1675 {
1676 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
1677 /*#define MAX_VIRTUAL_WIDTH 100000
1678
1679 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
1680 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1681 Refresh( true, &rect );
1682
1683 return true;
1684 */
1685 SortPrepare();
1686 g_model->Resort();
1687
1688 //Send event
1689 wxWindow *parent = GetParent();
1690 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
1691 le.SetEventObject(parent);
1692 le.SetModel(GetOwner()->GetModel());
1693 le.SetItem(item);
1694 le.SetColumn(col);
1695 le.SetDataViewColumn(GetOwner()->GetColumn(col));
1696 parent->GetEventHandler()->ProcessEvent(le);
1697
1698 return true;
1699 }
1700
1701 bool wxDataViewMainWindow::Cleared()
1702 {
1703 DestroyTree();
1704
1705 SortPrepare();
1706 BuildTree( GetOwner()->GetModel() );
1707
1708 UpdateDisplay();
1709
1710 return true;
1711 }
1712
1713 void wxDataViewMainWindow::UpdateDisplay()
1714 {
1715 m_dirty = true;
1716 }
1717
1718 void wxDataViewMainWindow::OnInternalIdle()
1719 {
1720 wxWindow::OnInternalIdle();
1721
1722 if (m_dirty)
1723 {
1724 RecalculateDisplay();
1725 m_dirty = false;
1726 }
1727 }
1728
1729 void wxDataViewMainWindow::RecalculateDisplay()
1730 {
1731 wxDataViewModel *model = GetOwner()->GetModel();
1732 if (!model)
1733 {
1734 Refresh();
1735 return;
1736 }
1737
1738 int width = GetEndOfLastCol();
1739 int height = GetLineStart( GetRowCount() );
1740
1741 SetVirtualSize( width, height );
1742 GetOwner()->SetScrollRate( 10, m_lineHeight );
1743
1744 Refresh();
1745 }
1746
1747 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1748 {
1749 wxWindow::ScrollWindow( dx, dy, rect );
1750
1751 if (GetOwner()->m_headerArea)
1752 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1753 }
1754
1755 void wxDataViewMainWindow::ScrollTo( int rows, int column )
1756 {
1757 int x, y;
1758 m_owner->GetScrollPixelsPerUnit( &x, &y );
1759 int sy = GetLineStart( rows )/y;
1760 int sx = 0;
1761 if( column != -1 )
1762 {
1763 wxRect rect = GetClientRect();
1764 int colnum = 0;
1765 int x_start, w = 0;
1766 int xx, yy, xe;
1767 m_owner->CalcUnscrolledPosition( rect.x, rect.y, &xx, &yy );
1768 for (x_start = 0; colnum < column; colnum++)
1769 {
1770 wxDataViewColumn *col = GetOwner()->GetColumn(colnum);
1771 if (col->IsHidden())
1772 continue; // skip it!
1773
1774 w = col->GetWidth();
1775 x_start += w;
1776 }
1777
1778 int x_end = x_start + w;
1779 xe = xx + rect.width;
1780 if( x_end > xe )
1781 {
1782 sx = ( xx + x_end - xe )/x;
1783 }
1784 if( x_start < xx )
1785 {
1786 sx = x_start/x;
1787 }
1788 }
1789 m_owner->Scroll( sx, sy );
1790 }
1791
1792 int wxDataViewMainWindow::GetCountPerPage() const
1793 {
1794 wxSize size = GetClientSize();
1795 return size.y / m_lineHeight;
1796 }
1797
1798 int wxDataViewMainWindow::GetEndOfLastCol() const
1799 {
1800 int width = 0;
1801 unsigned int i;
1802 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
1803 {
1804 const wxDataViewColumn *c =
1805 const_cast<wxDataViewCtrl*>(GetOwner())->GetColumn( i );
1806
1807 if (!c->IsHidden())
1808 width += c->GetWidth();
1809 }
1810 return width;
1811 }
1812
1813 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
1814 {
1815 int x = 0;
1816 int y = 0;
1817 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1818
1819 return GetLineAt( y );
1820 }
1821
1822 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
1823 {
1824 wxSize client_size = GetClientSize();
1825 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
1826 &client_size.x, &client_size.y );
1827
1828 //we should deal with the pixel here
1829 unsigned int row = GetLineAt(client_size.y) - 1;
1830
1831 return wxMin( GetRowCount()-1, row );
1832 }
1833
1834 unsigned int wxDataViewMainWindow::GetRowCount()
1835 {
1836 if ( m_count == -1 )
1837 {
1838 m_count = RecalculateCount();
1839 UpdateDisplay();
1840 }
1841 return m_count;
1842 }
1843
1844 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
1845 {
1846 m_currentRow = row;
1847
1848 // send event
1849 }
1850
1851 void wxDataViewMainWindow::SelectAllRows( bool on )
1852 {
1853 if (IsEmpty())
1854 return;
1855
1856 if (on)
1857 {
1858 m_selection.Clear();
1859 for (unsigned int i = 0; i < GetRowCount(); i++)
1860 m_selection.Add( i );
1861 Refresh();
1862 }
1863 else
1864 {
1865 unsigned int first_visible = GetFirstVisibleRow();
1866 unsigned int last_visible = GetLastVisibleRow();
1867 unsigned int i;
1868 for (i = 0; i < m_selection.GetCount(); i++)
1869 {
1870 unsigned int row = m_selection[i];
1871 if ((row >= first_visible) && (row <= last_visible))
1872 RefreshRow( row );
1873 }
1874 m_selection.Clear();
1875 }
1876 }
1877
1878 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
1879 {
1880 if (m_selection.Index( row ) == wxNOT_FOUND)
1881 {
1882 if (on)
1883 {
1884 m_selection.Add( row );
1885 RefreshRow( row );
1886 }
1887 }
1888 else
1889 {
1890 if (!on)
1891 {
1892 m_selection.Remove( row );
1893 RefreshRow( row );
1894 }
1895 }
1896 }
1897
1898 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
1899 {
1900 if (from > to)
1901 {
1902 unsigned int tmp = from;
1903 from = to;
1904 to = tmp;
1905 }
1906
1907 unsigned int i;
1908 for (i = from; i <= to; i++)
1909 {
1910 if (m_selection.Index( i ) == wxNOT_FOUND)
1911 {
1912 if (on)
1913 m_selection.Add( i );
1914 }
1915 else
1916 {
1917 if (!on)
1918 m_selection.Remove( i );
1919 }
1920 }
1921 RefreshRows( from, to );
1922 }
1923
1924 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
1925 {
1926 for (size_t i=0; i < aSelections.GetCount(); i++)
1927 {
1928 int n = aSelections[i];
1929
1930 m_selection.Add( n );
1931 RefreshRow( n );
1932 }
1933 }
1934
1935 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
1936 {
1937 if (m_selection.Index( row ) == wxNOT_FOUND)
1938 m_selection.Add( row );
1939 else
1940 m_selection.Remove( row );
1941 RefreshRow( row );
1942 }
1943
1944 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
1945 {
1946 return (m_selection.Index( row ) != wxNOT_FOUND);
1947 }
1948
1949 void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
1950 {
1951 wxWindow *parent = GetParent();
1952 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
1953
1954 le.SetEventObject(parent);
1955 le.SetModel(GetOwner()->GetModel());
1956 le.SetItem( item );
1957
1958 parent->GetEventHandler()->ProcessEvent(le);
1959 }
1960
1961 void wxDataViewMainWindow::RefreshRow( unsigned int row )
1962 {
1963 wxRect rect( 0, GetLineStart( row ), GetEndOfLastCol(), GetLineHeight( row ) );
1964 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1965
1966 wxSize client_size = GetClientSize();
1967 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1968 wxRect intersect_rect = client_rect.Intersect( rect );
1969 if (intersect_rect.width > 0)
1970 Refresh( true, &intersect_rect );
1971 }
1972
1973 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
1974 {
1975 if (from > to)
1976 {
1977 unsigned int tmp = to;
1978 to = from;
1979 from = tmp;
1980 }
1981
1982 wxRect rect( 0, GetLineStart( from ), GetEndOfLastCol(), GetLineStart( (to-from+1) ) );
1983 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1984
1985 wxSize client_size = GetClientSize();
1986 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1987 wxRect intersect_rect = client_rect.Intersect( rect );
1988 if (intersect_rect.width > 0)
1989 Refresh( true, &intersect_rect );
1990 }
1991
1992 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
1993 {
1994 wxSize client_size = GetClientSize();
1995 int start = GetLineStart( firstRow );
1996 m_owner->CalcScrolledPosition( start, 0, &start, NULL );
1997 if (start > client_size.y) return;
1998
1999 wxRect rect( 0, start, client_size.x, client_size.y - start );
2000
2001 Refresh( true, &rect );
2002 }
2003
2004 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
2005 {
2006 wxCHECK_RET( newCurrent < GetRowCount(),
2007 _T("invalid item index in OnArrowChar()") );
2008
2009 // if there is no selection, we cannot move it anywhere
2010 if (!HasCurrentRow())
2011 return;
2012
2013 unsigned int oldCurrent = m_currentRow;
2014
2015 // in single selection we just ignore Shift as we can't select several
2016 // items anyhow
2017 if ( event.ShiftDown() && !IsSingleSel() )
2018 {
2019 RefreshRow( oldCurrent );
2020
2021 ChangeCurrentRow( newCurrent );
2022
2023 // select all the items between the old and the new one
2024 if ( oldCurrent > newCurrent )
2025 {
2026 newCurrent = oldCurrent;
2027 oldCurrent = m_currentRow;
2028 }
2029
2030 SelectRows( oldCurrent, newCurrent, true );
2031 if (oldCurrent!=newCurrent)
2032 SendSelectionChangedEvent(GetItemByRow(m_selection[0]));
2033 }
2034 else // !shift
2035 {
2036 RefreshRow( oldCurrent );
2037
2038 // all previously selected items are unselected unless ctrl is held
2039 if ( !event.ControlDown() )
2040 SelectAllRows(false);
2041
2042 ChangeCurrentRow( newCurrent );
2043
2044 if ( !event.ControlDown() )
2045 {
2046 SelectRow( m_currentRow, true );
2047 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
2048 }
2049 else
2050 RefreshRow( m_currentRow );
2051 }
2052
2053 GetOwner()->EnsureVisible( m_currentRow, -1 );
2054 }
2055
2056 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2057 {
2058 wxRect rect;
2059 rect.x = 0;
2060 rect.y = GetLineStart( row );
2061 rect.width = GetEndOfLastCol();
2062 rect.height = GetLineHeight( row );
2063
2064 return rect;
2065 }
2066
2067 int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
2068 {
2069 const wxDataViewModel *model = GetOwner()->GetModel();
2070
2071 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2072 {
2073 // TODO make more efficient
2074
2075 int start = 0;
2076
2077 unsigned int r;
2078 for (r = 0; r < row; r++)
2079 {
2080 const wxDataViewTreeNode* node = GetTreeNodeByRow(r);
2081 if (!node) return start;
2082
2083 wxDataViewItem item = node->GetItem();
2084
2085 if (node && !node->HasChildren())
2086 {
2087 // Yes, if the node does not have any child, it must be a leaf which
2088 // mean that it is a temporarily created by GetTreeNodeByRow
2089 wxDELETE(node);
2090 }
2091
2092 unsigned int cols = GetOwner()->GetColumnCount();
2093 unsigned int col;
2094 int height = m_lineHeight;
2095 for (col = 0; col < cols; col++)
2096 {
2097 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2098 if (column->IsHidden())
2099 continue; // skip it!
2100
2101 if ((col != 0) && model->IsContainer(item) && !model->HasContainerColumns(item))
2102 continue; // skip it!
2103
2104 const wxDataViewRenderer *renderer = column->GetRenderer();
2105 wxVariant value;
2106 model->GetValue( value, item, column->GetModelColumn() );
2107 wxDataViewRenderer *renderer2 = const_cast<wxDataViewRenderer*>(renderer);
2108 renderer2->SetValue( value );
2109 height = wxMax( height, renderer->GetSize().y );
2110 }
2111
2112
2113 start += height;
2114 }
2115
2116 return start;
2117 }
2118 else
2119 {
2120 return row * m_lineHeight;
2121 }
2122 }
2123
2124 int wxDataViewMainWindow::GetLineAt( unsigned int y ) const
2125 {
2126 const wxDataViewModel *model = GetOwner()->GetModel();
2127
2128 // check for the easy case first
2129 if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
2130 return y / m_lineHeight;
2131
2132 // TODO make more efficient
2133 unsigned int row = 0;
2134 unsigned int yy = 0;
2135 for (;;)
2136 {
2137 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2138 if (!node)
2139 {
2140 // not really correct...
2141 return row + ((y-yy) / m_lineHeight);
2142 }
2143
2144 wxDataViewItem item = node->GetItem();
2145
2146 if (node && !node->HasChildren())
2147 {
2148 // Yes, if the node does not have any child, it must be a leaf which
2149 // mean that it is a temporarily created by GetTreeNodeByRow
2150 wxDELETE(node);
2151 }
2152
2153 unsigned int cols = GetOwner()->GetColumnCount();
2154 unsigned int col;
2155 int height = m_lineHeight;
2156 for (col = 0; col < cols; col++)
2157 {
2158 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2159 if (column->IsHidden())
2160 continue; // skip it!
2161
2162 if ((col != 0) && model->IsContainer(item) && !model->HasContainerColumns(item))
2163 continue; // skip it!
2164
2165 const wxDataViewRenderer *renderer = column->GetRenderer();
2166 wxVariant value;
2167 model->GetValue( value, item, column->GetModelColumn() );
2168 wxDataViewRenderer *renderer2 = const_cast<wxDataViewRenderer*>(renderer);
2169 renderer2->SetValue( value );
2170 height = wxMax( height, renderer->GetSize().y );
2171 }
2172
2173 yy += height;
2174 if (y < yy)
2175 return row;
2176
2177 row++;
2178 }
2179 }
2180
2181 int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
2182 {
2183 const wxDataViewModel *model = GetOwner()->GetModel();
2184
2185 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2186 {
2187 wxASSERT( !IsVirtualList() );
2188
2189 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2190 // wxASSERT( node );
2191 if (!node) return m_lineHeight;
2192
2193 wxDataViewItem item = node->GetItem();
2194
2195 if (node && !node->HasChildren())
2196 {
2197 // Yes, if the node does not have any child, it must be a leaf which
2198 // mean that it is a temporarily created by GetTreeNodeByRow
2199 wxDELETE(node);
2200 }
2201
2202 int height = m_lineHeight;
2203
2204 unsigned int cols = GetOwner()->GetColumnCount();
2205 unsigned int col;
2206 for (col = 0; col < cols; col++)
2207 {
2208 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2209 if (column->IsHidden())
2210 continue; // skip it!
2211
2212 if ((col != 0) && model->IsContainer(item) && !model->HasContainerColumns(item))
2213 continue; // skip it!
2214
2215 const wxDataViewRenderer *renderer = column->GetRenderer();
2216 wxVariant value;
2217 model->GetValue( value, item, column->GetModelColumn() );
2218 wxDataViewRenderer *renderer2 = const_cast<wxDataViewRenderer*>(renderer);
2219 renderer2->SetValue( value );
2220 height = wxMax( height, renderer->GetSize().y );
2221 }
2222
2223 return height;
2224 }
2225 else
2226 {
2227 return m_lineHeight;
2228 }
2229 }
2230
2231 class RowToItemJob: public DoJob
2232 {
2233 public:
2234 RowToItemJob( unsigned int row , int current ) { this->row = row; this->current = current;}
2235 virtual ~RowToItemJob() { }
2236
2237 virtual int operator() ( wxDataViewTreeNode * node )
2238 {
2239 current ++;
2240 if( current == static_cast<int>(row))
2241 {
2242 ret = node->GetItem();
2243 return DoJob::OK;
2244 }
2245
2246 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2247 {
2248 current += node->GetSubTreeCount();
2249 return DoJob::IGR;
2250 }
2251 else
2252 {
2253 //If the current has no child node, we can find the desired item of the row number directly.
2254 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
2255 if( node->GetNodes().GetCount() == 0)
2256 {
2257 int index = static_cast<int>(row) - current - 1;
2258 ret = node->GetChildren().Item( index );
2259 return DoJob::OK;
2260 }
2261 return DoJob::CONT;
2262 }
2263 }
2264
2265 virtual int operator() ( void * n )
2266 {
2267 current ++;
2268 if( current == static_cast<int>(row))
2269 {
2270 ret = wxDataViewItem( n );
2271 return DoJob::OK;
2272 }
2273 return DoJob::CONT;
2274 }
2275 wxDataViewItem GetResult(){ return ret; }
2276 private:
2277 unsigned int row;
2278 int current;
2279 wxDataViewItem ret;
2280 };
2281
2282 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const
2283 {
2284 if (!m_root)
2285 {
2286 return wxDataViewItem( wxUIntToPtr(row) );
2287 }
2288 else
2289 {
2290 RowToItemJob job( row, -2 );
2291 Walker( m_root , job );
2292 return job.GetResult();
2293 }
2294 }
2295
2296 class RowToTreeNodeJob: public DoJob
2297 {
2298 public:
2299 RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
2300 {
2301 this->row = row;
2302 this->current = current;
2303 ret = NULL;
2304 parent = node;
2305 }
2306 virtual ~RowToTreeNodeJob(){ }
2307
2308 virtual int operator() ( wxDataViewTreeNode * node )
2309 {
2310 current ++;
2311 if( current == static_cast<int>(row))
2312 {
2313 ret = node;
2314 return DoJob::OK;
2315 }
2316
2317 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2318 {
2319 current += node->GetSubTreeCount();
2320 return DoJob::IGR;
2321 }
2322 else
2323 {
2324 parent = node;
2325 //If the current has no child node, we can find the desired item of the row number directly.
2326 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
2327 if( node->GetNodes().GetCount() == 0)
2328 {
2329 int index = static_cast<int>(row) - current - 1;
2330 void * n = node->GetChildren().Item( index );
2331 ret = new wxDataViewTreeNode( parent );
2332 ret->SetItem( wxDataViewItem( n ));
2333 ret->SetHasChildren(false);
2334 return DoJob::OK;
2335 }
2336 return DoJob::CONT;
2337 }
2338
2339
2340 }
2341
2342 virtual int operator() ( void * n )
2343 {
2344 current ++;
2345 if( current == static_cast<int>(row))
2346 {
2347 ret = new wxDataViewTreeNode( parent );
2348 ret->SetItem( wxDataViewItem( n ));
2349 ret->SetHasChildren(false);
2350 return DoJob::OK;
2351 }
2352
2353 return DoJob::CONT;
2354 }
2355 wxDataViewTreeNode * GetResult(){ return ret; }
2356 private:
2357 unsigned int row;
2358 int current;
2359 wxDataViewTreeNode * ret;
2360 wxDataViewTreeNode * parent;
2361 };
2362
2363
2364 wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) const
2365 {
2366 wxASSERT( !IsVirtualList() );
2367
2368 RowToTreeNodeJob job( row , -2, m_root );
2369 Walker( m_root , job );
2370 return job.GetResult();
2371 }
2372
2373 wxDataViewEvent wxDataViewMainWindow::SendExpanderEvent( wxEventType type, const wxDataViewItem & item )
2374 {
2375 wxWindow *parent = GetParent();
2376 wxDataViewEvent le(type, parent->GetId());
2377
2378 le.SetEventObject(parent);
2379 le.SetModel(GetOwner()->GetModel());
2380 le.SetItem( item );
2381
2382 parent->GetEventHandler()->ProcessEvent(le);
2383 return le;
2384 }
2385
2386 void wxDataViewMainWindow::OnExpanding( unsigned int row )
2387 {
2388 if (IsVirtualList())
2389 return;
2390
2391 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2392 if( node != NULL )
2393 {
2394 if( node->HasChildren())
2395 {
2396 if( !node->IsOpen())
2397 {
2398 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,node->GetItem());
2399 //Check if the user prevent expanding
2400 if( e.GetSkipped() )
2401 return;
2402
2403 node->ToggleOpen();
2404 //Here I build the children of current node
2405 if( node->GetChildrenNumber() == 0 )
2406 {
2407 SortPrepare();
2408 ::BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
2409 }
2410 m_count = -1;
2411 UpdateDisplay();
2412 //Send the expanded event
2413 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
2414 }
2415 else
2416 {
2417 SelectRow( row, false );
2418 SelectRow( row + 1, true );
2419 ChangeCurrentRow( row + 1 );
2420 SendSelectionChangedEvent( GetItemByRow(row+1));
2421 }
2422 }
2423 else
2424 delete node;
2425 }
2426 }
2427
2428 void wxDataViewMainWindow::OnCollapsing(unsigned int row)
2429 {
2430 if (IsVirtualList())
2431 return;
2432
2433 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2434 if( node != NULL )
2435 {
2436 wxDataViewTreeNode * nd = node;
2437
2438 if( node->HasChildren() && node->IsOpen() )
2439 {
2440 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem());
2441 if( e.GetSkipped() )
2442 return;
2443 node->ToggleOpen();
2444 m_count = -1;
2445 UpdateDisplay();
2446 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,nd->GetItem());
2447 }
2448 else
2449 {
2450 node = node->GetParent();
2451 if( node != NULL )
2452 {
2453 int parent = GetRowByItem( node->GetItem() );
2454 if( parent >= 0 )
2455 {
2456 SelectRow( row, false);
2457 SelectRow(parent , true );
2458 ChangeCurrentRow( parent );
2459 SendSelectionChangedEvent( node->GetItem() );
2460 }
2461 }
2462 }
2463 if( !nd->HasChildren())
2464 delete nd;
2465 }
2466 }
2467
2468 wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
2469 {
2470 wxDataViewModel * model = GetOwner()->GetModel();
2471 if( model == NULL )
2472 return NULL;
2473
2474 //Compose the a parent-chain of the finding item
2475 ItemList list;
2476 list.DeleteContents( true );
2477 wxDataViewItem it( item );
2478 while( it.IsOk() )
2479 {
2480 wxDataViewItem * pItem = new wxDataViewItem( it );
2481 list.Insert( pItem );
2482 it = model->GetParent( it );
2483 }
2484
2485 //Find the item along the parent-chain.
2486 //This algorithm is designed to speed up the node-finding method
2487 wxDataViewTreeNode * node = m_root;
2488 for( ItemList::const_iterator iter = list.begin(); iter !=list.end(); iter++ )
2489 {
2490 if( node->HasChildren() )
2491 {
2492 if( node->GetChildrenNumber() == 0 )
2493 {
2494 SortPrepare();
2495 ::BuildTreeHelper(model, node->GetItem(), node);
2496 }
2497
2498 wxDataViewTreeNodes nodes = node->GetNodes();
2499 unsigned int i;
2500 bool found = false;
2501
2502 for (i = 0; i < nodes.GetCount(); i ++)
2503 {
2504 if (nodes[i]->GetItem() == (**iter))
2505 {
2506 if (nodes[i]->GetItem() == item)
2507 return nodes[i];
2508
2509 node = nodes[i];
2510 found = true;
2511 break;
2512 }
2513 }
2514 if (!found)
2515 return NULL;
2516 }
2517 else
2518 return NULL;
2519 }
2520 return NULL;
2521 }
2522
2523 void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column )
2524 {
2525 wxDataViewColumn *col = NULL;
2526 unsigned int cols = GetOwner()->GetColumnCount();
2527 unsigned int colnum = 0;
2528 int x, y;
2529 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
2530 for (unsigned x_start = 0; colnum < cols; colnum++)
2531 {
2532 col = GetOwner()->GetColumn(colnum);
2533 if (col->IsHidden())
2534 continue; // skip it!
2535
2536 unsigned int w = col->GetWidth();
2537 if (x_start+w >= (unsigned int)x)
2538 break;
2539
2540 x_start += w;
2541 }
2542
2543 column = col;
2544 item = GetItemByRow( GetLineAt( y ) );
2545 }
2546
2547 wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column )
2548 {
2549 int row = GetRowByItem(item);
2550 int y = GetLineStart( row );
2551 int h = GetLineHeight( m_lineHeight );
2552 int x = 0;
2553 wxDataViewColumn *col = NULL;
2554 for( int i = 0, cols = GetOwner()->GetColumnCount(); i < cols; i ++ )
2555 {
2556 col = GetOwner()->GetColumn( i );
2557 x += col->GetWidth();
2558 if( GetOwner()->GetColumn(i+1) == column )
2559 break;
2560 }
2561 int w = col->GetWidth();
2562 m_owner->CalcScrolledPosition( x, y, &x, &y );
2563 return wxRect(x, y, w, h);
2564 }
2565
2566 int wxDataViewMainWindow::RecalculateCount()
2567 {
2568 if (!m_root)
2569 {
2570 wxDataViewIndexListModel *list_model = (wxDataViewIndexListModel*) GetOwner()->GetModel();
2571 #ifndef __WXMAC__
2572 return list_model->GetLastIndex() + 1;
2573 #else
2574 return list_model->GetLastIndex() - 1;
2575 #endif
2576 }
2577 else
2578 {
2579 return m_root->GetSubTreeCount();
2580 }
2581 }
2582
2583 class ItemToRowJob : public DoJob
2584 {
2585 public:
2586 ItemToRowJob(const wxDataViewItem& item_, ItemList::const_iterator iter)
2587 : m_iter(iter),
2588 item(item_)
2589 {
2590 ret = -1;
2591 }
2592
2593 //Maybe binary search will help to speed up this process
2594 virtual int operator() ( wxDataViewTreeNode * node)
2595 {
2596 ret ++;
2597 if( node->GetItem() == item )
2598 {
2599 return DoJob::OK;
2600 }
2601
2602 if( node->GetItem() == **m_iter )
2603 {
2604 m_iter++;
2605 return DoJob::CONT;
2606 }
2607 else
2608 {
2609 ret += node->GetSubTreeCount();
2610 return DoJob::IGR;
2611 }
2612
2613 }
2614
2615 virtual int operator() ( void * n )
2616 {
2617 ret ++;
2618 if( n == item.GetID() )
2619 return DoJob::OK;
2620 return DoJob::CONT;
2621 }
2622 //the row number is begin from zero
2623 int GetResult() { return ret -1; }
2624
2625 private:
2626 ItemList::const_iterator m_iter;
2627 wxDataViewItem item;
2628 int ret;
2629
2630 };
2631
2632 int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
2633 {
2634 const wxDataViewModel * model = GetOwner()->GetModel();
2635 if( model == NULL )
2636 return -1;
2637
2638 if (!m_root)
2639 {
2640 return wxPtrToUInt( item.GetID() );
2641 }
2642 else
2643 {
2644 if( !item.IsOk() )
2645 return -1;
2646
2647 //Compose the a parent-chain of the finding item
2648 ItemList list;
2649 wxDataViewItem * pItem;
2650 list.DeleteContents( true );
2651 wxDataViewItem it( item );
2652 while( it.IsOk() )
2653 {
2654 pItem = new wxDataViewItem( it );
2655 list.Insert( pItem );
2656 it = model->GetParent( it );
2657 }
2658 pItem = new wxDataViewItem( );
2659 list.Insert( pItem );
2660
2661 ItemToRowJob job( item, list.begin() );
2662 Walker(m_root , job );
2663 return job.GetResult();
2664 }
2665 }
2666
2667 static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node)
2668 {
2669 if( !model->IsContainer( item ) )
2670 return;
2671
2672 wxDataViewItemArray children;
2673 unsigned int num = model->GetChildren( item, children);
2674 unsigned int index = 0;
2675 while( index < num )
2676 {
2677 if( model->IsContainer( children[index] ) )
2678 {
2679 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
2680 n->SetItem(children[index]);
2681 n->SetHasChildren( true );
2682 node->AddNode( n );
2683 }
2684 else
2685 {
2686 node->AddLeaf( children[index].GetID() );
2687 }
2688 index ++;
2689 }
2690 node->SetSubTreeCount( num );
2691 wxDataViewTreeNode * n = node->GetParent();
2692 if( n != NULL)
2693 n->ChangeSubTreeCount(num);
2694
2695 }
2696
2697 void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
2698 {
2699 DestroyTree();
2700
2701 if (GetOwner()->GetModel()->IsVirtualListModel())
2702 {
2703 m_count = -1;
2704 return;
2705 }
2706
2707 m_root = new wxDataViewTreeNode( NULL );
2708 m_root->SetHasChildren(true);
2709
2710 //First we define a invalid item to fetch the top-level elements
2711 wxDataViewItem item;
2712 SortPrepare();
2713 BuildTreeHelper( model, item, m_root);
2714 m_count = -1;
2715 }
2716
2717 static void DestroyTreeHelper( wxDataViewTreeNode * node )
2718 {
2719 if( node->GetNodeNumber() != 0 )
2720 {
2721 int len = node->GetNodeNumber();
2722 int i = 0;
2723 wxDataViewTreeNodes& nodes = node->GetNodes();
2724 for(; i < len; i ++ )
2725 {
2726 DestroyTreeHelper(nodes[i]);
2727 }
2728 }
2729 delete node;
2730 }
2731
2732 void wxDataViewMainWindow::DestroyTree()
2733 {
2734 if (!IsVirtualList())
2735 {
2736 ::DestroyTreeHelper(m_root);
2737 m_count = 0;
2738 m_root = NULL;
2739 }
2740 }
2741
2742 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
2743 {
2744 if ( GetParent()->HandleAsNavigationKey(event) )
2745 return;
2746
2747 // no item -> nothing to do
2748 if (!HasCurrentRow())
2749 {
2750 event.Skip();
2751 return;
2752 }
2753
2754 // don't use m_linesPerPage directly as it might not be computed yet
2755 const int pageSize = GetCountPerPage();
2756 wxCHECK_RET( pageSize, _T("should have non zero page size") );
2757
2758 switch ( event.GetKeyCode() )
2759 {
2760 case WXK_RETURN:
2761 {
2762 if (m_currentRow > 0)
2763 {
2764 wxWindow *parent = GetParent();
2765 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
2766 le.SetItem( GetItemByRow(m_currentRow) );
2767 le.SetEventObject(parent);
2768 le.SetModel(GetOwner()->GetModel());
2769
2770 parent->GetEventHandler()->ProcessEvent(le);
2771 }
2772 break;
2773 }
2774 case WXK_UP:
2775 if ( m_currentRow > 0 )
2776 OnArrowChar( m_currentRow - 1, event );
2777 break;
2778
2779 case WXK_DOWN:
2780 if ( m_currentRow < GetRowCount() - 1 )
2781 OnArrowChar( m_currentRow + 1, event );
2782 break;
2783 //Add the process for tree expanding/collapsing
2784 case WXK_LEFT:
2785 OnCollapsing(m_currentRow);
2786 break;
2787 case WXK_RIGHT:
2788 OnExpanding( m_currentRow);
2789 break;
2790 case WXK_END:
2791 if (!IsEmpty())
2792 OnArrowChar( GetRowCount() - 1, event );
2793 break;
2794
2795 case WXK_HOME:
2796 if (!IsEmpty())
2797 OnArrowChar( 0, event );
2798 break;
2799
2800 case WXK_PAGEUP:
2801 {
2802 int steps = pageSize - 1;
2803 int index = m_currentRow - steps;
2804 if (index < 0)
2805 index = 0;
2806
2807 OnArrowChar( index, event );
2808 }
2809 break;
2810
2811 case WXK_PAGEDOWN:
2812 {
2813 int steps = pageSize - 1;
2814 unsigned int index = m_currentRow + steps;
2815 unsigned int count = GetRowCount();
2816 if ( index >= count )
2817 index = count - 1;
2818
2819 OnArrowChar( index, event );
2820 }
2821 break;
2822
2823 default:
2824 event.Skip();
2825 }
2826 }
2827
2828 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
2829 {
2830 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2831 {
2832 // let the base handle mouse wheel events.
2833 event.Skip();
2834 return;
2835 }
2836
2837 int x = event.GetX();
2838 int y = event.GetY();
2839 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2840 wxDataViewColumn *col = NULL;
2841
2842 int xpos = 0;
2843 unsigned int cols = GetOwner()->GetColumnCount();
2844 unsigned int i;
2845 for (i = 0; i < cols; i++)
2846 {
2847 wxDataViewColumn *c = GetOwner()->GetColumn( i );
2848 if (c->IsHidden())
2849 continue; // skip it!
2850
2851 if (x < xpos + c->GetWidth())
2852 {
2853 col = c;
2854 break;
2855 }
2856 xpos += c->GetWidth();
2857 }
2858 if (!col)
2859 return;
2860
2861 wxDataViewRenderer *cell = col->GetRenderer();
2862 unsigned int current = GetLineAt( y );
2863 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
2864 {
2865 // Unselect all if below the last row ?
2866 return;
2867 }
2868
2869 //Test whether the mouse is hovered on the tree item button
2870 bool hover = false;
2871 if ((!IsVirtualList()) && (GetOwner()->GetExpanderColumn() == col))
2872 {
2873 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
2874 if( node!=NULL && node->HasChildren() )
2875 {
2876 int indent = node->GetIndentLevel();
2877 indent = GetOwner()->GetIndent()*indent;
2878 wxRect rect( xpos + indent + EXPANDER_MARGIN,
2879 GetLineStart( current ) + EXPANDER_MARGIN + (GetLineHeight(current)/2) - (m_lineHeight/2) - EXPANDER_OFFSET,
2880 m_lineHeight-2*EXPANDER_MARGIN,
2881 m_lineHeight-2*EXPANDER_MARGIN + EXPANDER_OFFSET);
2882 if( rect.Contains( x, y) )
2883 {
2884 //So the mouse is over the expander
2885 hover = true;
2886 if (m_underMouse && m_underMouse != node)
2887 {
2888 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
2889 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
2890 }
2891 if (m_underMouse != node)
2892 {
2893 //wxLogMessage("Do the row: %d", current);
2894 RefreshRow(current);
2895 }
2896 m_underMouse = node;
2897 }
2898 }
2899 if (node!=NULL && !node->HasChildren())
2900 delete node;
2901 }
2902 if (!hover)
2903 {
2904 if (m_underMouse != NULL)
2905 {
2906 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
2907 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
2908 m_underMouse = NULL;
2909 }
2910 }
2911
2912 wxDataViewModel *model = GetOwner()->GetModel();
2913
2914 if (event.Dragging())
2915 {
2916 if (m_dragCount == 0)
2917 {
2918 // we have to report the raw, physical coords as we want to be
2919 // able to call HitTest(event.m_pointDrag) from the user code to
2920 // get the item being dragged
2921 m_dragStart = event.GetPosition();
2922 }
2923
2924 m_dragCount++;
2925
2926 if (m_dragCount != 3)
2927 return;
2928
2929 if (event.LeftIsDown())
2930 {
2931 // Notify cell about drag
2932 }
2933 return;
2934 }
2935 else
2936 {
2937 m_dragCount = 0;
2938 }
2939
2940 bool forceClick = false;
2941
2942 if (event.ButtonDClick())
2943 {
2944 m_renameTimer->Stop();
2945 m_lastOnSame = false;
2946 }
2947
2948 wxDataViewItem item = GetItemByRow(current);
2949 bool ignore_other_columns =
2950 ((GetOwner()->GetExpanderColumn() != col) &&
2951 (model->IsContainer(item)) &&
2952 (!model->HasContainerColumns(item)));
2953
2954 if (event.LeftDClick())
2955 {
2956 if ( current == m_lineLastClicked )
2957 {
2958 if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE))
2959 {
2960 wxVariant value;
2961 model->GetValue( value, item, col->GetModelColumn() );
2962 cell->SetValue( value );
2963 wxRect cell_rect( xpos, GetLineStart( current ),
2964 col->GetWidth(), GetLineHeight( current ) );
2965 cell->Activate( cell_rect, model, item, col->GetModelColumn() );
2966
2967 }
2968 else
2969 {
2970 wxWindow *parent = GetParent();
2971 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
2972 le.SetItem( item );
2973 le.SetEventObject(parent);
2974 le.SetModel(GetOwner()->GetModel());
2975
2976 parent->GetEventHandler()->ProcessEvent(le);
2977 }
2978 return;
2979 }
2980 else
2981 {
2982 // The first click was on another item, so don't interpret this as
2983 // a double click, but as a simple click instead
2984 forceClick = true;
2985 }
2986 }
2987
2988 if (event.LeftUp())
2989 {
2990 if (m_lineSelectSingleOnUp != (unsigned int)-1)
2991 {
2992 // select single line
2993 SelectAllRows( false );
2994 SelectRow( m_lineSelectSingleOnUp, true );
2995 }
2996
2997 //Process the event of user clicking the expander
2998 bool expander = false;
2999 if ((!IsVirtualList()) && (GetOwner()->GetExpanderColumn() == col))
3000 {
3001 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
3002 if( node!=NULL && node->HasChildren() )
3003 {
3004 int indent = node->GetIndentLevel();
3005 indent = GetOwner()->GetIndent()*indent;
3006 wxRect rect( xpos + indent + EXPANDER_MARGIN,
3007 GetLineStart( current ) + EXPANDER_MARGIN + (GetLineHeight(current)/2) - (m_lineHeight/2) - EXPANDER_OFFSET,
3008 m_lineHeight-2*EXPANDER_MARGIN,
3009 m_lineHeight-2*EXPANDER_MARGIN + EXPANDER_OFFSET);
3010
3011 if( rect.Contains( x, y) )
3012 {
3013 expander = true;
3014 if( node->IsOpen() )
3015 OnCollapsing(current);
3016 else
3017 OnExpanding( current );
3018 }
3019 }
3020 if (node && !node->HasChildren())
3021 delete node;
3022 }
3023 //If the user click the expander, we do not do editing even if the column with expander are editable
3024 if (m_lastOnSame && !expander && !ignore_other_columns)
3025 {
3026 if ((col == m_currentCol) && (current == m_currentRow) &&
3027 (cell->GetMode() & wxDATAVIEW_CELL_EDITABLE) )
3028 {
3029 m_renameTimer->Start( 100, true );
3030 }
3031 }
3032
3033 m_lastOnSame = false;
3034 m_lineSelectSingleOnUp = (unsigned int)-1;
3035 }
3036 else
3037 {
3038 // This is necessary, because after a DnD operation in
3039 // from and to ourself, the up event is swallowed by the
3040 // DnD code. So on next non-up event (which means here and
3041 // now) m_lineSelectSingleOnUp should be reset.
3042 m_lineSelectSingleOnUp = (unsigned int)-1;
3043 }
3044
3045 if (event.RightDown())
3046 {
3047 m_lineBeforeLastClicked = m_lineLastClicked;
3048 m_lineLastClicked = current;
3049
3050 // If the item is already selected, do not update the selection.
3051 // Multi-selections should not be cleared if a selected item is clicked.
3052 if (!IsRowSelected(current))
3053 {
3054 SelectAllRows(false);
3055 ChangeCurrentRow(current);
3056 SelectRow(m_currentRow,true);
3057 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3058 }
3059
3060 wxVariant value;
3061 model->GetValue( value, item, col->GetModelColumn() );
3062 wxWindow *parent = GetParent();
3063 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
3064 le.SetItem( item );
3065 le.SetEventObject(parent);
3066 le.SetModel(GetOwner()->GetModel());
3067 le.SetValue(value);
3068 parent->GetEventHandler()->ProcessEvent(le);
3069 }
3070 else if (event.MiddleDown())
3071 {
3072 }
3073 if (event.LeftDown() || forceClick)
3074 {
3075 SetFocus();
3076
3077 m_lineBeforeLastClicked = m_lineLastClicked;
3078 m_lineLastClicked = current;
3079
3080 unsigned int oldCurrentRow = m_currentRow;
3081 bool oldWasSelected = IsRowSelected(m_currentRow);
3082
3083 bool cmdModifierDown = event.CmdDown();
3084 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
3085 {
3086 if ( IsSingleSel() || !IsRowSelected(current) )
3087 {
3088 SelectAllRows( false );
3089 ChangeCurrentRow(current);
3090 SelectRow(m_currentRow,true);
3091 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3092 }
3093 else // multi sel & current is highlighted & no mod keys
3094 {
3095 m_lineSelectSingleOnUp = current;
3096 ChangeCurrentRow(current); // change focus
3097 }
3098 }
3099 else // multi sel & either ctrl or shift is down
3100 {
3101 if (cmdModifierDown)
3102 {
3103 ChangeCurrentRow(current);
3104 ReverseRowSelection(m_currentRow);
3105 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
3106 }
3107 else if (event.ShiftDown())
3108 {
3109 ChangeCurrentRow(current);
3110
3111 unsigned int lineFrom = oldCurrentRow,
3112 lineTo = current;
3113
3114 if ( lineTo < lineFrom )
3115 {
3116 lineTo = lineFrom;
3117 lineFrom = m_currentRow;
3118 }
3119
3120 SelectRows(lineFrom, lineTo, true);
3121 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
3122 }
3123 else // !ctrl, !shift
3124 {
3125 // test in the enclosing if should make it impossible
3126 wxFAIL_MSG( _T("how did we get here?") );
3127 }
3128 }
3129
3130 if (m_currentRow != oldCurrentRow)
3131 RefreshRow( oldCurrentRow );
3132
3133 wxDataViewColumn *oldCurrentCol = m_currentCol;
3134
3135 // Update selection here...
3136 m_currentCol = col;
3137
3138 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
3139 (current == oldCurrentRow)) && oldWasSelected;
3140
3141 // Call LeftClick after everything else as under GTK+
3142 if (cell->GetMode() & wxDATAVIEW_CELL_ACTIVATABLE)
3143 {
3144 // notify cell about right click
3145 wxVariant value;
3146 model->GetValue( value, item, col->GetModelColumn() );
3147 cell->SetValue( value );
3148 wxRect cell_rect( xpos, GetLineStart( current ),
3149 col->GetWidth(), GetLineHeight( current ) );
3150 /* ignore ret */ cell->LeftClick( event.GetPosition(), cell_rect, model, item, col->GetModelColumn());
3151 }
3152 }
3153 }
3154
3155 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
3156 {
3157 m_hasFocus = true;
3158
3159 if (HasCurrentRow())
3160 Refresh();
3161
3162 event.Skip();
3163 }
3164
3165 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
3166 {
3167 m_hasFocus = false;
3168
3169 if (HasCurrentRow())
3170 Refresh();
3171
3172 event.Skip();
3173 }
3174
3175 wxDataViewItem wxDataViewMainWindow::GetSelection() const
3176 {
3177 if( m_selection.GetCount() != 1 )
3178 return wxDataViewItem();
3179
3180 return GetItemByRow( m_selection.Item(0));
3181 }
3182
3183 //-----------------------------------------------------------------------------
3184 // wxDataViewCtrl
3185 //-----------------------------------------------------------------------------
3186 WX_DEFINE_LIST(wxDataViewColumnList)
3187
3188 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
3189
3190 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
3191 EVT_SIZE(wxDataViewCtrl::OnSize)
3192 END_EVENT_TABLE()
3193
3194 wxDataViewCtrl::~wxDataViewCtrl()
3195 {
3196 if (m_notifier)
3197 GetModel()->RemoveNotifier( m_notifier );
3198
3199 m_cols.Clear();
3200 }
3201
3202 void wxDataViewCtrl::Init()
3203 {
3204 m_cols.DeleteContents(true);
3205 m_notifier = NULL;
3206
3207 // No sorting column at start
3208 m_sortingColumn = NULL;
3209 m_headerArea = NULL;
3210 }
3211
3212 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
3213 const wxPoint& pos, const wxSize& size,
3214 long style, const wxValidator& validator )
3215 {
3216 if ( (style & wxBORDER_MASK) == 0)
3217 style |= wxBORDER_SUNKEN;
3218
3219 Init();
3220
3221 if (!wxControl::Create( parent, id, pos, size,
3222 style | wxScrolledWindowStyle, validator))
3223 return false;
3224
3225 SetInitialSize(size);
3226
3227 #ifdef __WXMAC__
3228 MacSetClipChildren( true );
3229 #endif
3230
3231 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
3232
3233 if (HasFlag(wxDV_NO_HEADER))
3234 m_headerArea = NULL;
3235 else
3236 m_headerArea = new wxDataViewHeaderWindow(this);
3237
3238 SetTargetWindow( m_clientArea );
3239
3240 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
3241 if (m_headerArea)
3242 sizer->Add( m_headerArea, 0, wxGROW );
3243 sizer->Add( m_clientArea, 1, wxGROW );
3244 SetSizer( sizer );
3245
3246 return true;
3247 }
3248
3249 #ifdef __WXMSW__
3250 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
3251 WXWPARAM wParam,
3252 WXLPARAM lParam)
3253 {
3254 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
3255
3256 #ifndef __WXWINCE__
3257 // we need to process arrows ourselves for scrolling
3258 if ( nMsg == WM_GETDLGCODE )
3259 {
3260 rc |= DLGC_WANTARROWS;
3261 }
3262 #endif
3263
3264 return rc;
3265 }
3266 #endif
3267
3268 wxSize wxDataViewCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
3269 {
3270 wxSize newsize = size;
3271 if (!HasFlag(wxDV_NO_HEADER) && (m_headerArea))
3272 newsize.y -= m_headerArea->GetSize().y;
3273
3274 return newsize;
3275 }
3276
3277 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
3278 {
3279 // We need to override OnSize so that our scrolled
3280 // window a) does call Layout() to use sizers for
3281 // positioning the controls but b) does not query
3282 // the sizer for their size and use that for setting
3283 // the scrollable area as set that ourselves by
3284 // calling SetScrollbar() further down.
3285
3286 Layout();
3287
3288 AdjustScrollbars();
3289 }
3290
3291 void wxDataViewCtrl::SetFocus()
3292 {
3293 if (m_clientArea)
3294 m_clientArea->SetFocus();
3295 }
3296
3297 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
3298 {
3299 if (!wxDataViewCtrlBase::AssociateModel( model ))
3300 return false;
3301
3302 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
3303
3304 model->AddNotifier( m_notifier );
3305
3306 m_clientArea->DestroyTree();
3307
3308 m_clientArea->BuildTree(model);
3309
3310 m_clientArea->UpdateDisplay();
3311
3312 return true;
3313 }
3314
3315 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
3316 {
3317 if (!wxDataViewCtrlBase::AppendColumn(col))
3318 return false;
3319
3320 m_cols.Append( col );
3321 OnColumnsCountChanged();
3322 return true;
3323 }
3324
3325 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
3326 {
3327 if (!wxDataViewCtrlBase::PrependColumn(col))
3328 return false;
3329
3330 m_cols.Insert( col );
3331 OnColumnsCountChanged();
3332 return true;
3333 }
3334
3335 bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
3336 {
3337 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
3338 return false;
3339
3340 m_cols.Insert( pos, col );
3341 OnColumnsCountChanged();
3342 return true;
3343 }
3344
3345 void wxDataViewCtrl::OnColumnChange(unsigned int idx)
3346 {
3347 if ( m_headerArea )
3348 m_headerArea->UpdateColumn(idx);
3349
3350 m_clientArea->UpdateDisplay();
3351 }
3352
3353 void wxDataViewCtrl::OnColumnsCountChanged()
3354 {
3355 if (m_headerArea)
3356 m_headerArea->SetColumnCount(GetColumnCount());
3357
3358 m_clientArea->UpdateDisplay();
3359 }
3360
3361 void wxDataViewCtrl::DoSetExpanderColumn()
3362 {
3363 m_clientArea->UpdateDisplay();
3364 }
3365
3366 void wxDataViewCtrl::DoSetIndent()
3367 {
3368 m_clientArea->UpdateDisplay();
3369 }
3370
3371 unsigned int wxDataViewCtrl::GetColumnCount() const
3372 {
3373 return m_cols.GetCount();
3374 }
3375
3376 wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
3377 {
3378 return m_cols[idx];
3379 }
3380
3381 void wxDataViewCtrl::ColumnMoved( wxDataViewColumn* col, unsigned int new_pos )
3382 {
3383 if (new_pos > m_cols.GetCount()) return;
3384
3385 // Exchange position
3386 m_cols.DeleteContents(false);
3387 m_cols.DeleteObject( col );
3388 m_cols.Insert( new_pos, col );
3389 m_cols.DeleteContents(true);
3390
3391 m_clientArea->UpdateDisplay();
3392 }
3393
3394 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
3395 {
3396 wxDataViewColumnList::compatibility_iterator ret = m_cols.Find( column );
3397 if (!ret)
3398 return false;
3399
3400 m_cols.Erase(ret);
3401 OnColumnsCountChanged();
3402
3403 return true;
3404 }
3405
3406 bool wxDataViewCtrl::ClearColumns()
3407 {
3408 m_cols.Clear();
3409 OnColumnsCountChanged();
3410 return true;
3411 }
3412
3413 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
3414 {
3415 int ret = 0, dead = 0;
3416 int len = GetColumnCount();
3417 for (int i=0; i<len; i++)
3418 {
3419 wxDataViewColumn * col = GetColumn(i);
3420 if (col->IsHidden())
3421 continue;
3422 ret += col->GetWidth();
3423 if (column==col)
3424 {
3425 CalcScrolledPosition( ret, dead, &ret, &dead );
3426 break;
3427 }
3428 }
3429 return ret;
3430 }
3431
3432 wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
3433 {
3434 return NULL;
3435 }
3436
3437 //Selection code with wxDataViewItem as parameters
3438 wxDataViewItem wxDataViewCtrl::GetSelection() const
3439 {
3440 return m_clientArea->GetSelection();
3441 }
3442
3443 int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
3444 {
3445 sel.Empty();
3446 wxDataViewSelection selection = m_clientArea->GetSelections();
3447 int len = selection.GetCount();
3448 for( int i = 0; i < len; i ++)
3449 {
3450 unsigned int row = selection[i];
3451 sel.Add( m_clientArea->GetItemByRow( row ) );
3452 }
3453 return len;
3454 }
3455
3456 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
3457 {
3458 wxDataViewSelection selection(wxDataViewSelectionCmp);
3459 int len = sel.GetCount();
3460 for( int i = 0; i < len; i ++ )
3461 {
3462 int row = m_clientArea->GetRowByItem( sel[i] );
3463 if( row >= 0 )
3464 selection.Add( static_cast<unsigned int>(row) );
3465 }
3466 m_clientArea->SetSelections( selection );
3467 }
3468
3469 void wxDataViewCtrl::Select( const wxDataViewItem & item )
3470 {
3471 int row = m_clientArea->GetRowByItem( item );
3472 if( row >= 0 )
3473 {
3474 //Unselect all rows before select another in the single select mode
3475 if (m_clientArea->IsSingleSel())
3476 m_clientArea->SelectAllRows(false);
3477 m_clientArea->SelectRow(row, true);
3478 }
3479 }
3480
3481 void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
3482 {
3483 int row = m_clientArea->GetRowByItem( item );
3484 if( row >= 0 )
3485 m_clientArea->SelectRow(row, false);
3486 }
3487
3488 bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
3489 {
3490 int row = m_clientArea->GetRowByItem( item );
3491 if( row >= 0 )
3492 {
3493 return m_clientArea->IsRowSelected(row);
3494 }
3495 return false;
3496 }
3497
3498 //Selection code with row number as parameter
3499 int wxDataViewCtrl::GetSelections( wxArrayInt & sel ) const
3500 {
3501 sel.Empty();
3502 wxDataViewSelection selection = m_clientArea->GetSelections();
3503 int len = selection.GetCount();
3504 for( int i = 0; i < len; i ++)
3505 {
3506 unsigned int row = selection[i];
3507 sel.Add( row );
3508 }
3509 return len;
3510 }
3511
3512 void wxDataViewCtrl::SetSelections( const wxArrayInt & sel )
3513 {
3514 wxDataViewSelection selection(wxDataViewSelectionCmp);
3515 int len = sel.GetCount();
3516 for( int i = 0; i < len; i ++ )
3517 {
3518 int row = sel[i];
3519 if( row >= 0 )
3520 selection.Add( static_cast<unsigned int>(row) );
3521 }
3522 m_clientArea->SetSelections( selection );
3523 }
3524
3525 void wxDataViewCtrl::Select( int row )
3526 {
3527 if( row >= 0 )
3528 {
3529 if (m_clientArea->IsSingleSel())
3530 m_clientArea->SelectAllRows(false);
3531 m_clientArea->SelectRow( row, true );
3532 }
3533 }
3534
3535 void wxDataViewCtrl::Unselect( int row )
3536 {
3537 if( row >= 0 )
3538 m_clientArea->SelectRow(row, false);
3539 }
3540
3541 bool wxDataViewCtrl::IsSelected( int row ) const
3542 {
3543 if( row >= 0 )
3544 return m_clientArea->IsRowSelected(row);
3545 return false;
3546 }
3547
3548 void wxDataViewCtrl::SelectRange( int from, int to )
3549 {
3550 wxArrayInt sel;
3551 for( int i = from; i < to; i ++ )
3552 sel.Add( i );
3553 m_clientArea->Select(sel);
3554 }
3555
3556 void wxDataViewCtrl::UnselectRange( int from, int to )
3557 {
3558 wxDataViewSelection sel = m_clientArea->GetSelections();
3559 for( int i = from; i < to; i ++ )
3560 if( sel.Index( i ) != wxNOT_FOUND )
3561 sel.Remove( i );
3562 m_clientArea->SetSelections(sel);
3563 }
3564
3565 void wxDataViewCtrl::SelectAll()
3566 {
3567 m_clientArea->SelectAllRows(true);
3568 }
3569
3570 void wxDataViewCtrl::UnselectAll()
3571 {
3572 m_clientArea->SelectAllRows(false);
3573 }
3574
3575 void wxDataViewCtrl::EnsureVisible( int row, int column )
3576 {
3577 if( row < 0 )
3578 row = 0;
3579 if( row > (int) m_clientArea->GetRowCount() )
3580 row = m_clientArea->GetRowCount();
3581
3582 int first = m_clientArea->GetFirstVisibleRow();
3583 int last = m_clientArea->GetLastVisibleRow();
3584 if( row < first )
3585 m_clientArea->ScrollTo( row, column );
3586 else if( row > last )
3587 m_clientArea->ScrollTo( row - last + first, column );
3588 else
3589 m_clientArea->ScrollTo( first, column );
3590 }
3591
3592 void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataViewColumn * column )
3593 {
3594 int row = m_clientArea->GetRowByItem(item);
3595 if( row >= 0 )
3596 {
3597 if( column == NULL )
3598 EnsureVisible(row, -1);
3599 else
3600 {
3601 int col = 0;
3602 int len = GetColumnCount();
3603 for( int i = 0; i < len; i ++ )
3604 if( GetColumn(i) == column )
3605 {
3606 col = i;
3607 break;
3608 }
3609 EnsureVisible( row, col );
3610 }
3611 }
3612
3613 }
3614
3615 void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column ) const
3616 {
3617 m_clientArea->HitTest(point, item, column);
3618 }
3619
3620 wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column ) const
3621 {
3622 return m_clientArea->GetItemRect(item, column);
3623 }
3624
3625 wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
3626 {
3627 return m_clientArea->GetItemByRow( row );
3628 }
3629
3630 int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
3631 {
3632 return m_clientArea->GetRowByItem( item );
3633 }
3634
3635 void wxDataViewCtrl::Expand( const wxDataViewItem & item )
3636 {
3637 int row = m_clientArea->GetRowByItem( item );
3638 if (row != -1)
3639 m_clientArea->Expand(row);
3640 }
3641
3642 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
3643 {
3644 int row = m_clientArea->GetRowByItem( item );
3645 if (row != -1)
3646 m_clientArea->Collapse(row);
3647 }
3648
3649 #endif
3650 // !wxUSE_GENERICDATAVIEWCTRL
3651
3652 #endif
3653 // wxUSE_DATAVIEWCTRL