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