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