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