Use flat generic status bar by default and add wxSB_SUNKEN.
[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 #include "wx/frame.h"
38 #endif
39
40 #include "wx/stockitem.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 #include "wx/dnd.h"
50 #include "wx/stopwatch.h"
51 #include "wx/weakref.h"
52
53 //-----------------------------------------------------------------------------
54 // classes
55 //-----------------------------------------------------------------------------
56
57 class wxDataViewColumn;
58 class wxDataViewHeaderWindow;
59 class wxDataViewCtrl;
60
61 //-----------------------------------------------------------------------------
62 // classes
63 //-----------------------------------------------------------------------------
64
65 static const int SCROLL_UNIT_X = 15;
66
67 // the cell padding on the left/right
68 static const int PADDING_RIGHTLEFT = 3;
69
70 // the expander space margin
71 static const int EXPANDER_MARGIN = 4;
72
73 #ifdef __WXMSW__
74 static const int EXPANDER_OFFSET = 4;
75 #else
76 static const int EXPANDER_OFFSET = 1;
77 #endif
78
79 // Below is the compare stuff.
80 // For the generic implementation, both the leaf nodes and the nodes are sorted for
81 // fast search when needed
82 static wxDataViewModel* g_model;
83
84 // The column is either the index of the column to be used for sorting or one
85 // of the special values in this enum:
86 enum
87 {
88 // Sort when we're thawed later.
89 SortColumn_OnThaw = -3,
90
91 // Don't sort at all.
92 SortColumn_None = -2,
93
94 // Sort using the model default sort order.
95 SortColumn_Default = -1
96 };
97
98 static int g_column = SortColumn_None;
99 static bool g_asending = true;
100
101 // ----------------------------------------------------------------------------
102 // helper functions
103 // ----------------------------------------------------------------------------
104
105 namespace
106 {
107
108 // Return the expander column or, if it is not set, the first column and also
109 // set it as the expander one for the future.
110 wxDataViewColumn* GetExpanderColumnOrFirstOne(wxDataViewCtrl* dataview)
111 {
112 wxDataViewColumn* expander = dataview->GetExpanderColumn();
113 if (!expander)
114 {
115 // TODO-RTL: last column for RTL support
116 expander = dataview->GetColumnAt( 0 );
117 dataview->SetExpanderColumn(expander);
118 }
119
120 return expander;
121 }
122
123 } // anonymous namespace
124
125 //-----------------------------------------------------------------------------
126 // wxDataViewColumn
127 //-----------------------------------------------------------------------------
128
129 void wxDataViewColumn::Init(int width, wxAlignment align, int flags)
130 {
131 m_width = width;
132 m_minWidth = 0;
133 m_align = align;
134 m_flags = flags;
135 m_sort = false;
136 m_sortAscending = true;
137 }
138
139 int wxDataViewColumn::GetWidth() const
140 {
141 switch ( m_width )
142 {
143 case wxCOL_WIDTH_DEFAULT:
144 return wxDVC_DEFAULT_WIDTH;
145
146 case wxCOL_WIDTH_AUTOSIZE:
147 wxCHECK_MSG( m_owner, wxDVC_DEFAULT_WIDTH, "no owner control" );
148 return m_owner->GetBestColumnWidth(m_owner->GetColumnIndex(this));
149
150 default:
151 return m_width;
152 }
153 }
154
155 void wxDataViewColumn::UpdateDisplay()
156 {
157 if (m_owner)
158 {
159 int idx = m_owner->GetColumnIndex( this );
160 m_owner->OnColumnChange( idx );
161 }
162 }
163
164 void wxDataViewColumn::SetSortOrder(bool ascending)
165 {
166 if ( !m_owner )
167 return;
168
169 // First unset the old sort column if any.
170 int oldSortKey = m_owner->GetSortingColumnIndex();
171 if ( oldSortKey != wxNOT_FOUND )
172 {
173 m_owner->GetColumn(oldSortKey)->UnsetAsSortKey();
174 }
175
176 // Now set this one as the new sort column.
177 const int idx = m_owner->GetColumnIndex(this);
178 m_owner->SetSortingColumnIndex(idx);
179
180 m_sort = true;
181 m_sortAscending = ascending;
182
183 // Call this directly instead of using UpdateDisplay() as we already have
184 // the column index, no need to look it up again.
185 m_owner->OnColumnChange(idx);
186 }
187
188 //-----------------------------------------------------------------------------
189 // wxDataViewHeaderWindow
190 //-----------------------------------------------------------------------------
191
192 class wxDataViewHeaderWindow : public wxHeaderCtrl
193 {
194 public:
195 wxDataViewHeaderWindow(wxDataViewCtrl *parent)
196 : wxHeaderCtrl(parent)
197 {
198 }
199
200 wxDataViewCtrl *GetOwner() const
201 { return static_cast<wxDataViewCtrl *>(GetParent()); }
202
203 protected:
204 // implement/override wxHeaderCtrl functions by forwarding them to the main
205 // control
206 virtual const wxHeaderColumn& GetColumn(unsigned int idx) const
207 {
208 return *(GetOwner()->GetColumn(idx));
209 }
210
211 virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
212 {
213 wxDataViewCtrl * const owner = GetOwner();
214
215 int widthContents = owner->GetBestColumnWidth(idx);
216 owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents));
217 owner->OnColumnChange(idx);
218
219 return true;
220 }
221
222 private:
223 bool SendEvent(wxEventType type, unsigned int n)
224 {
225 wxDataViewCtrl * const owner = GetOwner();
226 wxDataViewEvent event(type, owner->GetId());
227
228 event.SetEventObject(owner);
229 event.SetColumn(n);
230 event.SetDataViewColumn(owner->GetColumn(n));
231 event.SetModel(owner->GetModel());
232
233 // for events created by wxDataViewHeaderWindow the
234 // row / value fields are not valid
235 return owner->ProcessWindowEvent(event);
236 }
237
238 void OnClick(wxHeaderCtrlEvent& event)
239 {
240 const unsigned idx = event.GetColumn();
241
242 if ( SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, idx) )
243 return;
244
245 // default handling for the column click is to sort by this column or
246 // toggle its sort order
247 wxDataViewCtrl * const owner = GetOwner();
248 wxDataViewColumn * const col = owner->GetColumn(idx);
249 if ( !col->IsSortable() )
250 {
251 // no default handling for non-sortable columns
252 event.Skip();
253 return;
254 }
255
256 if ( col->IsSortKey() )
257 {
258 // already using this column for sorting, just change the order
259 col->ToggleSortOrder();
260 }
261 else // not using this column for sorting yet
262 {
263 col->SetSortOrder(true);
264 }
265
266 wxDataViewModel * const model = owner->GetModel();
267 if ( model )
268 model->Resort();
269
270 owner->OnColumnChange(idx);
271
272 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, idx);
273 }
274
275 void OnRClick(wxHeaderCtrlEvent& event)
276 {
277 if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
278 event.GetColumn()) )
279 event.Skip();
280 }
281
282 void OnResize(wxHeaderCtrlEvent& event)
283 {
284 wxDataViewCtrl * const owner = GetOwner();
285
286 const unsigned col = event.GetColumn();
287 owner->GetColumn(col)->SetWidth(event.GetWidth());
288 GetOwner()->OnColumnChange(col);
289 }
290
291 void OnEndReorder(wxHeaderCtrlEvent& event)
292 {
293 wxDataViewCtrl * const owner = GetOwner();
294 owner->ColumnMoved(owner->GetColumn(event.GetColumn()),
295 event.GetNewOrder());
296 }
297
298 DECLARE_EVENT_TABLE()
299 wxDECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow);
300 };
301
302 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
303 EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick)
304 EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
305
306 EVT_HEADER_RESIZING(wxID_ANY, wxDataViewHeaderWindow::OnResize)
307 EVT_HEADER_END_RESIZE(wxID_ANY, wxDataViewHeaderWindow::OnResize)
308
309 EVT_HEADER_END_REORDER(wxID_ANY, wxDataViewHeaderWindow::OnEndReorder)
310 END_EVENT_TABLE()
311
312 //-----------------------------------------------------------------------------
313 // wxDataViewRenameTimer
314 //-----------------------------------------------------------------------------
315
316 class wxDataViewRenameTimer: public wxTimer
317 {
318 private:
319 wxDataViewMainWindow *m_owner;
320
321 public:
322 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
323 void Notify();
324 };
325
326 //-----------------------------------------------------------------------------
327 // wxDataViewTreeNode
328 //-----------------------------------------------------------------------------
329
330 class wxDataViewTreeNode;
331 WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
332
333 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
334 wxDataViewTreeNode ** node2);
335
336 class wxDataViewTreeNode
337 {
338 public:
339 wxDataViewTreeNode(wxDataViewTreeNode *parent, const wxDataViewItem& item)
340 : m_parent(parent),
341 m_item(item),
342 m_branchData(NULL)
343 {
344 }
345
346 ~wxDataViewTreeNode()
347 {
348 if ( m_branchData )
349 {
350 wxDataViewTreeNodes& nodes = m_branchData->children;
351 for ( wxDataViewTreeNodes::iterator i = nodes.begin();
352 i != nodes.end();
353 ++i )
354 {
355 delete *i;
356 }
357
358 delete m_branchData;
359 }
360 }
361
362 static wxDataViewTreeNode* CreateRootNode()
363 {
364 wxDataViewTreeNode *n = new wxDataViewTreeNode(NULL, wxDataViewItem());
365 n->m_branchData = new BranchNodeData;
366 n->m_branchData->open = true;
367 return n;
368 }
369
370 wxDataViewTreeNode * GetParent() const { return m_parent; }
371
372 const wxDataViewTreeNodes& GetChildNodes() const
373 {
374 wxASSERT( m_branchData != NULL );
375 return m_branchData->children;
376 }
377
378 void InsertChild(wxDataViewTreeNode *node, unsigned index)
379 {
380 if ( !m_branchData )
381 m_branchData = new BranchNodeData;
382
383 m_branchData->children.Insert(node, index);
384
385 // TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n)
386 if (g_column >= -1)
387 m_branchData->children.Sort( &wxGenericTreeModelNodeCmp );
388 }
389
390 void RemoveChild(wxDataViewTreeNode *node)
391 {
392 wxCHECK_RET( m_branchData != NULL, "leaf node doesn't have children" );
393 m_branchData->children.Remove(node);
394 }
395
396 // returns position of child node for given item in children list or wxNOT_FOUND
397 int FindChildByItem(const wxDataViewItem& item) const
398 {
399 if ( !m_branchData )
400 return wxNOT_FOUND;
401
402 const wxDataViewTreeNodes& nodes = m_branchData->children;
403 const int len = nodes.size();
404 for ( int i = 0; i < len; i++ )
405 {
406 if ( nodes[i]->m_item == item )
407 return i;
408 }
409 return wxNOT_FOUND;
410 }
411
412 const wxDataViewItem & GetItem() const { return m_item; }
413 void SetItem( const wxDataViewItem & item ) { m_item = item; }
414
415 int GetIndentLevel() const
416 {
417 int ret = 0;
418 const wxDataViewTreeNode * node = this;
419 while( node->GetParent()->GetParent() != NULL )
420 {
421 node = node->GetParent();
422 ret ++;
423 }
424 return ret;
425 }
426
427 bool IsOpen() const
428 {
429 return m_branchData && m_branchData->open;
430 }
431
432 void ToggleOpen()
433 {
434 // We do not allow the (invisible) root node to be collapsed because
435 // there is no way to expand it again.
436 if ( !m_parent )
437 return;
438
439 wxCHECK_RET( m_branchData != NULL, "can't open leaf node" );
440
441 int sum = 0;
442
443 const wxDataViewTreeNodes& nodes = m_branchData->children;
444 const int len = nodes.GetCount();
445 for ( int i = 0;i < len; i ++)
446 sum += 1 + nodes[i]->GetSubTreeCount();
447
448 if (m_branchData->open)
449 {
450 ChangeSubTreeCount(-sum);
451 m_branchData->open = !m_branchData->open;
452 }
453 else
454 {
455 m_branchData->open = !m_branchData->open;
456 ChangeSubTreeCount(+sum);
457 }
458 }
459
460 // "HasChildren" property corresponds to model's IsContainer(). Note that it may be true
461 // even if GetChildNodes() is empty; see below.
462 bool HasChildren() const
463 {
464 return m_branchData != NULL;
465 }
466
467 void SetHasChildren(bool has)
468 {
469 // The invisible root item always has children, so ignore any attempts
470 // to change this.
471 if ( !m_parent )
472 return;
473
474 if ( !has )
475 {
476 wxDELETE(m_branchData);
477 }
478 else if ( m_branchData == NULL )
479 {
480 m_branchData = new BranchNodeData;
481 }
482 }
483
484 int GetSubTreeCount() const
485 {
486 return m_branchData ? m_branchData->subTreeCount : 0;
487 }
488
489 void ChangeSubTreeCount( int num )
490 {
491 wxASSERT( m_branchData != NULL );
492
493 if( !m_branchData->open )
494 return;
495
496 m_branchData->subTreeCount += num;
497 wxASSERT( m_branchData->subTreeCount >= 0 );
498
499 if( m_parent )
500 m_parent->ChangeSubTreeCount(num);
501 }
502
503 void Resort()
504 {
505 if ( !m_branchData )
506 return;
507
508 if (g_column >= -1)
509 {
510 wxDataViewTreeNodes& nodes = m_branchData->children;
511
512 nodes.Sort( &wxGenericTreeModelNodeCmp );
513 int len = nodes.GetCount();
514 for (int i = 0; i < len; i ++)
515 {
516 if ( nodes[i]->HasChildren() )
517 nodes[i]->Resort();
518 }
519 }
520 }
521
522
523 private:
524 wxDataViewTreeNode *m_parent;
525
526 // Corresponding model item.
527 wxDataViewItem m_item;
528
529 // Data specific to non-leaf (branch, inner) nodes. They are kept in a
530 // separate struct in order to conserve memory.
531 struct BranchNodeData
532 {
533 BranchNodeData()
534 : open(false),
535 subTreeCount(0)
536 {
537 }
538
539 // Child nodes. Note that this may be empty even if m_hasChildren in
540 // case this branch of the tree wasn't expanded and realized yet.
541 wxDataViewTreeNodes children;
542
543 // Is the branch node currently open (expanded)?
544 bool open;
545
546 // Total count of expanded (i.e. visible with the help of some
547 // scrolling) items in the subtree, but excluding this node. I.e. it is
548 // 0 for leaves and is the number of rows the subtree occupies for
549 // branch nodes.
550 int subTreeCount;
551 };
552
553 BranchNodeData *m_branchData;
554 };
555
556
557 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
558 wxDataViewTreeNode ** node2)
559 {
560 return g_model->Compare( (*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending );
561 }
562
563
564 //-----------------------------------------------------------------------------
565 // wxDataViewMainWindow
566 //-----------------------------------------------------------------------------
567
568 WX_DEFINE_SORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection);
569
570 class wxDataViewMainWindow: public wxWindow
571 {
572 public:
573 wxDataViewMainWindow( wxDataViewCtrl *parent,
574 wxWindowID id,
575 const wxPoint &pos = wxDefaultPosition,
576 const wxSize &size = wxDefaultSize,
577 const wxString &name = wxT("wxdataviewctrlmainwindow") );
578 virtual ~wxDataViewMainWindow();
579
580 bool IsList() const { return GetModel()->IsListModel(); }
581 bool IsVirtualList() const { return m_root == NULL; }
582
583 // notifications from wxDataViewModel
584 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
585 bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item );
586 bool ItemChanged( const wxDataViewItem &item );
587 bool ValueChanged( const wxDataViewItem &item, unsigned int model_column );
588 bool Cleared();
589 void Resort()
590 {
591 if (!IsVirtualList())
592 {
593 SortPrepare();
594 m_root->Resort();
595 }
596 UpdateDisplay();
597 }
598
599 // Override the base class method to resort if needed, i.e. if
600 // SortPrepare() was called -- and ignored -- while we were frozen.
601 virtual void DoThaw()
602 {
603 if ( g_column == SortColumn_OnThaw )
604 {
605 Resort();
606 g_column = SortColumn_None;
607 }
608
609 wxWindow::DoThaw();
610 }
611
612 void SortPrepare()
613 {
614 g_model = GetModel();
615
616 // Avoid sorting while the window is frozen, this allows to quickly add
617 // many items without resorting after each addition and only resort
618 // them all at once when the window is finally thawed, see above.
619 if ( IsFrozen() )
620 {
621 g_column = SortColumn_OnThaw;
622 return;
623 }
624
625 wxDataViewColumn* col = GetOwner()->GetSortingColumn();
626 if( !col )
627 {
628 if (g_model->HasDefaultCompare())
629 g_column = SortColumn_Default;
630 else
631 g_column = SortColumn_None;
632
633 g_asending = true;
634 return;
635 }
636 g_column = col->GetModelColumn();
637 g_asending = col->IsSortOrderAscending();
638 }
639
640 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
641 wxDataViewCtrl *GetOwner() { return m_owner; }
642 const wxDataViewCtrl *GetOwner() const { return m_owner; }
643
644 wxDataViewModel* GetModel() { return GetOwner()->GetModel(); }
645 const wxDataViewModel* GetModel() const { return GetOwner()->GetModel(); }
646
647 #if wxUSE_DRAG_AND_DROP
648 wxBitmap CreateItemBitmap( unsigned int row, int &indent );
649 #endif // wxUSE_DRAG_AND_DROP
650 void OnPaint( wxPaintEvent &event );
651 void OnCharHook( wxKeyEvent &event );
652 void OnChar( wxKeyEvent &event );
653 void OnVerticalNavigation(int delta, const wxKeyEvent& event);
654 void OnLeftKey();
655 void OnRightKey();
656 void OnMouse( wxMouseEvent &event );
657 void OnSetFocus( wxFocusEvent &event );
658 void OnKillFocus( wxFocusEvent &event );
659
660 void UpdateDisplay();
661 void RecalculateDisplay();
662 void OnInternalIdle();
663
664 void OnRenameTimer();
665
666 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
667 void ScrollTo( int rows, int column );
668
669 unsigned GetCurrentRow() const { return m_currentRow; }
670 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
671 void ChangeCurrentRow( unsigned int row );
672 bool TryAdvanceCurrentColumn(wxDataViewTreeNode *node, bool forward);
673
674 wxDataViewColumn *GetCurrentColumn() const { return m_currentCol; }
675 void ClearCurrentColumn() { m_currentCol = NULL; }
676
677 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
678 bool IsEmpty() { return GetRowCount() == 0; }
679
680 int GetCountPerPage() const;
681 int GetEndOfLastCol() const;
682 unsigned int GetFirstVisibleRow() const;
683
684 // I change this method to un const because in the tree view,
685 // the displaying number of the tree are changing along with the
686 // expanding/collapsing of the tree nodes
687 unsigned int GetLastVisibleRow();
688 unsigned int GetRowCount() const;
689
690 const wxDataViewSelection& GetSelections() const { return m_selection; }
691 void SetSelections( const wxDataViewSelection & sel )
692 { m_selection = sel; UpdateDisplay(); }
693 void Select( const wxArrayInt& aSelections );
694 void SelectAllRows( bool on );
695 void SelectRow( unsigned int row, bool on );
696 void SelectRows( unsigned int from, unsigned int to, bool on );
697 void ReverseRowSelection( unsigned int row );
698 bool IsRowSelected( unsigned int row );
699 void SendSelectionChangedEvent( const wxDataViewItem& item);
700
701 void RefreshRow( unsigned int row );
702 void RefreshRows( unsigned int from, unsigned int to );
703 void RefreshRowsAfter( unsigned int firstRow );
704
705 // returns the colour to be used for drawing the rules
706 wxColour GetRuleColour() const
707 {
708 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
709 }
710
711 wxRect GetLineRect( unsigned int row ) const;
712
713 int GetLineStart( unsigned int row ) const; // row * m_lineHeight in fixed mode
714 int GetLineHeight( unsigned int row ) const; // m_lineHeight in fixed mode
715 int GetLineAt( unsigned int y ) const; // y / m_lineHeight in fixed mode
716
717 void SetRowHeight( int lineHeight ) { m_lineHeight = lineHeight; }
718 int GetRowHeight() const { return m_lineHeight; }
719 int GetDefaultRowHeight() const;
720
721 // Some useful functions for row and item mapping
722 wxDataViewItem GetItemByRow( unsigned int row ) const;
723 int GetRowByItem( const wxDataViewItem & item ) const;
724
725 wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row ) const;
726 // We did not need this temporarily
727 // wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
728
729 // Methods for building the mapping tree
730 void BuildTree( wxDataViewModel * model );
731 void DestroyTree();
732 void HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column );
733 wxRect GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column );
734
735 void Expand( unsigned int row );
736 void Collapse( unsigned int row );
737 bool IsExpanded( unsigned int row ) const;
738 bool HasChildren( unsigned int row ) const;
739
740 #if wxUSE_DRAG_AND_DROP
741 bool EnableDragSource( const wxDataFormat &format );
742 bool EnableDropTarget( const wxDataFormat &format );
743
744 void RemoveDropHint();
745 wxDragResult OnDragOver( wxDataFormat format, wxCoord x, wxCoord y, wxDragResult def );
746 bool OnDrop( wxDataFormat format, wxCoord x, wxCoord y );
747 wxDragResult OnData( wxDataFormat format, wxCoord x, wxCoord y, wxDragResult def );
748 void OnLeave();
749 #endif // wxUSE_DRAG_AND_DROP
750
751 void OnColumnsCountChanged();
752
753 // Called by wxDataViewCtrl and our own OnRenameTimer() to start edit the
754 // specified item in the given column.
755 void StartEditing(const wxDataViewItem& item, const wxDataViewColumn* col);
756
757 private:
758 int RecalculateCount() const;
759
760 // Return false only if the event was vetoed by its handler.
761 bool SendExpanderEvent(wxEventType type, const wxDataViewItem& item);
762
763 wxDataViewTreeNode * FindNode( const wxDataViewItem & item );
764
765 wxDataViewColumn *FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode);
766
767 bool IsCellEditableInMode(const wxDataViewItem& item, const wxDataViewColumn *col, wxDataViewCellMode mode) const;
768
769 void DrawCellBackground( wxDataViewRenderer* cell, wxDC& dc, const wxRect& rect );
770
771 private:
772 wxDataViewCtrl *m_owner;
773 int m_lineHeight;
774 bool m_dirty;
775
776 wxDataViewColumn *m_currentCol;
777 unsigned int m_currentRow;
778 wxDataViewSelection m_selection;
779
780 wxDataViewRenameTimer *m_renameTimer;
781 bool m_lastOnSame;
782
783 bool m_hasFocus;
784 bool m_useCellFocus;
785 bool m_currentColSetByKeyboard;
786
787 #if wxUSE_DRAG_AND_DROP
788 int m_dragCount;
789 wxPoint m_dragStart;
790
791 bool m_dragEnabled;
792 wxDataFormat m_dragFormat;
793
794 bool m_dropEnabled;
795 wxDataFormat m_dropFormat;
796 bool m_dropHint;
797 unsigned int m_dropHintLine;
798 #endif // wxUSE_DRAG_AND_DROP
799
800 // for double click logic
801 unsigned int m_lineLastClicked,
802 m_lineBeforeLastClicked,
803 m_lineSelectSingleOnUp;
804
805 // the pen used to draw horiz/vertical rules
806 wxPen m_penRule;
807
808 // the pen used to draw the expander and the lines
809 wxPen m_penExpander;
810
811 // This is the tree structure of the model
812 wxDataViewTreeNode * m_root;
813 int m_count;
814
815 // This is the tree node under the cursor
816 wxDataViewTreeNode * m_underMouse;
817
818 // The control used for editing or NULL.
819 wxWeakRef<wxWindow> m_editorCtrl;
820
821 // Id m_editorCtrl is non-NULL, pointer to the associated renderer.
822 wxDataViewRenderer* m_editorRenderer;
823
824 private:
825 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
826 DECLARE_EVENT_TABLE()
827 };
828
829 // ---------------------------------------------------------
830 // wxGenericDataViewModelNotifier
831 // ---------------------------------------------------------
832
833 class wxGenericDataViewModelNotifier: public wxDataViewModelNotifier
834 {
835 public:
836 wxGenericDataViewModelNotifier( wxDataViewMainWindow *mainWindow )
837 { m_mainWindow = mainWindow; }
838
839 virtual bool ItemAdded( const wxDataViewItem & parent, const wxDataViewItem & item )
840 { return m_mainWindow->ItemAdded( parent , item ); }
841 virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
842 { return m_mainWindow->ItemDeleted( parent, item ); }
843 virtual bool ItemChanged( const wxDataViewItem & item )
844 { return m_mainWindow->ItemChanged(item); }
845 virtual bool ValueChanged( const wxDataViewItem & item , unsigned int col )
846 { return m_mainWindow->ValueChanged( item, col ); }
847 virtual bool Cleared()
848 { return m_mainWindow->Cleared(); }
849 virtual void Resort()
850 { m_mainWindow->Resort(); }
851
852 wxDataViewMainWindow *m_mainWindow;
853 };
854
855 // ---------------------------------------------------------
856 // wxDataViewRenderer
857 // ---------------------------------------------------------
858
859 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
860
861 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
862 wxDataViewCellMode mode,
863 int align) :
864 wxDataViewCustomRendererBase( varianttype, mode, align )
865 {
866 m_align = align;
867 m_mode = mode;
868 m_ellipsizeMode = wxELLIPSIZE_MIDDLE;
869 m_dc = NULL;
870 }
871
872 wxDataViewRenderer::~wxDataViewRenderer()
873 {
874 delete m_dc;
875 }
876
877 wxDC *wxDataViewRenderer::GetDC()
878 {
879 if (m_dc == NULL)
880 {
881 if (GetOwner() == NULL)
882 return NULL;
883 if (GetOwner()->GetOwner() == NULL)
884 return NULL;
885 m_dc = new wxClientDC( GetOwner()->GetOwner() );
886 }
887
888 return m_dc;
889 }
890
891 void wxDataViewRenderer::SetAlignment( int align )
892 {
893 m_align=align;
894 }
895
896 int wxDataViewRenderer::GetAlignment() const
897 {
898 return m_align;
899 }
900
901 // ---------------------------------------------------------
902 // wxDataViewCustomRenderer
903 // ---------------------------------------------------------
904
905 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
906
907 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
908 wxDataViewCellMode mode, int align ) :
909 wxDataViewRenderer( varianttype, mode, align )
910 {
911 }
912
913 // ---------------------------------------------------------
914 // wxDataViewTextRenderer
915 // ---------------------------------------------------------
916
917 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewRenderer)
918
919 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
920 wxDataViewCellMode mode, int align ) :
921 wxDataViewRenderer( varianttype, mode, align )
922 {
923 }
924
925 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
926 {
927 m_text = value.GetString();
928
929 return true;
930 }
931
932 bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
933 {
934 return false;
935 }
936
937 bool wxDataViewTextRenderer::HasEditorCtrl() const
938 {
939 return true;
940 }
941
942 wxWindow* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
943 wxRect labelRect, const wxVariant &value )
944 {
945 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, value,
946 wxPoint(labelRect.x,labelRect.y),
947 wxSize(labelRect.width,labelRect.height),
948 wxTE_PROCESS_ENTER );
949
950 // select the text in the control an place the cursor at the end
951 ctrl->SetInsertionPointEnd();
952 ctrl->SelectAll();
953
954 return ctrl;
955 }
956
957 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant &value )
958 {
959 wxTextCtrl *text = (wxTextCtrl*) editor;
960 value = text->GetValue();
961 return true;
962 }
963
964 bool wxDataViewTextRenderer::Render(wxRect rect, wxDC *dc, int state)
965 {
966 RenderText(m_text, 0, rect, dc, state);
967 return true;
968 }
969
970 wxSize wxDataViewTextRenderer::GetSize() const
971 {
972 if (!m_text.empty())
973 return GetTextExtent(m_text);
974 else
975 return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE);
976 }
977
978 // ---------------------------------------------------------
979 // wxDataViewBitmapRenderer
980 // ---------------------------------------------------------
981
982 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewRenderer)
983
984 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
985 wxDataViewCellMode mode, int align ) :
986 wxDataViewRenderer( varianttype, mode, align )
987 {
988 }
989
990 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
991 {
992 if (value.GetType() == wxT("wxBitmap"))
993 m_bitmap << value;
994 if (value.GetType() == wxT("wxIcon"))
995 m_icon << value;
996
997 return true;
998 }
999
1000 bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
1001 {
1002 return false;
1003 }
1004
1005 bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
1006 {
1007 if (m_bitmap.IsOk())
1008 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
1009 else if (m_icon.IsOk())
1010 dc->DrawIcon( m_icon, cell.x, cell.y );
1011
1012 return true;
1013 }
1014
1015 wxSize wxDataViewBitmapRenderer::GetSize() const
1016 {
1017 if (m_bitmap.IsOk())
1018 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
1019 else if (m_icon.IsOk())
1020 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
1021
1022 return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE);
1023 }
1024
1025 // ---------------------------------------------------------
1026 // wxDataViewToggleRenderer
1027 // ---------------------------------------------------------
1028
1029 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewRenderer)
1030
1031 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
1032 wxDataViewCellMode mode, int align ) :
1033 wxDataViewRenderer( varianttype, mode, align )
1034 {
1035 m_toggle = false;
1036 }
1037
1038 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
1039 {
1040 m_toggle = value.GetBool();
1041
1042 return true;
1043 }
1044
1045 bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
1046 {
1047 return false;
1048 }
1049
1050 bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
1051 {
1052 int flags = 0;
1053 if (m_toggle)
1054 flags |= wxCONTROL_CHECKED;
1055 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE ||
1056 GetEnabled() == false)
1057 flags |= wxCONTROL_DISABLED;
1058
1059 // Ensure that the check boxes always have at least the minimal required
1060 // size, otherwise DrawCheckBox() doesn't really work well. If this size is
1061 // greater than the cell size, the checkbox will be truncated but this is a
1062 // lesser evil.
1063 wxSize size = cell.GetSize();
1064 size.IncTo(GetSize());
1065 cell.SetSize(size);
1066
1067 wxRendererNative::Get().DrawCheckBox(
1068 GetOwner()->GetOwner(),
1069 *dc,
1070 cell,
1071 flags );
1072
1073 return true;
1074 }
1075
1076 bool wxDataViewToggleRenderer::WXActivateCell(const wxRect& WXUNUSED(cell),
1077 wxDataViewModel *model,
1078 const wxDataViewItem& item,
1079 unsigned int col,
1080 const wxMouseEvent *mouseEvent)
1081 {
1082 if ( mouseEvent )
1083 {
1084 // only react to clicks directly on the checkbox, not elsewhere in the same cell:
1085 if ( !wxRect(GetSize()).Contains(mouseEvent->GetPosition()) )
1086 return false;
1087 }
1088
1089 model->ChangeValue(!m_toggle, item, col);
1090 return true;
1091 }
1092
1093 wxSize wxDataViewToggleRenderer::GetSize() const
1094 {
1095 // the window parameter is not used by GetCheckBoxSize() so it's
1096 // safe to pass NULL
1097 return wxRendererNative::Get().GetCheckBoxSize(NULL);
1098 }
1099
1100 // ---------------------------------------------------------
1101 // wxDataViewProgressRenderer
1102 // ---------------------------------------------------------
1103
1104 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewRenderer)
1105
1106 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
1107 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
1108 wxDataViewRenderer( varianttype, mode, align )
1109 {
1110 m_label = label;
1111 m_value = 0;
1112 }
1113
1114 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
1115 {
1116 m_value = (long) value;
1117
1118 if (m_value < 0) m_value = 0;
1119 if (m_value > 100) m_value = 100;
1120
1121 return true;
1122 }
1123
1124 bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
1125 {
1126 value = (long) m_value;
1127 return true;
1128 }
1129
1130 bool
1131 wxDataViewProgressRenderer::Render(wxRect rect, wxDC *dc, int WXUNUSED(state))
1132 {
1133 // deflate the rect to leave a small border between bars in adjacent rows
1134 wxRect bar = rect.Deflate(0, 1);
1135
1136 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1137 dc->SetPen( *wxBLACK_PEN );
1138 dc->DrawRectangle( bar );
1139
1140 bar.width = (int)(bar.width * m_value / 100.);
1141 dc->SetPen( *wxTRANSPARENT_PEN );
1142
1143 const wxDataViewItemAttr& attr = GetAttr();
1144 dc->SetBrush( attr.HasColour() ? wxBrush(attr.GetColour())
1145 : *wxBLUE_BRUSH );
1146 dc->DrawRectangle( bar );
1147
1148 return true;
1149 }
1150
1151 wxSize wxDataViewProgressRenderer::GetSize() const
1152 {
1153 return wxSize(40,12);
1154 }
1155
1156 // ---------------------------------------------------------
1157 // wxDataViewIconTextRenderer
1158 // ---------------------------------------------------------
1159
1160 IMPLEMENT_CLASS(wxDataViewIconTextRenderer, wxDataViewRenderer)
1161
1162 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(
1163 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
1164 wxDataViewRenderer( varianttype, mode, align )
1165 {
1166 SetMode(mode);
1167 SetAlignment(align);
1168 }
1169
1170 bool wxDataViewIconTextRenderer::SetValue( const wxVariant &value )
1171 {
1172 m_value << value;
1173 return true;
1174 }
1175
1176 bool wxDataViewIconTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
1177 {
1178 return false;
1179 }
1180
1181 bool wxDataViewIconTextRenderer::Render(wxRect rect, wxDC *dc, int state)
1182 {
1183 int xoffset = 0;
1184
1185 const wxIcon& icon = m_value.GetIcon();
1186 if ( icon.IsOk() )
1187 {
1188 dc->DrawIcon(icon, rect.x, rect.y + (rect.height - icon.GetHeight())/2);
1189 xoffset = icon.GetWidth()+4;
1190 }
1191
1192 RenderText(m_value.GetText(), xoffset, rect, dc, state);
1193
1194 return true;
1195 }
1196
1197 wxSize wxDataViewIconTextRenderer::GetSize() const
1198 {
1199 if (!m_value.GetText().empty())
1200 {
1201 wxSize size = GetTextExtent(m_value.GetText());
1202
1203 if (m_value.GetIcon().IsOk())
1204 size.x += m_value.GetIcon().GetWidth() + 4;
1205 return size;
1206 }
1207 return wxSize(80,20);
1208 }
1209
1210 wxWindow* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow *parent, wxRect labelRect, const wxVariant& value)
1211 {
1212 wxDataViewIconText iconText;
1213 iconText << value;
1214
1215 wxString text = iconText.GetText();
1216
1217 // adjust the label rect to take the width of the icon into account
1218 if (iconText.GetIcon().IsOk())
1219 {
1220 int w = iconText.GetIcon().GetWidth() + 4;
1221 labelRect.x += w;
1222 labelRect.width -= w;
1223 }
1224
1225 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, text,
1226 wxPoint(labelRect.x,labelRect.y),
1227 wxSize(labelRect.width,labelRect.height),
1228 wxTE_PROCESS_ENTER );
1229
1230 // select the text in the control an place the cursor at the end
1231 ctrl->SetInsertionPointEnd();
1232 ctrl->SelectAll();
1233
1234 return ctrl;
1235 }
1236
1237 bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant& value )
1238 {
1239 wxTextCtrl *text = (wxTextCtrl*) editor;
1240
1241 // The icon can't be edited so get its old value and reuse it.
1242 wxVariant valueOld;
1243 wxDataViewColumn* const col = GetOwner();
1244 GetView()->GetModel()->GetValue(valueOld, m_item, col->GetModelColumn());
1245
1246 wxDataViewIconText iconText;
1247 iconText << valueOld;
1248
1249 // But replace the text with the value entered by user.
1250 iconText.SetText(text->GetValue());
1251
1252 value << iconText;
1253 return true;
1254 }
1255
1256 //-----------------------------------------------------------------------------
1257 // wxDataViewDropTarget
1258 //-----------------------------------------------------------------------------
1259
1260 #if wxUSE_DRAG_AND_DROP
1261
1262 class wxBitmapCanvas: public wxWindow
1263 {
1264 public:
1265 wxBitmapCanvas( wxWindow *parent, const wxBitmap &bitmap, const wxSize &size ) :
1266 wxWindow( parent, wxID_ANY, wxPoint(0,0), size )
1267 {
1268 m_bitmap = bitmap;
1269 Connect( wxEVT_PAINT, wxPaintEventHandler(wxBitmapCanvas::OnPaint) );
1270 }
1271
1272 void OnPaint( wxPaintEvent &WXUNUSED(event) )
1273 {
1274 wxPaintDC dc(this);
1275 dc.DrawBitmap( m_bitmap, 0, 0);
1276 }
1277
1278 wxBitmap m_bitmap;
1279 };
1280
1281 class wxDataViewDropSource: public wxDropSource
1282 {
1283 public:
1284 wxDataViewDropSource( wxDataViewMainWindow *win, unsigned int row ) :
1285 wxDropSource( win )
1286 {
1287 m_win = win;
1288 m_row = row;
1289 m_hint = NULL;
1290 }
1291
1292 ~wxDataViewDropSource()
1293 {
1294 delete m_hint;
1295 }
1296
1297 virtual bool GiveFeedback( wxDragResult WXUNUSED(effect) )
1298 {
1299 wxPoint pos = wxGetMousePosition();
1300
1301 if (!m_hint)
1302 {
1303 int liney = m_win->GetLineStart( m_row );
1304 int linex = 0;
1305 m_win->GetOwner()->CalcUnscrolledPosition( 0, liney, NULL, &liney );
1306 m_win->ClientToScreen( &linex, &liney );
1307 m_dist_x = pos.x - linex;
1308 m_dist_y = pos.y - liney;
1309
1310 int indent = 0;
1311 wxBitmap ib = m_win->CreateItemBitmap( m_row, indent );
1312 m_dist_x -= indent;
1313 m_hint = new wxFrame( m_win->GetParent(), wxID_ANY, wxEmptyString,
1314 wxPoint(pos.x - m_dist_x, pos.y + 5 ),
1315 ib.GetSize(),
1316 wxFRAME_TOOL_WINDOW |
1317 wxFRAME_FLOAT_ON_PARENT |
1318 wxFRAME_NO_TASKBAR |
1319 wxNO_BORDER );
1320 new wxBitmapCanvas( m_hint, ib, ib.GetSize() );
1321 m_hint->Show();
1322 }
1323 else
1324 {
1325 m_hint->Move( pos.x - m_dist_x, pos.y + 5 );
1326 m_hint->SetTransparent( 128 );
1327 }
1328
1329 return false;
1330 }
1331
1332 wxDataViewMainWindow *m_win;
1333 unsigned int m_row;
1334 wxFrame *m_hint;
1335 int m_dist_x,m_dist_y;
1336 };
1337
1338
1339 class wxDataViewDropTarget: public wxDropTarget
1340 {
1341 public:
1342 wxDataViewDropTarget( wxDataObject *obj, wxDataViewMainWindow *win ) :
1343 wxDropTarget( obj )
1344 {
1345 m_win = win;
1346 }
1347
1348 virtual wxDragResult OnDragOver( wxCoord x, wxCoord y, wxDragResult def )
1349 {
1350 wxDataFormat format = GetMatchingPair();
1351 if (format == wxDF_INVALID)
1352 return wxDragNone;
1353 return m_win->OnDragOver( format, x, y, def);
1354 }
1355
1356 virtual bool OnDrop( wxCoord x, wxCoord y )
1357 {
1358 wxDataFormat format = GetMatchingPair();
1359 if (format == wxDF_INVALID)
1360 return false;
1361 return m_win->OnDrop( format, x, y );
1362 }
1363
1364 virtual wxDragResult OnData( wxCoord x, wxCoord y, wxDragResult def )
1365 {
1366 wxDataFormat format = GetMatchingPair();
1367 if (format == wxDF_INVALID)
1368 return wxDragNone;
1369 if (!GetData())
1370 return wxDragNone;
1371 return m_win->OnData( format, x, y, def );
1372 }
1373
1374 virtual void OnLeave()
1375 { m_win->OnLeave(); }
1376
1377 wxDataViewMainWindow *m_win;
1378 };
1379
1380 #endif // wxUSE_DRAG_AND_DROP
1381
1382 //-----------------------------------------------------------------------------
1383 // wxDataViewRenameTimer
1384 //-----------------------------------------------------------------------------
1385
1386 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1387 {
1388 m_owner = owner;
1389 }
1390
1391 void wxDataViewRenameTimer::Notify()
1392 {
1393 m_owner->OnRenameTimer();
1394 }
1395
1396 //-----------------------------------------------------------------------------
1397 // wxDataViewMainWindow
1398 //-----------------------------------------------------------------------------
1399
1400 // The tree building helper, declared firstly
1401 static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
1402 wxDataViewTreeNode * node);
1403
1404 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
1405 {
1406 if (row1 > row2) return 1;
1407 if (row1 == row2) return 0;
1408 return -1;
1409 }
1410
1411 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1412
1413 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1414 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1415 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1416 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
1417 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1418 EVT_CHAR_HOOK (wxDataViewMainWindow::OnCharHook)
1419 EVT_CHAR (wxDataViewMainWindow::OnChar)
1420 END_EVENT_TABLE()
1421
1422 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1423 const wxPoint &pos, const wxSize &size, const wxString &name ) :
1424 wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE, name ),
1425 m_selection( wxDataViewSelectionCmp )
1426
1427 {
1428 SetOwner( parent );
1429
1430 m_editorRenderer = NULL;
1431
1432 m_lastOnSame = false;
1433 m_renameTimer = new wxDataViewRenameTimer( this );
1434
1435 // TODO: user better initial values/nothing selected
1436 m_currentCol = NULL;
1437 m_currentColSetByKeyboard = false;
1438 m_useCellFocus = false;
1439 m_currentRow = (unsigned)-1;
1440 m_lineHeight = GetDefaultRowHeight();
1441
1442 #if wxUSE_DRAG_AND_DROP
1443 m_dragCount = 0;
1444 m_dragStart = wxPoint(0,0);
1445
1446 m_dragEnabled = false;
1447 m_dropEnabled = false;
1448 m_dropHint = false;
1449 m_dropHintLine = (unsigned int) -1;
1450 #endif // wxUSE_DRAG_AND_DROP
1451
1452 m_lineLastClicked = (unsigned int) -1;
1453 m_lineBeforeLastClicked = (unsigned int) -1;
1454 m_lineSelectSingleOnUp = (unsigned int) -1;
1455
1456 m_hasFocus = false;
1457
1458 SetBackgroundColour( *wxWHITE );
1459
1460 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
1461
1462 m_penRule = wxPen(GetRuleColour());
1463
1464 // compose a pen whichcan draw black lines
1465 // TODO: maybe there is something system colour to use
1466 m_penExpander = wxPen(wxColour(0,0,0));
1467
1468 m_root = wxDataViewTreeNode::CreateRootNode();
1469
1470 // Make m_count = -1 will cause the class recaculate the real displaying number of rows.
1471 m_count = -1;
1472 m_underMouse = NULL;
1473 UpdateDisplay();
1474 }
1475
1476 wxDataViewMainWindow::~wxDataViewMainWindow()
1477 {
1478 DestroyTree();
1479 delete m_renameTimer;
1480 }
1481
1482
1483 int wxDataViewMainWindow::GetDefaultRowHeight() const
1484 {
1485 #ifdef __WXMSW__
1486 // We would like to use the same line height that Explorer uses. This is
1487 // different from standard ListView control since Vista.
1488 if ( wxGetWinVersion() >= wxWinVersion_Vista )
1489 return wxMax(16, GetCharHeight()) + 6; // 16 = mini icon height
1490 else
1491 #endif // __WXMSW__
1492 return wxMax(16, GetCharHeight()) + 1; // 16 = mini icon height
1493 }
1494
1495
1496
1497 #if wxUSE_DRAG_AND_DROP
1498 bool wxDataViewMainWindow::EnableDragSource( const wxDataFormat &format )
1499 {
1500 m_dragFormat = format;
1501 m_dragEnabled = format != wxDF_INVALID;
1502
1503 return true;
1504 }
1505
1506 bool wxDataViewMainWindow::EnableDropTarget( const wxDataFormat &format )
1507 {
1508 m_dropFormat = format;
1509 m_dropEnabled = format != wxDF_INVALID;
1510
1511 if (m_dropEnabled)
1512 SetDropTarget( new wxDataViewDropTarget( new wxCustomDataObject( format ), this ) );
1513
1514 return true;
1515 }
1516
1517 void wxDataViewMainWindow::RemoveDropHint()
1518 {
1519 if (m_dropHint)
1520 {
1521 m_dropHint = false;
1522 RefreshRow( m_dropHintLine );
1523 m_dropHintLine = (unsigned int) -1;
1524 }
1525 }
1526
1527 wxDragResult wxDataViewMainWindow::OnDragOver( wxDataFormat format, wxCoord x,
1528 wxCoord y, wxDragResult def )
1529 {
1530 int xx = x;
1531 int yy = y;
1532 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1533 unsigned int row = GetLineAt( yy );
1534
1535 if ((row >= GetRowCount()) || (xx > GetEndOfLastCol()))
1536 {
1537 RemoveDropHint();
1538 return wxDragNone;
1539 }
1540
1541 wxDataViewItem item = GetItemByRow( row );
1542
1543 wxDataViewModel *model = GetModel();
1544
1545 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
1546 event.SetEventObject( m_owner );
1547 event.SetItem( item );
1548 event.SetModel( model );
1549 event.SetDataFormat( format );
1550 event.SetDropEffect( def );
1551 if (!m_owner->HandleWindowEvent( event ))
1552 {
1553 RemoveDropHint();
1554 return wxDragNone;
1555 }
1556
1557 if (!event.IsAllowed())
1558 {
1559 RemoveDropHint();
1560 return wxDragNone;
1561 }
1562
1563
1564 if (m_dropHint && (row != m_dropHintLine))
1565 RefreshRow( m_dropHintLine );
1566 m_dropHint = true;
1567 m_dropHintLine = row;
1568 RefreshRow( row );
1569
1570 return def;
1571 }
1572
1573 bool wxDataViewMainWindow::OnDrop( wxDataFormat format, wxCoord x, wxCoord y )
1574 {
1575 RemoveDropHint();
1576
1577 int xx = x;
1578 int yy = y;
1579 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1580 unsigned int row = GetLineAt( yy );
1581
1582 if ((row >= GetRowCount()) || (xx > GetEndOfLastCol()))
1583 return false;
1584
1585 wxDataViewItem item = GetItemByRow( row );
1586
1587 wxDataViewModel *model = GetModel();
1588
1589 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
1590 event.SetEventObject( m_owner );
1591 event.SetItem( item );
1592 event.SetModel( model );
1593 event.SetDataFormat( format );
1594 if (!m_owner->HandleWindowEvent( event ))
1595 return false;
1596
1597 if (!event.IsAllowed())
1598 return false;
1599
1600 return true;
1601 }
1602
1603 wxDragResult wxDataViewMainWindow::OnData( wxDataFormat format, wxCoord x, wxCoord y,
1604 wxDragResult def )
1605 {
1606 int xx = x;
1607 int yy = y;
1608 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1609 unsigned int row = GetLineAt( yy );
1610
1611 if ((row >= GetRowCount()) || (xx > GetEndOfLastCol()))
1612 return wxDragNone;
1613
1614 wxDataViewItem item = GetItemByRow( row );
1615
1616 wxDataViewModel *model = GetModel();
1617
1618 wxCustomDataObject *obj = (wxCustomDataObject *) GetDropTarget()->GetDataObject();
1619
1620 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, m_owner->GetId() );
1621 event.SetEventObject( m_owner );
1622 event.SetItem( item );
1623 event.SetModel( model );
1624 event.SetDataFormat( format );
1625 event.SetDataSize( obj->GetSize() );
1626 event.SetDataBuffer( obj->GetData() );
1627 event.SetDropEffect( def );
1628 if (!m_owner->HandleWindowEvent( event ))
1629 return wxDragNone;
1630
1631 if (!event.IsAllowed())
1632 return wxDragNone;
1633
1634 return def;
1635 }
1636
1637 void wxDataViewMainWindow::OnLeave()
1638 {
1639 RemoveDropHint();
1640 }
1641
1642 wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent )
1643 {
1644 int height = GetLineHeight( row );
1645 int width = 0;
1646 unsigned int cols = GetOwner()->GetColumnCount();
1647 unsigned int col;
1648 for (col = 0; col < cols; col++)
1649 {
1650 wxDataViewColumn *column = GetOwner()->GetColumnAt(col);
1651 if (column->IsHidden())
1652 continue; // skip it!
1653 width += column->GetWidth();
1654 }
1655
1656 indent = 0;
1657 if (!IsList())
1658 {
1659 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
1660 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
1661 indent = indent + m_lineHeight;
1662 // try to use the m_lineHeight as the expander space
1663 }
1664 width -= indent;
1665
1666 wxBitmap bitmap( width, height );
1667 wxMemoryDC dc( bitmap );
1668 dc.SetFont( GetFont() );
1669 dc.SetPen( *wxBLACK_PEN );
1670 dc.SetBrush( *wxWHITE_BRUSH );
1671 dc.DrawRectangle( 0,0,width,height );
1672
1673 wxDataViewModel *model = m_owner->GetModel();
1674
1675 wxDataViewColumn * const
1676 expander = GetExpanderColumnOrFirstOne(GetOwner());
1677
1678 int x = 0;
1679 for (col = 0; col < cols; col++)
1680 {
1681 wxDataViewColumn *column = GetOwner()->GetColumnAt( col );
1682 wxDataViewRenderer *cell = column->GetRenderer();
1683
1684 if (column->IsHidden())
1685 continue; // skip it!
1686
1687 width = column->GetWidth();
1688
1689 if (column == expander)
1690 width -= indent;
1691
1692 wxDataViewItem item = GetItemByRow( row );
1693 cell->PrepareForItem(model, item, column->GetModelColumn());
1694
1695 wxRect item_rect(x, 0, width, height);
1696 item_rect.Deflate(PADDING_RIGHTLEFT, 0);
1697
1698 // dc.SetClippingRegion( item_rect );
1699 cell->WXCallRender(item_rect, &dc, 0);
1700 // dc.DestroyClippingRegion();
1701
1702 x += width;
1703 }
1704
1705 return bitmap;
1706 }
1707
1708 #endif // wxUSE_DRAG_AND_DROP
1709
1710
1711 // Draw focus rect for individual cell. Unlike native focus rect, we render
1712 // this in foreground text color (typically white) to enhance contrast and
1713 // make it visible.
1714 static void DrawSelectedCellFocusRect(wxDC& dc, const wxRect& rect)
1715 {
1716 // (This code is based on wxRendererGeneric::DrawFocusRect and modified.)
1717
1718 // draw the pixels manually because the "dots" in wxPen with wxDOT style
1719 // may be short traits and not really dots
1720 //
1721 // note that to behave in the same manner as DrawRect(), we must exclude
1722 // the bottom and right borders from the rectangle
1723 wxCoord x1 = rect.GetLeft(),
1724 y1 = rect.GetTop(),
1725 x2 = rect.GetRight(),
1726 y2 = rect.GetBottom();
1727
1728 wxDCPenChanger pen(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
1729
1730 wxCoord z;
1731 for ( z = x1 + 1; z < x2; z += 2 )
1732 dc.DrawPoint(z, rect.GetTop());
1733
1734 wxCoord shift = z == x2 ? 0 : 1;
1735 for ( z = y1 + shift; z < y2; z += 2 )
1736 dc.DrawPoint(x2, z);
1737
1738 shift = z == y2 ? 0 : 1;
1739 for ( z = x2 - shift; z > x1; z -= 2 )
1740 dc.DrawPoint(z, y2);
1741
1742 shift = z == x1 ? 0 : 1;
1743 for ( z = y2 - shift; z > y1; z -= 2 )
1744 dc.DrawPoint(x1, z);
1745 }
1746
1747
1748 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1749 {
1750 wxDataViewModel *model = GetModel();
1751 wxAutoBufferedPaintDC dc( this );
1752
1753 dc.SetBrush(GetOwner()->GetBackgroundColour());
1754 dc.SetPen( *wxTRANSPARENT_PEN );
1755 dc.DrawRectangle(GetClientSize());
1756
1757 if ( IsEmpty() )
1758 {
1759 // No items to draw.
1760 return;
1761 }
1762
1763 // prepare the DC
1764 GetOwner()->PrepareDC( dc );
1765 dc.SetFont( GetFont() );
1766
1767 wxRect update = GetUpdateRegion().GetBox();
1768 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1769
1770 // compute which items needs to be redrawn
1771 unsigned int item_start = GetLineAt( wxMax(0,update.y) );
1772 unsigned int item_count =
1773 wxMin( (int)( GetLineAt( wxMax(0,update.y+update.height) ) - item_start + 1),
1774 (int)(GetRowCount( ) - item_start));
1775 unsigned int item_last = item_start + item_count;
1776
1777 // Send the event to wxDataViewCtrl itself.
1778 wxWindow * const parent = GetParent();
1779 wxDataViewEvent cache_event(wxEVT_COMMAND_DATAVIEW_CACHE_HINT, parent->GetId());
1780 cache_event.SetEventObject(parent);
1781 cache_event.SetCache(item_start, item_last - 1);
1782 parent->ProcessWindowEvent(cache_event);
1783
1784 // compute which columns needs to be redrawn
1785 unsigned int cols = GetOwner()->GetColumnCount();
1786 if ( !cols )
1787 {
1788 // we assume that we have at least one column below and painting an
1789 // empty control is unnecessary anyhow
1790 return;
1791 }
1792
1793 unsigned int col_start = 0;
1794 unsigned int x_start;
1795 for (x_start = 0; col_start < cols; col_start++)
1796 {
1797 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_start);
1798 if (col->IsHidden())
1799 continue; // skip it!
1800
1801 unsigned int w = col->GetWidth();
1802 if (x_start+w >= (unsigned int)update.x)
1803 break;
1804
1805 x_start += w;
1806 }
1807
1808 unsigned int col_last = col_start;
1809 unsigned int x_last = x_start;
1810 for (; col_last < cols; col_last++)
1811 {
1812 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_last);
1813 if (col->IsHidden())
1814 continue; // skip it!
1815
1816 if (x_last > (unsigned int)update.GetRight())
1817 break;
1818
1819 x_last += col->GetWidth();
1820 }
1821
1822 // Draw background of alternate rows specially if required
1823 if ( m_owner->HasFlag(wxDV_ROW_LINES) )
1824 {
1825 wxColour altRowColour = m_owner->m_alternateRowColour;
1826 if ( !altRowColour.IsOk() )
1827 {
1828 // Determine the alternate rows colour automatically from the
1829 // background colour.
1830 const wxColour bgColour = m_owner->GetBackgroundColour();
1831
1832 // Depending on the background, alternate row color
1833 // will be 3% more dark or 50% brighter.
1834 int alpha = bgColour.GetRGB() > 0x808080 ? 97 : 150;
1835 altRowColour = bgColour.ChangeLightness(alpha);
1836 }
1837
1838 dc.SetPen(*wxTRANSPARENT_PEN);
1839 dc.SetBrush(wxBrush(altRowColour));
1840
1841 for (unsigned int item = item_start; item < item_last; item++)
1842 {
1843 if ( item % 2 )
1844 {
1845 dc.DrawRectangle(x_start,
1846 GetLineStart(item),
1847 GetClientSize().GetWidth(),
1848 GetLineHeight(item));
1849 }
1850 }
1851 }
1852
1853 // Draw horizontal rules if required
1854 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1855 {
1856 dc.SetPen(m_penRule);
1857 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1858
1859 for (unsigned int i = item_start; i <= item_last; i++)
1860 {
1861 int y = GetLineStart( i );
1862 dc.DrawLine(x_start, y, x_last, y);
1863 }
1864 }
1865
1866 // Draw vertical rules if required
1867 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1868 {
1869 dc.SetPen(m_penRule);
1870 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1871
1872 // NB: Vertical rules are drawn in the last pixel of a column so that
1873 // they align perfectly with native MSW wxHeaderCtrl as well as for
1874 // consistency with MSW native list control. There's no vertical
1875 // rule at the most-left side of the control.
1876
1877 int x = x_start - 1;
1878 for (unsigned int i = col_start; i < col_last; i++)
1879 {
1880 wxDataViewColumn *col = GetOwner()->GetColumnAt(i);
1881 if (col->IsHidden())
1882 continue; // skip it
1883
1884 x += col->GetWidth();
1885
1886 dc.DrawLine(x, GetLineStart( item_start ),
1887 x, GetLineStart( item_last ) );
1888 }
1889 }
1890
1891 // redraw the background for the items which are selected/current
1892 for (unsigned int item = item_start; item < item_last; item++)
1893 {
1894 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1895
1896 if (selected || item == m_currentRow)
1897 {
1898 wxRect rect( x_start, GetLineStart( item ),
1899 x_last - x_start, GetLineHeight( item ) );
1900
1901 // draw selection and whole-item focus:
1902 if ( selected )
1903 {
1904 int flags = wxCONTROL_SELECTED;
1905 if (m_hasFocus)
1906 flags |= wxCONTROL_FOCUSED;
1907
1908 wxRendererNative::Get().DrawItemSelectionRect
1909 (
1910 this,
1911 dc,
1912 rect,
1913 flags
1914 );
1915 }
1916
1917 // draw keyboard focus rect if applicable
1918 if ( item == m_currentRow && m_hasFocus )
1919 {
1920 bool renderColumnFocus = false;
1921
1922 if ( m_useCellFocus && m_currentCol && m_currentColSetByKeyboard )
1923 {
1924 renderColumnFocus = true;
1925
1926 // If this is container node without columns, render full-row focus:
1927 if ( !IsList() )
1928 {
1929 wxDataViewTreeNode *node = GetTreeNodeByRow(item);
1930 if ( node->HasChildren() && !model->HasContainerColumns(node->GetItem()) )
1931 renderColumnFocus = false;
1932 }
1933 }
1934
1935 if ( renderColumnFocus )
1936 {
1937 for ( unsigned int i = col_start; i < col_last; i++ )
1938 {
1939 wxDataViewColumn *col = GetOwner()->GetColumnAt(i);
1940 if ( col->IsHidden() )
1941 continue;
1942
1943 rect.width = col->GetWidth();
1944
1945 if ( col == m_currentCol )
1946 {
1947 // make the rect more visible by adding a small
1948 // margin around it:
1949 rect.Deflate(1, 1);
1950
1951 if ( selected )
1952 {
1953 // DrawFocusRect() uses XOR and is all but
1954 // invisible against dark-blue background. Use
1955 // the same color used for selected text.
1956 DrawSelectedCellFocusRect(dc, rect);
1957 }
1958 else
1959 {
1960 wxRendererNative::Get().DrawFocusRect
1961 (
1962 this,
1963 dc,
1964 rect,
1965 0
1966 );
1967 }
1968 break;
1969 }
1970
1971 rect.x += rect.width;
1972 }
1973 }
1974 else
1975 {
1976 // render focus rectangle for the whole row
1977 wxRendererNative::Get().DrawFocusRect
1978 (
1979 this,
1980 dc,
1981 rect,
1982 selected ? (int)wxCONTROL_SELECTED : 0
1983 );
1984 }
1985 }
1986 }
1987 }
1988
1989 #if wxUSE_DRAG_AND_DROP
1990 if (m_dropHint)
1991 {
1992 wxRect rect( x_start, GetLineStart( m_dropHintLine ),
1993 x_last - x_start, GetLineHeight( m_dropHintLine ) );
1994 dc.SetPen( *wxBLACK_PEN );
1995 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1996 dc.DrawRectangle( rect );
1997 }
1998 #endif // wxUSE_DRAG_AND_DROP
1999
2000 wxDataViewColumn * const
2001 expander = GetExpanderColumnOrFirstOne(GetOwner());
2002
2003 // redraw all cells for all rows which must be repainted and all columns
2004 wxRect cell_rect;
2005 cell_rect.x = x_start;
2006 for (unsigned int i = col_start; i < col_last; i++)
2007 {
2008 wxDataViewColumn *col = GetOwner()->GetColumnAt( i );
2009 wxDataViewRenderer *cell = col->GetRenderer();
2010 cell_rect.width = col->GetWidth();
2011
2012 if ( col->IsHidden() || cell_rect.width <= 0 )
2013 continue; // skip it!
2014
2015 for (unsigned int item = item_start; item < item_last; item++)
2016 {
2017 // get the cell value and set it into the renderer
2018 wxDataViewTreeNode *node = NULL;
2019 wxDataViewItem dataitem;
2020
2021 if (!IsVirtualList())
2022 {
2023 node = GetTreeNodeByRow(item);
2024 if( node == NULL )
2025 continue;
2026
2027 dataitem = node->GetItem();
2028
2029 // Skip all columns of "container" rows except the expander
2030 // column itself unless HasContainerColumns() overrides this.
2031 if ( col != expander &&
2032 model->IsContainer(dataitem) &&
2033 !model->HasContainerColumns(dataitem) )
2034 continue;
2035 }
2036 else
2037 {
2038 dataitem = wxDataViewItem( wxUIntToPtr(item+1) );
2039 }
2040
2041 cell->PrepareForItem(model, dataitem, col->GetModelColumn());
2042
2043 // update cell_rect
2044 cell_rect.y = GetLineStart( item );
2045 cell_rect.height = GetLineHeight( item );
2046
2047 // draw the background
2048 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
2049 if ( !selected )
2050 DrawCellBackground( cell, dc, cell_rect );
2051
2052 // deal with the expander
2053 int indent = 0;
2054 if ((!IsList()) && (col == expander))
2055 {
2056 // Calculate the indent first
2057 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
2058
2059 // we reserve m_lineHeight of horizontal space for the expander
2060 // but leave EXPANDER_MARGIN around the expander itself
2061 int exp_x = cell_rect.x + indent + EXPANDER_MARGIN;
2062
2063 indent += m_lineHeight;
2064
2065 // draw expander if needed and visible
2066 if ( node->HasChildren() && exp_x < cell_rect.GetRight() )
2067 {
2068 dc.SetPen( m_penExpander );
2069 dc.SetBrush( wxNullBrush );
2070
2071 int exp_size = m_lineHeight - 2*EXPANDER_MARGIN;
2072 int exp_y = cell_rect.y + (cell_rect.height - exp_size)/2
2073 + EXPANDER_MARGIN - EXPANDER_OFFSET;
2074
2075 const wxRect rect(exp_x, exp_y, exp_size, exp_size);
2076
2077 int flag = 0;
2078 if ( m_underMouse == node )
2079 flag |= wxCONTROL_CURRENT;
2080 if ( node->IsOpen() )
2081 flag |= wxCONTROL_EXPANDED;
2082
2083 // ensure that we don't overflow the cell (which might
2084 // happen if the column is very narrow)
2085 wxDCClipper clip(dc, cell_rect);
2086
2087 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, flag);
2088 }
2089
2090 // force the expander column to left-center align
2091 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
2092 }
2093
2094 wxRect item_rect = cell_rect;
2095 item_rect.Deflate(PADDING_RIGHTLEFT, 0);
2096
2097 // account for the tree indent (harmless if we're not indented)
2098 item_rect.x += indent;
2099 item_rect.width -= indent;
2100
2101 if ( item_rect.width <= 0 )
2102 continue;
2103
2104 int state = 0;
2105 if (m_hasFocus && selected)
2106 state |= wxDATAVIEW_CELL_SELECTED;
2107
2108 // TODO: it would be much more efficient to create a clipping
2109 // region for the entire column being rendered (in the OnPaint
2110 // of wxDataViewMainWindow) instead of a single clip region for
2111 // each cell. However it would mean that each renderer should
2112 // respect the given wxRect's top & bottom coords, eventually
2113 // violating only the left & right coords - however the user can
2114 // make its own renderer and thus we cannot be sure of that.
2115 wxDCClipper clip(dc, item_rect);
2116
2117 cell->WXCallRender(item_rect, &dc, state);
2118 }
2119
2120 cell_rect.x += cell_rect.width;
2121 }
2122 }
2123
2124
2125 void wxDataViewMainWindow::DrawCellBackground( wxDataViewRenderer* cell, wxDC& dc, const wxRect& rect )
2126 {
2127 wxRect rectBg( rect );
2128
2129 // don't overlap the horizontal rules
2130 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
2131 {
2132 rectBg.x++;
2133 rectBg.width--;
2134 }
2135
2136 // don't overlap the vertical rules
2137 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
2138 {
2139 rectBg.y++;
2140 rectBg.height--;
2141 }
2142
2143 cell->RenderBackground(&dc, rectBg);
2144 }
2145
2146 void wxDataViewMainWindow::OnRenameTimer()
2147 {
2148 // We have to call this here because changes may just have
2149 // been made and no screen update taken place.
2150 if ( m_dirty )
2151 {
2152 // TODO: use wxTheApp->SafeYieldFor(NULL, wxEVT_CATEGORY_UI) instead
2153 // (needs to be tested!)
2154 wxSafeYield();
2155 }
2156
2157 wxDataViewItem item = GetItemByRow( m_currentRow );
2158
2159 StartEditing( item, m_currentCol );
2160 }
2161
2162 void
2163 wxDataViewMainWindow::StartEditing(const wxDataViewItem& item,
2164 const wxDataViewColumn* col)
2165 {
2166 wxDataViewRenderer* renderer = col->GetRenderer();
2167 if ( !IsCellEditableInMode(item, col, wxDATAVIEW_CELL_EDITABLE) )
2168 return;
2169
2170 const wxRect itemRect = GetItemRect(item, col);
2171 if ( renderer->StartEditing(item, itemRect) )
2172 {
2173 // Save the renderer to be able to finish/cancel editing it later and
2174 // save the control to be able to detect if we're still editing it.
2175 m_editorRenderer = renderer;
2176 m_editorCtrl = renderer->GetEditorCtrl();
2177 }
2178 }
2179
2180 //-----------------------------------------------------------------------------
2181 // Helper class for do operation on the tree node
2182 //-----------------------------------------------------------------------------
2183 class DoJob
2184 {
2185 public:
2186 DoJob() { }
2187 virtual ~DoJob() { }
2188
2189 // The return value control how the tree-walker tranverse the tree
2190 enum
2191 {
2192 DONE, // Job done, stop traversing and return
2193 SKIP_SUBTREE, // Ignore the current node's subtree and continue
2194 CONTINUE // Job not done, continue
2195 };
2196
2197 virtual int operator() ( wxDataViewTreeNode * node ) = 0;
2198 };
2199
2200 bool Walker( wxDataViewTreeNode * node, DoJob & func )
2201 {
2202 wxCHECK_MSG( node, false, "can't walk NULL node" );
2203
2204 switch( func( node ) )
2205 {
2206 case DoJob::DONE:
2207 return true;
2208 case DoJob::SKIP_SUBTREE:
2209 return false;
2210 case DoJob::CONTINUE:
2211 break;
2212 }
2213
2214 if ( node->HasChildren() )
2215 {
2216 const wxDataViewTreeNodes& nodes = node->GetChildNodes();
2217
2218 for ( wxDataViewTreeNodes::const_iterator i = nodes.begin();
2219 i != nodes.end();
2220 ++i )
2221 {
2222 if ( Walker(*i, func) )
2223 return true;
2224 }
2225 }
2226
2227 return false;
2228 }
2229
2230 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
2231 {
2232 if (IsVirtualList())
2233 {
2234 wxDataViewVirtualListModel *list_model =
2235 (wxDataViewVirtualListModel*) GetModel();
2236 m_count = list_model->GetCount();
2237 }
2238 else
2239 {
2240 SortPrepare();
2241
2242 wxDataViewTreeNode *parentNode = FindNode(parent);
2243
2244 if ( !parentNode )
2245 return false;
2246
2247 wxDataViewItemArray modelSiblings;
2248 GetModel()->GetChildren(parent, modelSiblings);
2249 const int modelSiblingsSize = modelSiblings.size();
2250
2251 int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
2252 wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
2253
2254 wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item);
2255 itemNode->SetHasChildren(GetModel()->IsContainer(item));
2256
2257 parentNode->SetHasChildren(true);
2258
2259 const wxDataViewTreeNodes& nodeSiblings = parentNode->GetChildNodes();
2260 const int nodeSiblingsSize = nodeSiblings.size();
2261
2262 int nodePos = 0;
2263
2264 if ( posInModel == modelSiblingsSize - 1 )
2265 {
2266 nodePos = nodeSiblingsSize;
2267 }
2268 else if ( modelSiblingsSize == nodeSiblingsSize + 1 )
2269 {
2270 // This is the simple case when our node tree already matches the
2271 // model and only this one item is missing.
2272 nodePos = posInModel;
2273 }
2274 else
2275 {
2276 // It's possible that a larger discrepancy between the model and
2277 // our realization exists. This can happen e.g. when adding a bunch
2278 // of items to the model and then calling ItemsAdded() just once
2279 // afterwards. In this case, we must find the right position by
2280 // looking at sibling items.
2281
2282 // append to the end if we won't find a better position:
2283 nodePos = nodeSiblingsSize;
2284
2285 for ( int nextItemPos = posInModel + 1;
2286 nextItemPos < modelSiblingsSize;
2287 nextItemPos++ )
2288 {
2289 int nextNodePos = parentNode->FindChildByItem(modelSiblings[nextItemPos]);
2290 if ( nextNodePos != wxNOT_FOUND )
2291 {
2292 nodePos = nextNodePos;
2293 break;
2294 }
2295 }
2296 }
2297
2298 parentNode->ChangeSubTreeCount(+1);
2299 parentNode->InsertChild(itemNode, nodePos);
2300
2301 m_count = -1;
2302 }
2303
2304 GetOwner()->InvalidateColBestWidths();
2305 UpdateDisplay();
2306
2307 return true;
2308 }
2309
2310 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
2311 const wxDataViewItem& item)
2312 {
2313 if (IsVirtualList())
2314 {
2315 wxDataViewVirtualListModel *list_model =
2316 (wxDataViewVirtualListModel*) GetModel();
2317 m_count = list_model->GetCount();
2318
2319 if ( !m_selection.empty() )
2320 {
2321 const int row = GetRowByItem(item);
2322
2323 int rowIndexInSelection = wxNOT_FOUND;
2324
2325 const size_t selCount = m_selection.size();
2326 for ( size_t i = 0; i < selCount; i++ )
2327 {
2328 if ( m_selection[i] == (unsigned)row )
2329 rowIndexInSelection = i;
2330 else if ( m_selection[i] > (unsigned)row )
2331 m_selection[i]--;
2332 }
2333
2334 if ( rowIndexInSelection != wxNOT_FOUND )
2335 m_selection.RemoveAt(rowIndexInSelection);
2336 }
2337
2338 }
2339 else // general case
2340 {
2341 wxDataViewTreeNode *parentNode = FindNode(parent);
2342
2343 // Notice that it is possible that the item being deleted is not in the
2344 // tree at all, for example we could be deleting a never shown (because
2345 // collapsed) item in a tree model. So it's not an error if we don't know
2346 // about this item, just return without doing anything then.
2347 if ( !parentNode )
2348 return true;
2349
2350 wxCHECK_MSG( parentNode->HasChildren(), false, "parent node doesn't have children?" );
2351 const wxDataViewTreeNodes& parentsChildren = parentNode->GetChildNodes();
2352
2353 // We can't use FindNode() to find 'item', because it was already
2354 // removed from the model by the time ItemDeleted() is called, so we
2355 // have to do it manually. We keep track of its position as well for
2356 // later use.
2357 int itemPosInNode = 0;
2358 wxDataViewTreeNode *itemNode = NULL;
2359 for ( wxDataViewTreeNodes::const_iterator i = parentsChildren.begin();
2360 i != parentsChildren.end();
2361 ++i, ++itemPosInNode )
2362 {
2363 if( (*i)->GetItem() == item )
2364 {
2365 itemNode = *i;
2366 break;
2367 }
2368 }
2369
2370 // If the parent wasn't expanded, it's possible that we didn't have a
2371 // node corresponding to 'item' and so there's nothing left to do.
2372 if ( !itemNode )
2373 {
2374 // If this was the last child to be removed, it's possible the parent
2375 // node became a leaf. Let's ask the model about it.
2376 if ( parentNode->GetChildNodes().empty() )
2377 parentNode->SetHasChildren(GetModel()->IsContainer(parent));
2378
2379 return true;
2380 }
2381
2382 // Delete the item from wxDataViewTreeNode representation:
2383 const int itemsDeleted = 1 + itemNode->GetSubTreeCount();
2384
2385 parentNode->RemoveChild(itemNode);
2386 delete itemNode;
2387 parentNode->ChangeSubTreeCount(-itemsDeleted);
2388
2389 // Make the row number invalid and get a new valid one when user call GetRowCount
2390 m_count = -1;
2391
2392 // If this was the last child to be removed, it's possible the parent
2393 // node became a leaf. Let's ask the model about it.
2394 if ( parentNode->GetChildNodes().empty() )
2395 {
2396 bool isContainer = GetModel()->IsContainer(parent);
2397 parentNode->SetHasChildren(isContainer);
2398 if ( isContainer )
2399 {
2400 // If it's still a container, make sure we show "+" icon for it
2401 // and not "-" one as there is nothing to collapse any more.
2402 if ( parentNode->IsOpen() )
2403 parentNode->ToggleOpen();
2404 }
2405 }
2406
2407 // Update selection by removing 'item' and its entire children tree from the selection.
2408 if ( !m_selection.empty() )
2409 {
2410 // we can't call GetRowByItem() on 'item', as it's already deleted, so compute it from
2411 // the parent ('parentNode') and position in its list of children
2412 int itemRow;
2413 if ( itemPosInNode == 0 )
2414 {
2415 // 1st child, row number is that of the parent parentNode + 1
2416 itemRow = GetRowByItem(parentNode->GetItem()) + 1;
2417 }
2418 else
2419 {
2420 // row number is that of the sibling above 'item' + its subtree if any + 1
2421 const wxDataViewTreeNode *siblingNode = parentNode->GetChildNodes()[itemPosInNode - 1];
2422
2423 itemRow = GetRowByItem(siblingNode->GetItem()) +
2424 siblingNode->GetSubTreeCount() +
2425 1;
2426 }
2427
2428 wxDataViewSelection newsel(wxDataViewSelectionCmp);
2429
2430 const size_t numSelections = m_selection.size();
2431 for ( size_t i = 0; i < numSelections; ++i )
2432 {
2433 const int s = m_selection[i];
2434 if ( s < itemRow )
2435 newsel.push_back(s);
2436 else if ( s >= itemRow + itemsDeleted )
2437 newsel.push_back(s - itemsDeleted);
2438 // else: deleted item, remove from selection
2439 }
2440
2441 m_selection = newsel;
2442 }
2443 }
2444
2445 // Change the current row to the last row if the current exceed the max row number
2446 if ( m_currentRow >= GetRowCount() )
2447 ChangeCurrentRow(m_count - 1);
2448
2449 GetOwner()->InvalidateColBestWidths();
2450 UpdateDisplay();
2451
2452 return true;
2453 }
2454
2455 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
2456 {
2457 SortPrepare();
2458 g_model->Resort();
2459
2460 GetOwner()->InvalidateColBestWidths();
2461
2462 // Send event
2463 wxWindow *parent = GetParent();
2464 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
2465 le.SetEventObject(parent);
2466 le.SetModel(GetModel());
2467 le.SetItem(item);
2468 parent->ProcessWindowEvent(le);
2469
2470 return true;
2471 }
2472
2473 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int model_column )
2474 {
2475 int view_column = -1;
2476 unsigned int n_col = m_owner->GetColumnCount();
2477 for (unsigned i = 0; i < n_col; i++)
2478 {
2479 wxDataViewColumn *column = m_owner->GetColumn( i );
2480 if (column->GetModelColumn() == model_column)
2481 {
2482 view_column = (int) i;
2483 break;
2484 }
2485 }
2486 if (view_column == -1)
2487 return false;
2488
2489 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
2490 /*#define MAX_VIRTUAL_WIDTH 100000
2491
2492 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
2493 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2494 Refresh( true, &rect );
2495
2496 return true;
2497 */
2498 SortPrepare();
2499 g_model->Resort();
2500
2501 GetOwner()->InvalidateColBestWidth(view_column);
2502
2503 // Send event
2504 wxWindow *parent = GetParent();
2505 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
2506 le.SetEventObject(parent);
2507 le.SetModel(GetModel());
2508 le.SetItem(item);
2509 le.SetColumn(view_column);
2510 le.SetDataViewColumn(GetOwner()->GetColumn(view_column));
2511 parent->ProcessWindowEvent(le);
2512
2513 return true;
2514 }
2515
2516 bool wxDataViewMainWindow::Cleared()
2517 {
2518 DestroyTree();
2519 m_selection.Clear();
2520 m_currentRow = (unsigned)-1;
2521
2522 if (GetModel())
2523 {
2524 SortPrepare();
2525 BuildTree( GetModel() );
2526 }
2527 else
2528 {
2529 m_count = 0;
2530 }
2531
2532 GetOwner()->InvalidateColBestWidths();
2533 UpdateDisplay();
2534
2535 return true;
2536 }
2537
2538 void wxDataViewMainWindow::UpdateDisplay()
2539 {
2540 m_dirty = true;
2541 m_underMouse = NULL;
2542 }
2543
2544 void wxDataViewMainWindow::OnInternalIdle()
2545 {
2546 wxWindow::OnInternalIdle();
2547
2548 if (m_dirty)
2549 {
2550 RecalculateDisplay();
2551 m_dirty = false;
2552 }
2553 }
2554
2555 void wxDataViewMainWindow::RecalculateDisplay()
2556 {
2557 wxDataViewModel *model = GetModel();
2558 if (!model)
2559 {
2560 Refresh();
2561 return;
2562 }
2563
2564 int width = GetEndOfLastCol();
2565 int height = GetLineStart( GetRowCount() );
2566
2567 SetVirtualSize( width, height );
2568 GetOwner()->SetScrollRate( 10, m_lineHeight );
2569
2570 Refresh();
2571 }
2572
2573 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
2574 {
2575 m_underMouse = NULL;
2576
2577 wxWindow::ScrollWindow( dx, dy, rect );
2578
2579 if (GetOwner()->m_headerArea)
2580 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
2581 }
2582
2583 void wxDataViewMainWindow::ScrollTo( int rows, int column )
2584 {
2585 m_underMouse = NULL;
2586
2587 int x, y;
2588 m_owner->GetScrollPixelsPerUnit( &x, &y );
2589 int sy = GetLineStart( rows )/y;
2590 int sx = -1;
2591 if( column != -1 )
2592 {
2593 wxRect rect = GetClientRect();
2594 int colnum = 0;
2595 int x_start, w = 0;
2596 int xx, yy, xe;
2597 m_owner->CalcUnscrolledPosition( rect.x, rect.y, &xx, &yy );
2598 for (x_start = 0; colnum < column; colnum++)
2599 {
2600 wxDataViewColumn *col = GetOwner()->GetColumnAt(colnum);
2601 if (col->IsHidden())
2602 continue; // skip it!
2603
2604 w = col->GetWidth();
2605 x_start += w;
2606 }
2607
2608 int x_end = x_start + w;
2609 xe = xx + rect.width;
2610 if( x_end > xe )
2611 {
2612 sx = ( xx + x_end - xe )/x;
2613 }
2614 if( x_start < xx )
2615 {
2616 sx = x_start/x;
2617 }
2618 }
2619 m_owner->Scroll( sx, sy );
2620 }
2621
2622 int wxDataViewMainWindow::GetCountPerPage() const
2623 {
2624 wxSize size = GetClientSize();
2625 return size.y / m_lineHeight;
2626 }
2627
2628 int wxDataViewMainWindow::GetEndOfLastCol() const
2629 {
2630 int width = 0;
2631 unsigned int i;
2632 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
2633 {
2634 const wxDataViewColumn *c =
2635 const_cast<wxDataViewCtrl*>(GetOwner())->GetColumnAt( i );
2636
2637 if (!c->IsHidden())
2638 width += c->GetWidth();
2639 }
2640 return width;
2641 }
2642
2643 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
2644 {
2645 int x = 0;
2646 int y = 0;
2647 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2648
2649 return GetLineAt( y );
2650 }
2651
2652 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
2653 {
2654 wxSize client_size = GetClientSize();
2655 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
2656 &client_size.x, &client_size.y );
2657
2658 // we should deal with the pixel here
2659 unsigned int row = GetLineAt(client_size.y) - 1;
2660
2661 return wxMin( GetRowCount()-1, row );
2662 }
2663
2664 unsigned int wxDataViewMainWindow::GetRowCount() const
2665 {
2666 if ( m_count == -1 )
2667 {
2668 wxDataViewMainWindow* const
2669 self = const_cast<wxDataViewMainWindow*>(this);
2670 self->m_count = RecalculateCount();
2671 self->UpdateDisplay();
2672 }
2673 return m_count;
2674 }
2675
2676 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
2677 {
2678 m_currentRow = row;
2679
2680 // send event
2681 }
2682
2683 void wxDataViewMainWindow::SelectAllRows( bool on )
2684 {
2685 if (IsEmpty())
2686 return;
2687
2688 if (on)
2689 {
2690 m_selection.Clear();
2691 for (unsigned int i = 0; i < GetRowCount(); i++)
2692 m_selection.Add( i );
2693 Refresh();
2694 }
2695 else
2696 {
2697 unsigned int first_visible = GetFirstVisibleRow();
2698 unsigned int last_visible = GetLastVisibleRow();
2699 unsigned int i;
2700 for (i = 0; i < m_selection.GetCount(); i++)
2701 {
2702 unsigned int row = m_selection[i];
2703 if ((row >= first_visible) && (row <= last_visible))
2704 RefreshRow( row );
2705 }
2706 m_selection.Clear();
2707 }
2708 }
2709
2710 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
2711 {
2712 if (m_selection.Index( row ) == wxNOT_FOUND)
2713 {
2714 if (on)
2715 {
2716 m_selection.Add( row );
2717 RefreshRow( row );
2718 }
2719 }
2720 else
2721 {
2722 if (!on)
2723 {
2724 m_selection.Remove( row );
2725 RefreshRow( row );
2726 }
2727 }
2728 }
2729
2730 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
2731 {
2732 if (from > to)
2733 {
2734 unsigned int tmp = from;
2735 from = to;
2736 to = tmp;
2737 }
2738
2739 unsigned int i;
2740 for (i = from; i <= to; i++)
2741 {
2742 if (m_selection.Index( i ) == wxNOT_FOUND)
2743 {
2744 if (on)
2745 m_selection.Add( i );
2746 }
2747 else
2748 {
2749 if (!on)
2750 m_selection.Remove( i );
2751 }
2752 }
2753 RefreshRows( from, to );
2754 }
2755
2756 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2757 {
2758 for (size_t i=0; i < aSelections.GetCount(); i++)
2759 {
2760 int n = aSelections[i];
2761
2762 m_selection.Add( n );
2763 RefreshRow( n );
2764 }
2765 }
2766
2767 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
2768 {
2769 if (m_selection.Index( row ) == wxNOT_FOUND)
2770 m_selection.Add( row );
2771 else
2772 m_selection.Remove( row );
2773 RefreshRow( row );
2774 }
2775
2776 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
2777 {
2778 return (m_selection.Index( row ) != wxNOT_FOUND);
2779 }
2780
2781 void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
2782 {
2783 wxWindow *parent = GetParent();
2784 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
2785
2786 le.SetEventObject(parent);
2787 le.SetModel(GetModel());
2788 le.SetItem( item );
2789
2790 parent->ProcessWindowEvent(le);
2791 }
2792
2793 void wxDataViewMainWindow::RefreshRow( unsigned int row )
2794 {
2795 wxRect rect( 0, GetLineStart( row ), GetEndOfLastCol(), GetLineHeight( row ) );
2796 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2797
2798 wxSize client_size = GetClientSize();
2799 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2800 wxRect intersect_rect = client_rect.Intersect( rect );
2801 if (intersect_rect.width > 0)
2802 Refresh( true, &intersect_rect );
2803 }
2804
2805 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
2806 {
2807 if (from > to)
2808 {
2809 unsigned int tmp = to;
2810 to = from;
2811 from = tmp;
2812 }
2813
2814 wxRect rect( 0, GetLineStart( from ), GetEndOfLastCol(), GetLineStart( (to-from+1) ) );
2815 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2816
2817 wxSize client_size = GetClientSize();
2818 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2819 wxRect intersect_rect = client_rect.Intersect( rect );
2820 if (intersect_rect.width > 0)
2821 Refresh( true, &intersect_rect );
2822 }
2823
2824 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
2825 {
2826 wxSize client_size = GetClientSize();
2827 int start = GetLineStart( firstRow );
2828 m_owner->CalcScrolledPosition( start, 0, &start, NULL );
2829 if (start > client_size.y) return;
2830
2831 wxRect rect( 0, start, client_size.x, client_size.y - start );
2832
2833 Refresh( true, &rect );
2834 }
2835
2836 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2837 {
2838 wxRect rect;
2839 rect.x = 0;
2840 rect.y = GetLineStart( row );
2841 rect.width = GetEndOfLastCol();
2842 rect.height = GetLineHeight( row );
2843
2844 return rect;
2845 }
2846
2847 int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
2848 {
2849 const wxDataViewModel *model = GetModel();
2850
2851 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2852 {
2853 // TODO make more efficient
2854
2855 int start = 0;
2856
2857 unsigned int r;
2858 for (r = 0; r < row; r++)
2859 {
2860 const wxDataViewTreeNode* node = GetTreeNodeByRow(r);
2861 if (!node) return start;
2862
2863 wxDataViewItem item = node->GetItem();
2864
2865 unsigned int cols = GetOwner()->GetColumnCount();
2866 unsigned int col;
2867 int height = m_lineHeight;
2868 for (col = 0; col < cols; col++)
2869 {
2870 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2871 if (column->IsHidden())
2872 continue; // skip it!
2873
2874 if ((col != 0) &&
2875 model->IsContainer(item) &&
2876 !model->HasContainerColumns(item))
2877 continue; // skip it!
2878
2879 wxDataViewRenderer *renderer =
2880 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2881 renderer->PrepareForItem(model, item, column->GetModelColumn());
2882
2883 height = wxMax( height, renderer->GetSize().y );
2884 }
2885
2886 start += height;
2887 }
2888
2889 return start;
2890 }
2891 else
2892 {
2893 return row * m_lineHeight;
2894 }
2895 }
2896
2897 int wxDataViewMainWindow::GetLineAt( unsigned int y ) const
2898 {
2899 const wxDataViewModel *model = GetModel();
2900
2901 // check for the easy case first
2902 if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
2903 return y / m_lineHeight;
2904
2905 // TODO make more efficient
2906 unsigned int row = 0;
2907 unsigned int yy = 0;
2908 for (;;)
2909 {
2910 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2911 if (!node)
2912 {
2913 // not really correct...
2914 return row + ((y-yy) / m_lineHeight);
2915 }
2916
2917 wxDataViewItem item = node->GetItem();
2918
2919 unsigned int cols = GetOwner()->GetColumnCount();
2920 unsigned int col;
2921 int height = m_lineHeight;
2922 for (col = 0; col < cols; col++)
2923 {
2924 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2925 if (column->IsHidden())
2926 continue; // skip it!
2927
2928 if ((col != 0) &&
2929 model->IsContainer(item) &&
2930 !model->HasContainerColumns(item))
2931 continue; // skip it!
2932
2933 wxDataViewRenderer *renderer =
2934 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2935 renderer->PrepareForItem(model, item, column->GetModelColumn());
2936
2937 height = wxMax( height, renderer->GetSize().y );
2938 }
2939
2940 yy += height;
2941 if (y < yy)
2942 return row;
2943
2944 row++;
2945 }
2946 }
2947
2948 int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
2949 {
2950 const wxDataViewModel *model = GetModel();
2951
2952 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2953 {
2954 wxASSERT( !IsVirtualList() );
2955
2956 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2957 // wxASSERT( node );
2958 if (!node) return m_lineHeight;
2959
2960 wxDataViewItem item = node->GetItem();
2961
2962 int height = m_lineHeight;
2963
2964 unsigned int cols = GetOwner()->GetColumnCount();
2965 unsigned int col;
2966 for (col = 0; col < cols; col++)
2967 {
2968 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2969 if (column->IsHidden())
2970 continue; // skip it!
2971
2972 if ((col != 0) &&
2973 model->IsContainer(item) &&
2974 !model->HasContainerColumns(item))
2975 continue; // skip it!
2976
2977 wxDataViewRenderer *renderer =
2978 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2979 renderer->PrepareForItem(model, item, column->GetModelColumn());
2980
2981 height = wxMax( height, renderer->GetSize().y );
2982 }
2983
2984 return height;
2985 }
2986 else
2987 {
2988 return m_lineHeight;
2989 }
2990 }
2991
2992
2993 class RowToTreeNodeJob: public DoJob
2994 {
2995 public:
2996 RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
2997 {
2998 this->row = row;
2999 this->current = current;
3000 ret = NULL;
3001 parent = node;
3002 }
3003
3004 virtual int operator() ( wxDataViewTreeNode * node )
3005 {
3006 current ++;
3007 if( current == static_cast<int>(row))
3008 {
3009 ret = node;
3010 return DoJob::DONE;
3011 }
3012
3013 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
3014 {
3015 current += node->GetSubTreeCount();
3016 return DoJob::SKIP_SUBTREE;
3017 }
3018 else
3019 {
3020 parent = node;
3021
3022 // If the current node has only leaf children, we can find the
3023 // desired node directly. This can speed up finding the node
3024 // in some cases, and will have a very good effect for list views.
3025 if ( node->HasChildren() &&
3026 (int)node->GetChildNodes().size() == node->GetSubTreeCount() )
3027 {
3028 const int index = static_cast<int>(row) - current - 1;
3029 ret = node->GetChildNodes()[index];
3030 return DoJob::DONE;
3031 }
3032
3033 return DoJob::CONTINUE;
3034 }
3035 }
3036
3037 wxDataViewTreeNode * GetResult() const
3038 { return ret; }
3039
3040 private:
3041 unsigned int row;
3042 int current;
3043 wxDataViewTreeNode * ret;
3044 wxDataViewTreeNode * parent;
3045 };
3046
3047 wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) const
3048 {
3049 wxASSERT( !IsVirtualList() );
3050
3051 if ( row == (unsigned)-1 )
3052 return NULL;
3053
3054 RowToTreeNodeJob job( row , -2, m_root );
3055 Walker( m_root , job );
3056 return job.GetResult();
3057 }
3058
3059 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const
3060 {
3061 wxDataViewItem item;
3062 if (IsVirtualList())
3063 {
3064 if ( row < GetRowCount() )
3065 item = wxDataViewItem(wxUIntToPtr(row+1));
3066 }
3067 else
3068 {
3069 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
3070 if ( node )
3071 item = node->GetItem();
3072 }
3073
3074 return item;
3075 }
3076
3077 bool
3078 wxDataViewMainWindow::SendExpanderEvent(wxEventType type,
3079 const wxDataViewItem& item)
3080 {
3081 wxWindow *parent = GetParent();
3082 wxDataViewEvent le(type, parent->GetId());
3083
3084 le.SetEventObject(parent);
3085 le.SetModel(GetModel());
3086 le.SetItem( item );
3087
3088 return !parent->ProcessWindowEvent(le) || le.IsAllowed();
3089 }
3090
3091 bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const
3092 {
3093 if (IsList())
3094 return false;
3095
3096 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
3097 if (!node)
3098 return false;
3099
3100 if (!node->HasChildren())
3101 return false;
3102
3103 return node->IsOpen();
3104 }
3105
3106 bool wxDataViewMainWindow::HasChildren( unsigned int row ) const
3107 {
3108 if (IsList())
3109 return false;
3110
3111 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
3112 if (!node)
3113 return false;
3114
3115 if (!node->HasChildren())
3116 return false;
3117
3118 return true;
3119 }
3120
3121 void wxDataViewMainWindow::Expand( unsigned int row )
3122 {
3123 if (IsList())
3124 return;
3125
3126 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
3127 if (!node)
3128 return;
3129
3130 if (!node->HasChildren())
3131 return;
3132
3133 if (!node->IsOpen())
3134 {
3135 if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, node->GetItem()) )
3136 {
3137 // Vetoed by the event handler.
3138 return;
3139 }
3140
3141 node->ToggleOpen();
3142
3143 // build the children of current node
3144 if( node->GetChildNodes().empty() )
3145 {
3146 SortPrepare();
3147 ::BuildTreeHelper(GetModel(), node->GetItem(), node);
3148 }
3149
3150 // By expanding the node all row indices that are currently in the selection list
3151 // and are greater than our node have become invalid. So we have to correct that now.
3152 const unsigned rowAdjustment = node->GetSubTreeCount();
3153 for(unsigned i=0; i<m_selection.size(); ++i)
3154 {
3155 const unsigned testRow = m_selection[i];
3156 // all rows above us are not affected, so skip them
3157 if(testRow <= row)
3158 continue;
3159
3160 m_selection[i] += rowAdjustment;
3161 }
3162
3163 if(m_currentRow > row)
3164 ChangeCurrentRow(m_currentRow + rowAdjustment);
3165
3166 m_count = -1;
3167 UpdateDisplay();
3168 // Send the expanded event
3169 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
3170 }
3171 }
3172
3173 void wxDataViewMainWindow::Collapse(unsigned int row)
3174 {
3175 if (IsList())
3176 return;
3177
3178 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
3179 if (!node)
3180 return;
3181
3182 if (!node->HasChildren())
3183 return;
3184
3185 if (node->IsOpen())
3186 {
3187 if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem()) )
3188 {
3189 // Vetoed by the event handler.
3190 return;
3191 }
3192
3193 // Find out if there are selected items below the current node.
3194 bool selectCollapsingRow = false;
3195 const unsigned rowAdjustment = node->GetSubTreeCount();
3196 unsigned maxRowToBeTested = row + rowAdjustment;
3197 for(unsigned i=0; i<m_selection.size(); ++i)
3198 {
3199 const unsigned testRow = m_selection[i];
3200 if(testRow > row && testRow <= maxRowToBeTested)
3201 {
3202 selectCollapsingRow = true;
3203 // get out as soon as we have found a node that is selected
3204 break;
3205 }
3206 }
3207
3208 node->ToggleOpen();
3209
3210 // If the node to be closed has selected items the user won't see those any longer.
3211 // We select the collapsing node in this case.
3212 if(selectCollapsingRow)
3213 {
3214 SelectAllRows(false);
3215 ChangeCurrentRow(row);
3216 SelectRow(row, true);
3217 SendSelectionChangedEvent(GetItemByRow(row));
3218 }
3219 else
3220 {
3221 // if there were no selected items below our node we still need to "fix" the
3222 // selection list to adjust for the changing of the row indices.
3223 // We actually do the opposite of what we are doing in Expand().
3224 for(unsigned i=0; i<m_selection.size(); ++i)
3225 {
3226 const unsigned testRow = m_selection[i];
3227 // all rows above us are not affected, so skip them
3228 if(testRow <= row)
3229 continue;
3230
3231 m_selection[i] -= rowAdjustment;
3232 }
3233
3234 // if the "current row" is being collapsed away we change it to the current row ;-)
3235 if(m_currentRow > row && m_currentRow <= maxRowToBeTested)
3236 ChangeCurrentRow(row);
3237 else if(m_currentRow > row)
3238 ChangeCurrentRow(m_currentRow - rowAdjustment);
3239 }
3240
3241 m_count = -1;
3242 UpdateDisplay();
3243 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,node->GetItem());
3244 }
3245 }
3246
3247 wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
3248 {
3249 const wxDataViewModel * model = GetModel();
3250 if( model == NULL )
3251 return NULL;
3252
3253 if (!item.IsOk())
3254 return m_root;
3255
3256 // Compose the parent-chain for the item we are looking for
3257 wxVector<wxDataViewItem> parentChain;
3258 wxDataViewItem it( item );
3259 while( it.IsOk() )
3260 {
3261 parentChain.push_back(it);
3262 it = model->GetParent(it);
3263 }
3264
3265 // Find the item along the parent-chain.
3266 // This algorithm is designed to speed up the node-finding method
3267 wxDataViewTreeNode* node = m_root;
3268 for( unsigned iter = parentChain.size()-1; ; --iter )
3269 {
3270 if( node->HasChildren() )
3271 {
3272 if( node->GetChildNodes().empty() )
3273 {
3274 // Even though the item is a container, it doesn't have any
3275 // child nodes in the control's representation yet. We have
3276 // to realize its subtree now.
3277 SortPrepare();
3278 ::BuildTreeHelper(model, node->GetItem(), node);
3279 }
3280
3281 const wxDataViewTreeNodes& nodes = node->GetChildNodes();
3282 bool found = false;
3283
3284 for (unsigned i = 0; i < nodes.GetCount(); ++i)
3285 {
3286 wxDataViewTreeNode* currentNode = nodes[i];
3287 if (currentNode->GetItem() == parentChain[iter])
3288 {
3289 if (currentNode->GetItem() == item)
3290 return currentNode;
3291
3292 node = currentNode;
3293 found = true;
3294 break;
3295 }
3296 }
3297 if (!found)
3298 return NULL;
3299 }
3300 else
3301 return NULL;
3302
3303 if ( !iter )
3304 break;
3305 }
3306 return NULL;
3307 }
3308
3309 void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item,
3310 wxDataViewColumn* &column )
3311 {
3312 wxDataViewColumn *col = NULL;
3313 unsigned int cols = GetOwner()->GetColumnCount();
3314 unsigned int colnum = 0;
3315 int x, y;
3316 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
3317 for (unsigned x_start = 0; colnum < cols; colnum++)
3318 {
3319 col = GetOwner()->GetColumnAt(colnum);
3320 if (col->IsHidden())
3321 continue; // skip it!
3322
3323 unsigned int w = col->GetWidth();
3324 if (x_start+w >= (unsigned int)x)
3325 break;
3326
3327 x_start += w;
3328 }
3329
3330 column = col;
3331 item = GetItemByRow( GetLineAt( y ) );
3332 }
3333
3334 wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item,
3335 const wxDataViewColumn* column )
3336 {
3337 int xpos = 0;
3338 int width = 0;
3339
3340 unsigned int cols = GetOwner()->GetColumnCount();
3341 // If column is null the loop will compute the combined width of all columns.
3342 // Otherwise, it will compute the x position of the column we are looking for.
3343 for (unsigned int i = 0; i < cols; i++)
3344 {
3345 wxDataViewColumn* col = GetOwner()->GetColumnAt( i );
3346
3347 if (col == column)
3348 break;
3349
3350 if (col->IsHidden())
3351 continue; // skip it!
3352
3353 xpos += col->GetWidth();
3354 width += col->GetWidth();
3355 }
3356
3357 if(column != 0)
3358 {
3359 // If we have a column, we need can get its width directly.
3360 if(column->IsHidden())
3361 width = 0;
3362 else
3363 width = column->GetWidth();
3364
3365 }
3366 else
3367 {
3368 // If we have no column, we reset the x position back to zero.
3369 xpos = 0;
3370 }
3371
3372 // we have to take an expander column into account and compute its indentation
3373 // to get the correct x position where the actual text is
3374 int indent = 0;
3375 int row = GetRowByItem(item);
3376 if (!IsList() &&
3377 (column == 0 || GetExpanderColumnOrFirstOne(GetOwner()) == column) )
3378 {
3379 wxDataViewTreeNode* node = GetTreeNodeByRow(row);
3380 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
3381 indent = indent + m_lineHeight; // use m_lineHeight as the width of the expander
3382 }
3383
3384 wxRect itemRect( xpos + indent,
3385 GetLineStart( row ),
3386 width - indent,
3387 GetLineHeight( row ) );
3388
3389 GetOwner()->CalcScrolledPosition( itemRect.x, itemRect.y,
3390 &itemRect.x, &itemRect.y );
3391
3392 return itemRect;
3393 }
3394
3395 int wxDataViewMainWindow::RecalculateCount() const
3396 {
3397 if (IsVirtualList())
3398 {
3399 wxDataViewVirtualListModel *list_model =
3400 (wxDataViewVirtualListModel*) GetModel();
3401
3402 return list_model->GetCount();
3403 }
3404 else
3405 {
3406 return m_root->GetSubTreeCount();
3407 }
3408 }
3409
3410 class ItemToRowJob : public DoJob
3411 {
3412 public:
3413 ItemToRowJob(const wxDataViewItem& item_, wxVector<wxDataViewItem>::reverse_iterator iter)
3414 : m_iter(iter),
3415 item(item_)
3416 {
3417 ret = -1;
3418 }
3419
3420 // Maybe binary search will help to speed up this process
3421 virtual int operator() ( wxDataViewTreeNode * node)
3422 {
3423 ret ++;
3424 if( node->GetItem() == item )
3425 {
3426 return DoJob::DONE;
3427 }
3428
3429 if( node->GetItem() == *m_iter )
3430 {
3431 m_iter++;
3432 return DoJob::CONTINUE;
3433 }
3434 else
3435 {
3436 ret += node->GetSubTreeCount();
3437 return DoJob::SKIP_SUBTREE;
3438 }
3439
3440 }
3441
3442 // the row number is begin from zero
3443 int GetResult() const
3444 { return ret -1; }
3445
3446 private:
3447 wxVector<wxDataViewItem>::reverse_iterator m_iter;
3448 wxDataViewItem item;
3449 int ret;
3450
3451 };
3452
3453 int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
3454 {
3455 const wxDataViewModel * model = GetModel();
3456 if( model == NULL )
3457 return -1;
3458
3459 if (IsVirtualList())
3460 {
3461 return wxPtrToUInt( item.GetID() ) -1;
3462 }
3463 else
3464 {
3465 if( !item.IsOk() )
3466 return -1;
3467
3468 // Compose the parent-chain of the item we are looking for
3469 wxVector<wxDataViewItem> parentChain;
3470 wxDataViewItem it( item );
3471 while( it.IsOk() )
3472 {
3473 parentChain.push_back(it);
3474 it = model->GetParent(it);
3475 }
3476
3477 // add an 'invalid' item to represent our 'invisible' root node
3478 parentChain.push_back(wxDataViewItem());
3479
3480 // the parent chain was created by adding the deepest parent first.
3481 // so if we want to start at the root node, we have to iterate backwards through the vector
3482 ItemToRowJob job( item, parentChain.rbegin() );
3483 Walker( m_root, job );
3484 return job.GetResult();
3485 }
3486 }
3487
3488 static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
3489 wxDataViewTreeNode * node)
3490 {
3491 if( !model->IsContainer( item ) )
3492 return;
3493
3494 wxDataViewItemArray children;
3495 unsigned int num = model->GetChildren( item, children);
3496
3497 for ( unsigned int index = 0; index < num; index++ )
3498 {
3499 wxDataViewTreeNode *n = new wxDataViewTreeNode(node, children[index]);
3500
3501 if( model->IsContainer(children[index]) )
3502 n->SetHasChildren( true );
3503
3504 node->InsertChild(n, index);
3505 }
3506
3507 wxASSERT( node->IsOpen() );
3508 node->ChangeSubTreeCount(+num);
3509 }
3510
3511 void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
3512 {
3513 DestroyTree();
3514
3515 if (GetModel()->IsVirtualListModel())
3516 {
3517 m_count = -1;
3518 return;
3519 }
3520
3521 m_root = wxDataViewTreeNode::CreateRootNode();
3522
3523 // First we define a invalid item to fetch the top-level elements
3524 wxDataViewItem item;
3525 SortPrepare();
3526 BuildTreeHelper( model, item, m_root);
3527 m_count = -1;
3528 }
3529
3530 void wxDataViewMainWindow::DestroyTree()
3531 {
3532 if (!IsVirtualList())
3533 {
3534 wxDELETE(m_root);
3535 m_count = 0;
3536 }
3537 }
3538
3539 wxDataViewColumn*
3540 wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode)
3541 {
3542 // Edit the current column editable in 'mode'. If no column is focused
3543 // (typically because the user has full row selected), try to find the
3544 // first editable column (this would typically be a checkbox for
3545 // wxDATAVIEW_CELL_ACTIVATABLE and we don't want to force the user to set
3546 // focus on the checkbox column; or on the only editable text column).
3547
3548 wxDataViewColumn *candidate = m_currentCol;
3549
3550 if ( candidate &&
3551 !IsCellEditableInMode(item, candidate, mode) &&
3552 !m_currentColSetByKeyboard )
3553 {
3554 // If current column was set by mouse to something not editable (in
3555 // 'mode') and the user pressed Space/F2 to edit it, treat the
3556 // situation as if there was whole-row focus, because that's what is
3557 // visually indicated and the mouse click could very well be targeted
3558 // on the row rather than on an individual cell.
3559 //
3560 // But if it was done by keyboard, respect that even if the column
3561 // isn't editable, because focus is visually on that column and editing
3562 // something else would be surprising.
3563 candidate = NULL;
3564 }
3565
3566 if ( !candidate )
3567 {
3568 const unsigned cols = GetOwner()->GetColumnCount();
3569 for ( unsigned i = 0; i < cols; i++ )
3570 {
3571 wxDataViewColumn *c = GetOwner()->GetColumnAt(i);
3572 if ( c->IsHidden() )
3573 continue;
3574
3575 if ( IsCellEditableInMode(item, c, mode) )
3576 {
3577 candidate = c;
3578 break;
3579 }
3580 }
3581 }
3582
3583 // If on container item without columns, only the expander column
3584 // may be directly editable:
3585 if ( candidate &&
3586 GetOwner()->GetExpanderColumn() != candidate &&
3587 GetModel()->IsContainer(item) &&
3588 !GetModel()->HasContainerColumns(item) )
3589 {
3590 candidate = GetOwner()->GetExpanderColumn();
3591 }
3592
3593 if ( !candidate )
3594 return NULL;
3595
3596 if ( !IsCellEditableInMode(item, candidate, mode) )
3597 return NULL;
3598
3599 return candidate;
3600 }
3601
3602 bool wxDataViewMainWindow::IsCellEditableInMode(const wxDataViewItem& item,
3603 const wxDataViewColumn *col,
3604 wxDataViewCellMode mode) const
3605 {
3606 if ( col->GetRenderer()->GetMode() != mode )
3607 return false;
3608
3609 if ( !GetModel()->IsEnabled(item, col->GetModelColumn()) )
3610 return false;
3611
3612 return true;
3613 }
3614
3615 void wxDataViewMainWindow::OnCharHook(wxKeyEvent& event)
3616 {
3617 if ( m_editorCtrl )
3618 {
3619 // Handle any keys special for the in-place editor and return without
3620 // calling Skip() below.
3621 switch ( event.GetKeyCode() )
3622 {
3623 case WXK_ESCAPE:
3624 m_editorRenderer->CancelEditing();
3625 return;
3626
3627 case WXK_RETURN:
3628 m_editorRenderer->FinishEditing();
3629 return;
3630 }
3631 }
3632
3633 event.Skip();
3634 }
3635
3636 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
3637 {
3638 wxWindow * const parent = GetParent();
3639
3640 // propagate the char event upwards
3641 wxKeyEvent eventForParent(event);
3642 eventForParent.SetEventObject(parent);
3643 if ( parent->ProcessWindowEvent(eventForParent) )
3644 return;
3645
3646 if ( parent->HandleAsNavigationKey(event) )
3647 return;
3648
3649 // no item -> nothing to do
3650 if (!HasCurrentRow())
3651 {
3652 event.Skip();
3653 return;
3654 }
3655
3656 // don't use m_linesPerPage directly as it might not be computed yet
3657 const int pageSize = GetCountPerPage();
3658 wxCHECK_RET( pageSize, wxT("should have non zero page size") );
3659
3660 switch ( event.GetKeyCode() )
3661 {
3662 case WXK_RETURN:
3663 if ( event.HasModifiers() )
3664 {
3665 event.Skip();
3666 break;
3667 }
3668 else
3669 {
3670 // Enter activates the item, i.e. sends wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED to
3671 // it. Only if that event is not handled do we activate column renderer (which
3672 // is normally done by Space) or even inline editing.
3673
3674 const wxDataViewItem item = GetItemByRow(m_currentRow);
3675
3676 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,
3677 parent->GetId());
3678 le.SetItem(item);
3679 le.SetEventObject(parent);
3680 le.SetModel(GetModel());
3681
3682 if ( parent->ProcessWindowEvent(le) )
3683 break;
3684 // else: fall through to WXK_SPACE handling
3685 }
3686
3687 case WXK_SPACE:
3688 if ( event.HasModifiers() )
3689 {
3690 event.Skip();
3691 break;
3692 }
3693 else
3694 {
3695 // Space toggles activatable items or -- if not activatable --
3696 // starts inline editing (this is normally done using F2 on
3697 // Windows, but Space is common everywhere else, so use it too
3698 // for greater cross-platform compatibility).
3699
3700 const wxDataViewItem item = GetItemByRow(m_currentRow);
3701
3702 // Activate the current activatable column. If not column is focused (typically
3703 // because the user has full row selected), try to find the first activatable
3704 // column (this would typically be a checkbox and we don't want to force the user
3705 // to set focus on the checkbox column).
3706 wxDataViewColumn *activatableCol = FindColumnForEditing(item, wxDATAVIEW_CELL_ACTIVATABLE);
3707
3708 if ( activatableCol )
3709 {
3710 const unsigned colIdx = activatableCol->GetModelColumn();
3711 const wxRect cell_rect = GetOwner()->GetItemRect(item, activatableCol);
3712
3713 wxDataViewRenderer *cell = activatableCol->GetRenderer();
3714 cell->PrepareForItem(GetModel(), item, colIdx);
3715 cell->WXActivateCell(cell_rect, GetModel(), item, colIdx, NULL);
3716
3717 break;
3718 }
3719 // else: fall through to WXK_F2 handling
3720 }
3721
3722 case WXK_F2:
3723 if ( event.HasModifiers() )
3724 {
3725 event.Skip();
3726 break;
3727 }
3728 else
3729 {
3730 if( !m_selection.empty() )
3731 {
3732 // Mimic Windows 7 behavior: edit the item that has focus
3733 // if it is selected and the first selected item if focus
3734 // is out of selection.
3735 int sel;
3736 if ( m_selection.Index(m_currentRow) != wxNOT_FOUND )
3737 sel = m_currentRow;
3738 else
3739 sel = m_selection[0];
3740
3741
3742 const wxDataViewItem item = GetItemByRow(sel);
3743
3744 // Edit the current column. If no column is focused
3745 // (typically because the user has full row selected), try
3746 // to find the first editable column.
3747 wxDataViewColumn *editableCol = FindColumnForEditing(item, wxDATAVIEW_CELL_EDITABLE);
3748
3749 if ( editableCol )
3750 GetOwner()->EditItem(item, editableCol);
3751 }
3752 }
3753 break;
3754
3755 case WXK_UP:
3756 OnVerticalNavigation( -1, event );
3757 break;
3758
3759 case WXK_DOWN:
3760 OnVerticalNavigation( +1, event );
3761 break;
3762 // Add the process for tree expanding/collapsing
3763 case WXK_LEFT:
3764 OnLeftKey();
3765 break;
3766
3767 case WXK_RIGHT:
3768 OnRightKey();
3769 break;
3770
3771 case WXK_END:
3772 OnVerticalNavigation( +(int)GetRowCount(), event );
3773 break;
3774
3775 case WXK_HOME:
3776 OnVerticalNavigation( -(int)GetRowCount(), event );
3777 break;
3778
3779 case WXK_PAGEUP:
3780 OnVerticalNavigation( -(pageSize - 1), event );
3781 break;
3782
3783 case WXK_PAGEDOWN:
3784 OnVerticalNavigation( +(pageSize - 1), event );
3785 break;
3786
3787 default:
3788 event.Skip();
3789 }
3790 }
3791
3792 void wxDataViewMainWindow::OnVerticalNavigation(int delta, const wxKeyEvent& event)
3793 {
3794 // if there is no selection, we cannot move it anywhere
3795 if (!HasCurrentRow() || IsEmpty())
3796 return;
3797
3798 int newRow = (int)m_currentRow + delta;
3799
3800 // let's keep the new row inside the allowed range
3801 if ( newRow < 0 )
3802 newRow = 0;
3803
3804 const int rowCount = (int)GetRowCount();
3805 if ( newRow >= rowCount )
3806 newRow = rowCount - 1;
3807
3808 unsigned int oldCurrent = m_currentRow;
3809 unsigned int newCurrent = (unsigned int)newRow;
3810
3811 // in single selection we just ignore Shift as we can't select several
3812 // items anyhow
3813 if ( event.ShiftDown() && !IsSingleSel() )
3814 {
3815 RefreshRow( oldCurrent );
3816
3817 ChangeCurrentRow( newCurrent );
3818
3819 // select all the items between the old and the new one
3820 if ( oldCurrent > newCurrent )
3821 {
3822 newCurrent = oldCurrent;
3823 oldCurrent = m_currentRow;
3824 }
3825
3826 SelectRows( oldCurrent, newCurrent, true );
3827 if (oldCurrent!=newCurrent)
3828 SendSelectionChangedEvent(GetItemByRow(m_selection[0]));
3829 }
3830 else // !shift
3831 {
3832 RefreshRow( oldCurrent );
3833
3834 // all previously selected items are unselected unless ctrl is held
3835 if ( !event.ControlDown() )
3836 SelectAllRows(false);
3837
3838 ChangeCurrentRow( newCurrent );
3839
3840 if ( !event.ControlDown() )
3841 {
3842 SelectRow( m_currentRow, true );
3843 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
3844 }
3845 else
3846 RefreshRow( m_currentRow );
3847 }
3848
3849 GetOwner()->EnsureVisible( m_currentRow, -1 );
3850 }
3851
3852 void wxDataViewMainWindow::OnLeftKey()
3853 {
3854 if ( IsList() )
3855 {
3856 TryAdvanceCurrentColumn(NULL, /*forward=*/false);
3857 }
3858 else
3859 {
3860 wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow);
3861 if ( !node )
3862 return;
3863
3864 if ( TryAdvanceCurrentColumn(node, /*forward=*/false) )
3865 return;
3866
3867 // Because TryAdvanceCurrentColumn() return false, we are at the first
3868 // column or using whole-row selection. In this situation, we can use
3869 // the standard TreeView handling of the left key.
3870 if (node->HasChildren() && node->IsOpen())
3871 {
3872 Collapse(m_currentRow);
3873 }
3874 else
3875 {
3876 // if the node is already closed, we move the selection to its parent
3877 wxDataViewTreeNode *parent_node = node->GetParent();
3878
3879 if (parent_node)
3880 {
3881 int parent = GetRowByItem( parent_node->GetItem() );
3882 if ( parent >= 0 )
3883 {
3884 unsigned int row = m_currentRow;
3885 SelectRow( row, false);
3886 SelectRow( parent, true );
3887 ChangeCurrentRow( parent );
3888 GetOwner()->EnsureVisible( parent, -1 );
3889 SendSelectionChangedEvent( parent_node->GetItem() );
3890 }
3891 }
3892 }
3893 }
3894 }
3895
3896 void wxDataViewMainWindow::OnRightKey()
3897 {
3898 if ( IsList() )
3899 {
3900 TryAdvanceCurrentColumn(NULL, /*forward=*/true);
3901 }
3902 else
3903 {
3904 wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow);
3905 if ( !node )
3906 return;
3907
3908 if ( node->HasChildren() )
3909 {
3910 if ( !node->IsOpen() )
3911 {
3912 Expand( m_currentRow );
3913 }
3914 else
3915 {
3916 // if the node is already open, we move the selection to the first child
3917 unsigned int row = m_currentRow;
3918 SelectRow( row, false );
3919 SelectRow( row + 1, true );
3920 ChangeCurrentRow( row + 1 );
3921 GetOwner()->EnsureVisible( row + 1, -1 );
3922 SendSelectionChangedEvent( GetItemByRow(row+1) );
3923 }
3924 }
3925 else
3926 {
3927 TryAdvanceCurrentColumn(node, /*forward=*/true);
3928 }
3929 }
3930 }
3931
3932 bool wxDataViewMainWindow::TryAdvanceCurrentColumn(wxDataViewTreeNode *node, bool forward)
3933 {
3934 if ( GetOwner()->GetColumnCount() == 0 )
3935 return false;
3936
3937 if ( !m_useCellFocus )
3938 return false;
3939
3940 if ( node )
3941 {
3942 // navigation shouldn't work in branch nodes without other columns:
3943 if ( node->HasChildren() && !GetModel()->HasContainerColumns(node->GetItem()) )
3944 return false;
3945 }
3946
3947 if ( m_currentCol == NULL || !m_currentColSetByKeyboard )
3948 {
3949 if ( forward )
3950 {
3951 m_currentCol = GetOwner()->GetColumnAt(1);
3952 m_currentColSetByKeyboard = true;
3953 RefreshRow(m_currentRow);
3954 return true;
3955 }
3956 else
3957 return false;
3958 }
3959
3960 int idx = GetOwner()->GetColumnIndex(m_currentCol) + (forward ? +1 : -1);
3961
3962 if ( idx >= (int)GetOwner()->GetColumnCount() )
3963 return false;
3964
3965 GetOwner()->EnsureVisible(m_currentRow, idx);
3966
3967 if ( idx < 1 )
3968 {
3969 // We are going to the left of the second column. Reset to whole-row
3970 // focus (which means first column would be edited).
3971 m_currentCol = NULL;
3972 RefreshRow(m_currentRow);
3973 return true;
3974 }
3975
3976 m_currentCol = GetOwner()->GetColumnAt(idx);
3977 m_currentColSetByKeyboard = true;
3978 RefreshRow(m_currentRow);
3979 return true;
3980 }
3981
3982 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
3983 {
3984 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
3985 {
3986 // let the base handle mouse wheel events.
3987 event.Skip();
3988 return;
3989 }
3990
3991 if(event.ButtonDown())
3992 {
3993 // Not skipping button down events would prevent the system from
3994 // setting focus to this window as most (all?) of them do by default,
3995 // so skip it to enable default handling.
3996 event.Skip();
3997 }
3998
3999 int x = event.GetX();
4000 int y = event.GetY();
4001 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
4002 wxDataViewColumn *col = NULL;
4003
4004 int xpos = 0;
4005 unsigned int cols = GetOwner()->GetColumnCount();
4006 unsigned int i;
4007 for (i = 0; i < cols; i++)
4008 {
4009 wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
4010 if (c->IsHidden())
4011 continue; // skip it!
4012
4013 if (x < xpos + c->GetWidth())
4014 {
4015 col = c;
4016 break;
4017 }
4018 xpos += c->GetWidth();
4019 }
4020
4021 wxDataViewModel* const model = GetModel();
4022
4023 const unsigned int current = GetLineAt( y );
4024 const wxDataViewItem item = GetItemByRow(current);
4025
4026 // Handle right clicking here, before everything else as context menu
4027 // events should be sent even when we click outside of any item, unlike all
4028 // the other ones.
4029 if (event.RightUp())
4030 {
4031 wxWindow *parent = GetParent();
4032 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
4033 le.SetEventObject(parent);
4034 le.SetModel(model);
4035
4036 if ( item.IsOk() && col )
4037 {
4038 le.SetItem( item );
4039 le.SetColumn( col->GetModelColumn() );
4040 le.SetDataViewColumn( col );
4041 }
4042
4043 parent->ProcessWindowEvent(le);
4044 return;
4045 }
4046
4047 // Check if we clicked outside the item area.
4048 if ((current >= GetRowCount()) || !col)
4049 {
4050 // Follow Windows convention here: clicking either left or right (but
4051 // not middle) button clears the existing selection.
4052 if (m_owner && (event.LeftDown() || event.RightDown()))
4053 {
4054 if (!GetSelections().empty())
4055 {
4056 m_owner->UnselectAll();
4057 SendSelectionChangedEvent(wxDataViewItem());
4058 }
4059 }
4060 event.Skip();
4061 return;
4062 }
4063
4064 wxDataViewRenderer *cell = col->GetRenderer();
4065 wxDataViewColumn* const
4066 expander = GetExpanderColumnOrFirstOne(GetOwner());
4067
4068 // Test whether the mouse is hovering over the expander (a.k.a tree "+"
4069 // button) and also determine the offset of the real cell start, skipping
4070 // the indentation and the expander itself.
4071 bool hoverOverExpander = false;
4072 int itemOffset = 0;
4073 if ((!IsList()) && (expander == col))
4074 {
4075 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
4076
4077 int indent = node->GetIndentLevel();
4078 itemOffset = GetOwner()->GetIndent()*indent;
4079
4080 if ( node->HasChildren() )
4081 {
4082 // we make the rectangle we are looking in a bit bigger than the actual
4083 // visual expander so the user can hit that little thing reliably
4084 wxRect rect(itemOffset,
4085 GetLineStart( current ) + (GetLineHeight(current) - m_lineHeight)/2,
4086 m_lineHeight, m_lineHeight);
4087
4088 if( rect.Contains(x, y) )
4089 {
4090 // So the mouse is over the expander
4091 hoverOverExpander = true;
4092 if (m_underMouse && m_underMouse != node)
4093 {
4094 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
4095 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
4096 }
4097 if (m_underMouse != node)
4098 {
4099 // wxLogMessage("Do the row: %d", current);
4100 RefreshRow(current);
4101 }
4102 m_underMouse = node;
4103 }
4104 }
4105
4106 // Account for the expander as well, even if this item doesn't have it,
4107 // its parent does so it still counts for the offset.
4108 itemOffset += m_lineHeight;
4109 }
4110 if (!hoverOverExpander)
4111 {
4112 if (m_underMouse != NULL)
4113 {
4114 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
4115 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
4116 m_underMouse = NULL;
4117 }
4118 }
4119
4120 #if wxUSE_DRAG_AND_DROP
4121 if (event.Dragging())
4122 {
4123 if (m_dragCount == 0)
4124 {
4125 // we have to report the raw, physical coords as we want to be
4126 // able to call HitTest(event.m_pointDrag) from the user code to
4127 // get the item being dragged
4128 m_dragStart = event.GetPosition();
4129 }
4130
4131 m_dragCount++;
4132
4133 if (m_dragCount != 3)
4134 return;
4135
4136 if (event.LeftIsDown())
4137 {
4138 m_owner->CalcUnscrolledPosition( m_dragStart.x, m_dragStart.y,
4139 &m_dragStart.x, &m_dragStart.y );
4140 unsigned int drag_item_row = GetLineAt( m_dragStart.y );
4141 wxDataViewItem itemDragged = GetItemByRow( drag_item_row );
4142
4143 // Notify cell about drag
4144 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
4145 event.SetEventObject( m_owner );
4146 event.SetItem( itemDragged );
4147 event.SetModel( model );
4148 if (!m_owner->HandleWindowEvent( event ))
4149 return;
4150
4151 if (!event.IsAllowed())
4152 return;
4153
4154 wxDataObject *obj = event.GetDataObject();
4155 if (!obj)
4156 return;
4157
4158 wxDataViewDropSource drag( this, drag_item_row );
4159 drag.SetData( *obj );
4160 /* wxDragResult res = */ drag.DoDragDrop(event.GetDragFlags());
4161 delete obj;
4162 }
4163 return;
4164 }
4165 else
4166 {
4167 m_dragCount = 0;
4168 }
4169 #endif // wxUSE_DRAG_AND_DROP
4170
4171 bool simulateClick = false;
4172
4173 if (event.ButtonDClick())
4174 {
4175 m_renameTimer->Stop();
4176 m_lastOnSame = false;
4177 }
4178
4179 bool ignore_other_columns =
4180 ((expander != col) &&
4181 (model->IsContainer(item)) &&
4182 (!model->HasContainerColumns(item)));
4183
4184 if (event.LeftDClick())
4185 {
4186 if(hoverOverExpander)
4187 {
4188 // a double click on the expander will be converted into a "simulated" normal click
4189 simulateClick = true;
4190 }
4191 else if ( current == m_lineLastClicked )
4192 {
4193 wxWindow *parent = GetParent();
4194 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
4195 le.SetItem( item );
4196 le.SetColumn( col->GetModelColumn() );
4197 le.SetDataViewColumn( col );
4198 le.SetEventObject(parent);
4199 le.SetModel(GetModel());
4200
4201 parent->ProcessWindowEvent(le);
4202 return;
4203 }
4204 else
4205 {
4206 // The first click was on another item, so don't interpret this as
4207 // a double click, but as a simple click instead
4208 simulateClick = true;
4209 }
4210 }
4211
4212 if (event.LeftUp() && !hoverOverExpander)
4213 {
4214 if (m_lineSelectSingleOnUp != (unsigned int)-1)
4215 {
4216 // select single line
4217 SelectAllRows( false );
4218 SelectRow( m_lineSelectSingleOnUp, true );
4219 SendSelectionChangedEvent( GetItemByRow(m_lineSelectSingleOnUp) );
4220 }
4221
4222 // If the user click the expander, we do not do editing even if the column
4223 // with expander are editable
4224 if (m_lastOnSame && !ignore_other_columns)
4225 {
4226 if ((col == m_currentCol) && (current == m_currentRow) &&
4227 IsCellEditableInMode(item, col, wxDATAVIEW_CELL_EDITABLE) )
4228 {
4229 m_renameTimer->Start( 100, true );
4230 }
4231 }
4232
4233 m_lastOnSame = false;
4234 m_lineSelectSingleOnUp = (unsigned int)-1;
4235 }
4236 else if(!event.LeftUp())
4237 {
4238 // This is necessary, because after a DnD operation in
4239 // from and to ourself, the up event is swallowed by the
4240 // DnD code. So on next non-up event (which means here and
4241 // now) m_lineSelectSingleOnUp should be reset.
4242 m_lineSelectSingleOnUp = (unsigned int)-1;
4243 }
4244
4245 if (event.RightDown())
4246 {
4247 m_lineBeforeLastClicked = m_lineLastClicked;
4248 m_lineLastClicked = current;
4249
4250 // If the item is already selected, do not update the selection.
4251 // Multi-selections should not be cleared if a selected item is clicked.
4252 if (!IsRowSelected(current))
4253 {
4254 SelectAllRows(false);
4255 const unsigned oldCurrent = m_currentRow;
4256 ChangeCurrentRow(current);
4257 SelectRow(m_currentRow,true);
4258 RefreshRow(oldCurrent);
4259 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
4260 }
4261 }
4262 else if (event.MiddleDown())
4263 {
4264 }
4265
4266 if((event.LeftDown() || simulateClick) && hoverOverExpander)
4267 {
4268 wxDataViewTreeNode* node = GetTreeNodeByRow(current);
4269
4270 // hoverOverExpander being true tells us that our node must be
4271 // valid and have children.
4272 // So we don't need any extra checks.
4273 if( node->IsOpen() )
4274 Collapse(current);
4275 else
4276 Expand(current);
4277 }
4278 else if ((event.LeftDown() || simulateClick) && !hoverOverExpander)
4279 {
4280 m_lineBeforeLastClicked = m_lineLastClicked;
4281 m_lineLastClicked = current;
4282
4283 unsigned int oldCurrentRow = m_currentRow;
4284 bool oldWasSelected = IsRowSelected(m_currentRow);
4285
4286 bool cmdModifierDown = event.CmdDown();
4287 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
4288 {
4289 if ( IsSingleSel() || !IsRowSelected(current) )
4290 {
4291 SelectAllRows( false );
4292 ChangeCurrentRow(current);
4293 SelectRow(m_currentRow,true);
4294 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
4295 }
4296 else // multi sel & current is highlighted & no mod keys
4297 {
4298 m_lineSelectSingleOnUp = current;
4299 ChangeCurrentRow(current); // change focus
4300 }
4301 }
4302 else // multi sel & either ctrl or shift is down
4303 {
4304 if (cmdModifierDown)
4305 {
4306 ChangeCurrentRow(current);
4307 ReverseRowSelection(m_currentRow);
4308 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
4309 }
4310 else if (event.ShiftDown())
4311 {
4312 ChangeCurrentRow(current);
4313
4314 unsigned int lineFrom = oldCurrentRow,
4315 lineTo = current;
4316
4317 if ( lineTo < lineFrom )
4318 {
4319 lineTo = lineFrom;
4320 lineFrom = m_currentRow;
4321 }
4322
4323 SelectRows(lineFrom, lineTo, true);
4324 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
4325 }
4326 else // !ctrl, !shift
4327 {
4328 // test in the enclosing if should make it impossible
4329 wxFAIL_MSG( wxT("how did we get here?") );
4330 }
4331 }
4332
4333 if (m_currentRow != oldCurrentRow)
4334 RefreshRow( oldCurrentRow );
4335
4336 wxDataViewColumn *oldCurrentCol = m_currentCol;
4337
4338 // Update selection here...
4339 m_currentCol = col;
4340 m_currentColSetByKeyboard = false;
4341
4342 // This flag is used to decide whether we should start editing the item
4343 // label. We do it if the user clicks twice (but not double clicks,
4344 // i.e. simulateClick is false) on the same item but not if the click
4345 // was used for something else already, e.g. selecting the item (so it
4346 // must have been already selected) or giving the focus to the control
4347 // (so it must have had focus already).
4348 m_lastOnSame = !simulateClick && ((col == oldCurrentCol) &&
4349 (current == oldCurrentRow)) && oldWasSelected &&
4350 HasFocus();
4351
4352 // Call ActivateCell() after everything else as under GTK+
4353 if ( IsCellEditableInMode(item, col, wxDATAVIEW_CELL_ACTIVATABLE) )
4354 {
4355 // notify cell about click
4356 cell->PrepareForItem(model, item, col->GetModelColumn());
4357
4358 wxRect cell_rect( xpos + itemOffset,
4359 GetLineStart( current ),
4360 col->GetWidth() - itemOffset,
4361 GetLineHeight( current ) );
4362
4363 // Report position relative to the cell's custom area, i.e.
4364 // no the entire space as given by the control but the one
4365 // used by the renderer after calculation of alignment etc.
4366
4367 // adjust the rectangle ourselves to account for the alignment
4368 wxRect rectItem = cell_rect;
4369 const int align = cell->GetAlignment();
4370 if ( align != wxDVR_DEFAULT_ALIGNMENT )
4371 {
4372 const wxSize size = cell->GetSize();
4373
4374 if ( size.x >= 0 && size.x < cell_rect.width )
4375 {
4376 if ( align & wxALIGN_CENTER_HORIZONTAL )
4377 rectItem.x += (cell_rect.width - size.x)/2;
4378 else if ( align & wxALIGN_RIGHT )
4379 rectItem.x += cell_rect.width - size.x;
4380 // else: wxALIGN_LEFT is the default
4381 }
4382
4383 if ( size.y >= 0 && size.y < cell_rect.height )
4384 {
4385 if ( align & wxALIGN_CENTER_VERTICAL )
4386 rectItem.y += (cell_rect.height - size.y)/2;
4387 else if ( align & wxALIGN_BOTTOM )
4388 rectItem.y += cell_rect.height - size.y;
4389 // else: wxALIGN_TOP is the default
4390 }
4391 }
4392
4393 wxMouseEvent event2(event);
4394 event2.m_x -= rectItem.x;
4395 event2.m_y -= rectItem.y;
4396 m_owner->CalcUnscrolledPosition(event2.m_x, event2.m_y, &event2.m_x, &event2.m_y);
4397
4398 /* ignore ret */ cell->WXActivateCell
4399 (
4400 cell_rect,
4401 model,
4402 item,
4403 col->GetModelColumn(),
4404 &event2
4405 );
4406 }
4407 }
4408 }
4409
4410 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
4411 {
4412 m_hasFocus = true;
4413
4414 if (HasCurrentRow())
4415 Refresh();
4416
4417 event.Skip();
4418 }
4419
4420 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
4421 {
4422 m_hasFocus = false;
4423
4424 if (HasCurrentRow())
4425 Refresh();
4426
4427 event.Skip();
4428 }
4429
4430 void wxDataViewMainWindow::OnColumnsCountChanged()
4431 {
4432 int editableCount = 0;
4433
4434 const unsigned cols = GetOwner()->GetColumnCount();
4435 for ( unsigned i = 0; i < cols; i++ )
4436 {
4437 wxDataViewColumn *c = GetOwner()->GetColumnAt(i);
4438 if ( c->IsHidden() )
4439 continue;
4440 if ( c->GetRenderer()->GetMode() != wxDATAVIEW_CELL_INERT )
4441 editableCount++;
4442 }
4443
4444 m_useCellFocus = (editableCount > 1);
4445
4446 UpdateDisplay();
4447 }
4448
4449 //-----------------------------------------------------------------------------
4450 // wxDataViewCtrl
4451 //-----------------------------------------------------------------------------
4452
4453 WX_DEFINE_LIST(wxDataViewColumnList)
4454
4455 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
4456 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
4457 EVT_SIZE(wxDataViewCtrl::OnSize)
4458 END_EVENT_TABLE()
4459
4460 wxDataViewCtrl::~wxDataViewCtrl()
4461 {
4462 if (m_notifier)
4463 GetModel()->RemoveNotifier( m_notifier );
4464
4465 m_cols.Clear();
4466 m_colsBestWidths.clear();
4467 }
4468
4469 void wxDataViewCtrl::Init()
4470 {
4471 m_cols.DeleteContents(true);
4472 m_notifier = NULL;
4473
4474 // No sorting column at start
4475 m_sortingColumnIdx = wxNOT_FOUND;
4476
4477 m_headerArea = NULL;
4478 m_clientArea = NULL;
4479
4480 m_colsDirty = false;
4481 }
4482
4483 bool wxDataViewCtrl::Create(wxWindow *parent,
4484 wxWindowID id,
4485 const wxPoint& pos,
4486 const wxSize& size,
4487 long style,
4488 const wxValidator& validator,
4489 const wxString& name)
4490 {
4491 // if ( (style & wxBORDER_MASK) == 0)
4492 // style |= wxBORDER_SUNKEN;
4493
4494 Init();
4495
4496 if (!wxControl::Create( parent, id, pos, size,
4497 style | wxScrolledWindowStyle, validator, name))
4498 return false;
4499
4500 SetInitialSize(size);
4501
4502 #ifdef __WXMAC__
4503 MacSetClipChildren( true );
4504 #endif
4505
4506 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
4507
4508 // We use the cursor keys for moving the selection, not scrolling, so call
4509 // this method to ensure wxScrollHelperEvtHandler doesn't catch all
4510 // keyboard events forwarded to us from wxListMainWindow.
4511 DisableKeyboardScrolling();
4512
4513 if (HasFlag(wxDV_NO_HEADER))
4514 m_headerArea = NULL;
4515 else
4516 m_headerArea = new wxDataViewHeaderWindow(this);
4517
4518 SetTargetWindow( m_clientArea );
4519
4520 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
4521 if (m_headerArea)
4522 sizer->Add( m_headerArea, 0, wxGROW );
4523 sizer->Add( m_clientArea, 1, wxGROW );
4524 SetSizer( sizer );
4525
4526 return true;
4527 }
4528
4529 wxBorder wxDataViewCtrl::GetDefaultBorder() const
4530 {
4531 return wxBORDER_THEME;
4532 }
4533
4534 #ifdef __WXMSW__
4535 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
4536 WXWPARAM wParam,
4537 WXLPARAM lParam)
4538 {
4539 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
4540
4541 #ifndef __WXWINCE__
4542 // we need to process arrows ourselves for scrolling
4543 if ( nMsg == WM_GETDLGCODE )
4544 {
4545 rc |= DLGC_WANTARROWS;
4546 }
4547 #endif
4548
4549 return rc;
4550 }
4551 #endif
4552
4553 wxSize wxDataViewCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
4554 {
4555 wxSize newsize = size;
4556 if (!HasFlag(wxDV_NO_HEADER) && (m_headerArea))
4557 newsize.y -= m_headerArea->GetSize().y;
4558
4559 return newsize;
4560 }
4561
4562 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
4563 {
4564 // We need to override OnSize so that our scrolled
4565 // window a) does call Layout() to use sizers for
4566 // positioning the controls but b) does not query
4567 // the sizer for their size and use that for setting
4568 // the scrollable area as set that ourselves by
4569 // calling SetScrollbar() further down.
4570
4571 Layout();
4572
4573 AdjustScrollbars();
4574
4575 // We must redraw the headers if their height changed. Normally this
4576 // shouldn't happen as the control shouldn't let itself be resized beneath
4577 // its minimal height but avoid the display artefacts that appear if it
4578 // does happen, e.g. because there is really not enough vertical space.
4579 if ( !HasFlag(wxDV_NO_HEADER) && m_headerArea &&
4580 m_headerArea->GetSize().y <= m_headerArea->GetBestSize(). y )
4581 {
4582 m_headerArea->Refresh();
4583 }
4584 }
4585
4586 void wxDataViewCtrl::SetFocus()
4587 {
4588 if (m_clientArea)
4589 m_clientArea->SetFocus();
4590 }
4591
4592 bool wxDataViewCtrl::SetFont(const wxFont & font)
4593 {
4594 if (!wxControl::SetFont(font))
4595 return false;
4596
4597 if (m_headerArea)
4598 m_headerArea->SetFont(font);
4599
4600 if (m_clientArea)
4601 {
4602 m_clientArea->SetFont(font);
4603 m_clientArea->SetRowHeight(m_clientArea->GetDefaultRowHeight());
4604 }
4605
4606 if (m_headerArea || m_clientArea)
4607 {
4608 InvalidateColBestWidths();
4609 Layout();
4610 }
4611
4612 return true;
4613 }
4614
4615
4616
4617 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
4618 {
4619 if (!wxDataViewCtrlBase::AssociateModel( model ))
4620 return false;
4621
4622 if (model)
4623 {
4624 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
4625 model->AddNotifier( m_notifier );
4626 }
4627 else if (m_notifier)
4628 {
4629 m_notifier->Cleared();
4630 m_notifier = NULL;
4631 }
4632
4633 m_clientArea->DestroyTree();
4634
4635 if (model)
4636 {
4637 m_clientArea->BuildTree(model);
4638 }
4639
4640 m_clientArea->UpdateDisplay();
4641
4642 return true;
4643 }
4644
4645 #if wxUSE_DRAG_AND_DROP
4646
4647 bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format )
4648 {
4649 return m_clientArea->EnableDragSource( format );
4650 }
4651
4652 bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format )
4653 {
4654 return m_clientArea->EnableDropTarget( format );
4655 }
4656
4657 #endif // wxUSE_DRAG_AND_DROP
4658
4659 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
4660 {
4661 if (!wxDataViewCtrlBase::AppendColumn(col))
4662 return false;
4663
4664 m_cols.Append( col );
4665 m_colsBestWidths.push_back(CachedColWidthInfo());
4666 OnColumnsCountChanged();
4667 return true;
4668 }
4669
4670 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
4671 {
4672 if (!wxDataViewCtrlBase::PrependColumn(col))
4673 return false;
4674
4675 m_cols.Insert( col );
4676 m_colsBestWidths.insert(m_colsBestWidths.begin(), CachedColWidthInfo());
4677 OnColumnsCountChanged();
4678 return true;
4679 }
4680
4681 bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
4682 {
4683 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
4684 return false;
4685
4686 m_cols.Insert( pos, col );
4687 m_colsBestWidths.insert(m_colsBestWidths.begin() + pos, CachedColWidthInfo());
4688 OnColumnsCountChanged();
4689 return true;
4690 }
4691
4692 void wxDataViewCtrl::OnColumnChange(unsigned int idx)
4693 {
4694 if ( m_headerArea )
4695 m_headerArea->UpdateColumn(idx);
4696
4697 m_clientArea->UpdateDisplay();
4698 }
4699
4700 void wxDataViewCtrl::OnColumnsCountChanged()
4701 {
4702 if (m_headerArea)
4703 m_headerArea->SetColumnCount(GetColumnCount());
4704
4705 m_clientArea->OnColumnsCountChanged();
4706 }
4707
4708 void wxDataViewCtrl::DoSetExpanderColumn()
4709 {
4710 m_clientArea->UpdateDisplay();
4711 }
4712
4713 void wxDataViewCtrl::DoSetIndent()
4714 {
4715 m_clientArea->UpdateDisplay();
4716 }
4717
4718 unsigned int wxDataViewCtrl::GetColumnCount() const
4719 {
4720 return m_cols.GetCount();
4721 }
4722
4723 bool wxDataViewCtrl::SetRowHeight( int lineHeight )
4724 {
4725 if ( !m_clientArea )
4726 return false;
4727
4728 m_clientArea->SetRowHeight(lineHeight);
4729
4730 return true;
4731 }
4732
4733 wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
4734 {
4735 return m_cols[idx];
4736 }
4737
4738 wxDataViewColumn *wxDataViewCtrl::GetColumnAt(unsigned int pos) const
4739 {
4740 // columns can't be reordered if there is no header window which allows
4741 // to do this
4742 const unsigned idx = m_headerArea ? m_headerArea->GetColumnsOrder()[pos]
4743 : pos;
4744
4745 return GetColumn(idx);
4746 }
4747
4748 int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn *column) const
4749 {
4750 const unsigned count = m_cols.size();
4751 for ( unsigned n = 0; n < count; n++ )
4752 {
4753 if ( m_cols[n] == column )
4754 return n;
4755 }
4756
4757 return wxNOT_FOUND;
4758 }
4759
4760 unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
4761 {
4762 if ( m_colsBestWidths[idx].width != 0 )
4763 return m_colsBestWidths[idx].width;
4764
4765 const int count = m_clientArea->GetRowCount();
4766 wxDataViewColumn *column = GetColumn(idx);
4767 wxDataViewRenderer *renderer =
4768 const_cast<wxDataViewRenderer*>(column->GetRenderer());
4769
4770 class MaxWidthCalculator
4771 {
4772 public:
4773 MaxWidthCalculator(const wxDataViewCtrl *dvc,
4774 wxDataViewMainWindow *clientArea,
4775 wxDataViewRenderer *renderer,
4776 const wxDataViewModel *model,
4777 unsigned column,
4778 int expanderSize)
4779 : m_width(0),
4780 m_dvc(dvc),
4781 m_clientArea(clientArea),
4782 m_renderer(renderer),
4783 m_model(model),
4784 m_column(column),
4785 m_expanderSize(expanderSize)
4786
4787 {
4788 m_isExpanderCol =
4789 !clientArea->IsList() &&
4790 (column == 0 ||
4791 GetExpanderColumnOrFirstOne(const_cast<wxDataViewCtrl*>(dvc)) == dvc->GetColumnAt(column));
4792 }
4793
4794 void UpdateWithWidth(int width)
4795 {
4796 m_width = wxMax(m_width, width);
4797 }
4798
4799 void UpdateWithRow(int row)
4800 {
4801 int indent = 0;
4802 wxDataViewItem item;
4803
4804 if ( m_isExpanderCol )
4805 {
4806 wxDataViewTreeNode *node = m_clientArea->GetTreeNodeByRow(row);
4807 item = node->GetItem();
4808 indent = m_dvc->GetIndent() * node->GetIndentLevel() + m_expanderSize;
4809 }
4810 else
4811 {
4812 item = m_clientArea->GetItemByRow(row);
4813 }
4814
4815 m_renderer->PrepareForItem(m_model, item, m_column);
4816 m_width = wxMax(m_width, m_renderer->GetSize().x + indent);
4817 }
4818
4819 int GetMaxWidth() const { return m_width; }
4820
4821 private:
4822 int m_width;
4823 const wxDataViewCtrl *m_dvc;
4824 wxDataViewMainWindow *m_clientArea;
4825 wxDataViewRenderer *m_renderer;
4826 const wxDataViewModel *m_model;
4827 unsigned m_column;
4828 bool m_isExpanderCol;
4829 int m_expanderSize;
4830 };
4831
4832 MaxWidthCalculator calculator(this, m_clientArea, renderer,
4833 GetModel(), column->GetModelColumn(),
4834 m_clientArea->GetRowHeight());
4835
4836 calculator.UpdateWithWidth(column->GetMinWidth());
4837
4838 if ( m_headerArea )
4839 calculator.UpdateWithWidth(m_headerArea->GetColumnTitleWidth(*column));
4840
4841 // The code below deserves some explanation. For very large controls, we
4842 // simply can't afford to calculate sizes for all items, it takes too
4843 // long. So the best we can do is to check the first and the last N/2
4844 // items in the control for some sufficiently large N and calculate best
4845 // sizes from that. That can result in the calculated best width being too
4846 // small for some outliers, but it's better to get slightly imperfect
4847 // result than to wait several seconds after every update. To avoid highly
4848 // visible miscalculations, we also include all currently visible items
4849 // no matter what. Finally, the value of N is determined dynamically by
4850 // measuring how much time we spent on the determining item widths so far.
4851
4852 #if wxUSE_STOPWATCH
4853 int top_part_end = count;
4854 static const long CALC_TIMEOUT = 20/*ms*/;
4855 // don't call wxStopWatch::Time() too often
4856 static const unsigned CALC_CHECK_FREQ = 100;
4857 wxStopWatch timer;
4858 #else
4859 // use some hard-coded limit, that's the best we can do without timer
4860 int top_part_end = wxMin(500, count);
4861 #endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH
4862
4863 int row = 0;
4864
4865 for ( row = 0; row < top_part_end; row++ )
4866 {
4867 #if wxUSE_STOPWATCH
4868 if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 &&
4869 timer.Time() > CALC_TIMEOUT )
4870 break;
4871 #endif // wxUSE_STOPWATCH
4872 calculator.UpdateWithRow(row);
4873 }
4874
4875 // row is the first unmeasured item now; that's our value of N/2
4876
4877 if ( row < count )
4878 {
4879 top_part_end = row;
4880
4881 // add bottom N/2 items now:
4882 const int bottom_part_start = wxMax(row, count - row);
4883 for ( row = bottom_part_start; row < count; row++ )
4884 {
4885 calculator.UpdateWithRow(row);
4886 }
4887
4888 // finally, include currently visible items in the calculation:
4889 const wxPoint origin = CalcUnscrolledPosition(wxPoint(0, 0));
4890 int first_visible = m_clientArea->GetLineAt(origin.y);
4891 int last_visible = m_clientArea->GetLineAt(origin.y + GetClientSize().y);
4892
4893 first_visible = wxMax(first_visible, top_part_end);
4894 last_visible = wxMin(bottom_part_start, last_visible);
4895
4896 for ( row = first_visible; row < last_visible; row++ )
4897 {
4898 calculator.UpdateWithRow(row);
4899 }
4900
4901 wxLogTrace("dataview",
4902 "determined best size from %d top, %d bottom plus %d more visible items out of %d total",
4903 top_part_end,
4904 count - bottom_part_start,
4905 wxMax(0, last_visible - first_visible),
4906 count);
4907 }
4908
4909 int max_width = calculator.GetMaxWidth();
4910 if ( max_width > 0 )
4911 max_width += 2 * PADDING_RIGHTLEFT;
4912
4913 const_cast<wxDataViewCtrl*>(this)->m_colsBestWidths[idx].width = max_width;
4914 return max_width;
4915 }
4916
4917 void wxDataViewCtrl::ColumnMoved(wxDataViewColumn * WXUNUSED(col),
4918 unsigned int WXUNUSED(new_pos))
4919 {
4920 // do _not_ reorder m_cols elements here, they should always be in the
4921 // order in which columns were added, we only display the columns in
4922 // different order
4923 m_clientArea->UpdateDisplay();
4924 }
4925
4926 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
4927 {
4928 wxDataViewColumnList::compatibility_iterator ret = m_cols.Find( column );
4929 if (!ret)
4930 return false;
4931
4932 m_colsBestWidths.erase(m_colsBestWidths.begin() + GetColumnIndex(column));
4933 m_cols.Erase(ret);
4934
4935 if ( m_clientArea->GetCurrentColumn() == column )
4936 m_clientArea->ClearCurrentColumn();
4937
4938 OnColumnsCountChanged();
4939
4940 return true;
4941 }
4942
4943 bool wxDataViewCtrl::ClearColumns()
4944 {
4945 SetExpanderColumn(NULL);
4946 m_cols.Clear();
4947 m_colsBestWidths.clear();
4948
4949 m_clientArea->ClearCurrentColumn();
4950
4951 OnColumnsCountChanged();
4952
4953 return true;
4954 }
4955
4956 void wxDataViewCtrl::InvalidateColBestWidth(int idx)
4957 {
4958 m_colsBestWidths[idx].width = 0;
4959 m_colsBestWidths[idx].dirty = true;
4960 m_colsDirty = true;
4961 }
4962
4963 void wxDataViewCtrl::InvalidateColBestWidths()
4964 {
4965 // mark all columns as dirty:
4966 m_colsBestWidths.clear();
4967 m_colsBestWidths.resize(m_cols.size());
4968 m_colsDirty = true;
4969 }
4970
4971 void wxDataViewCtrl::UpdateColWidths()
4972 {
4973 m_colsDirty = false;
4974
4975 if ( !m_headerArea )
4976 return;
4977
4978 const unsigned len = m_colsBestWidths.size();
4979 for ( unsigned i = 0; i < len; i++ )
4980 {
4981 // Note that we have to have an explicit 'dirty' flag here instead of
4982 // checking if the width==0, as is done in GetBestColumnWidth().
4983 //
4984 // Testing width==0 wouldn't work correctly if some code called
4985 // GetWidth() after col. width invalidation but before
4986 // wxDataViewCtrl::UpdateColWidths() was called at idle time. This
4987 // would result in the header's column width getting out of sync with
4988 // the control itself.
4989 if ( m_colsBestWidths[i].dirty )
4990 {
4991 m_headerArea->UpdateColumn(i);
4992 m_colsBestWidths[i].dirty = false;
4993 }
4994 }
4995 }
4996
4997 void wxDataViewCtrl::OnInternalIdle()
4998 {
4999 wxDataViewCtrlBase::OnInternalIdle();
5000
5001 if ( m_colsDirty )
5002 UpdateColWidths();
5003 }
5004
5005 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
5006 {
5007 unsigned int len = GetColumnCount();
5008 for ( unsigned int i = 0; i < len; i++ )
5009 {
5010 wxDataViewColumn * col = GetColumnAt(i);
5011 if (column==col)
5012 return i;
5013 }
5014
5015 return wxNOT_FOUND;
5016 }
5017
5018 wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
5019 {
5020 return m_sortingColumnIdx == wxNOT_FOUND ? NULL
5021 : GetColumn(m_sortingColumnIdx);
5022 }
5023
5024 wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const
5025 {
5026 return GetItemByRow(m_clientArea->GetCurrentRow());
5027 }
5028
5029 void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item)
5030 {
5031 const int row = m_clientArea->GetRowByItem(item);
5032
5033 const unsigned oldCurrent = m_clientArea->GetCurrentRow();
5034 if ( static_cast<unsigned>(row) != oldCurrent )
5035 {
5036 m_clientArea->ChangeCurrentRow(row);
5037 m_clientArea->RefreshRow(oldCurrent);
5038 m_clientArea->RefreshRow(row);
5039 }
5040 }
5041
5042 wxDataViewColumn *wxDataViewCtrl::GetCurrentColumn() const
5043 {
5044 return m_clientArea->GetCurrentColumn();
5045 }
5046
5047 int wxDataViewCtrl::GetSelectedItemsCount() const
5048 {
5049 return m_clientArea->GetSelections().size();
5050 }
5051
5052 int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
5053 {
5054 sel.Empty();
5055 const wxDataViewSelection& selections = m_clientArea->GetSelections();
5056
5057 const size_t len = selections.size();
5058 for ( size_t i = 0; i < len; i++ )
5059 {
5060 wxDataViewItem item = m_clientArea->GetItemByRow(selections[i]);
5061 if ( item.IsOk() )
5062 {
5063 sel.Add(item);
5064 }
5065 else
5066 {
5067 wxFAIL_MSG( "invalid item in selection - bad internal state" );
5068 }
5069 }
5070
5071 return sel.size();
5072 }
5073
5074 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
5075 {
5076 wxDataViewSelection selection(wxDataViewSelectionCmp);
5077
5078 wxDataViewItem last_parent;
5079
5080 int len = sel.GetCount();
5081 for( int i = 0; i < len; i ++ )
5082 {
5083 wxDataViewItem item = sel[i];
5084 wxDataViewItem parent = GetModel()->GetParent( item );
5085 if (parent)
5086 {
5087 if (parent != last_parent)
5088 ExpandAncestors(item);
5089 }
5090
5091 last_parent = parent;
5092 int row = m_clientArea->GetRowByItem( item );
5093 if( row >= 0 )
5094 selection.Add( static_cast<unsigned int>(row) );
5095 }
5096
5097 m_clientArea->SetSelections( selection );
5098 }
5099
5100 void wxDataViewCtrl::Select( const wxDataViewItem & item )
5101 {
5102 ExpandAncestors( item );
5103
5104 int row = m_clientArea->GetRowByItem( item );
5105 if( row >= 0 )
5106 {
5107 // Unselect all rows before select another in the single select mode
5108 if (m_clientArea->IsSingleSel())
5109 m_clientArea->SelectAllRows(false);
5110
5111 m_clientArea->SelectRow(row, true);
5112
5113 // Also set focus to the selected item
5114 m_clientArea->ChangeCurrentRow( row );
5115 }
5116 }
5117
5118 void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
5119 {
5120 int row = m_clientArea->GetRowByItem( item );
5121 if( row >= 0 )
5122 m_clientArea->SelectRow(row, false);
5123 }
5124
5125 bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
5126 {
5127 int row = m_clientArea->GetRowByItem( item );
5128 if( row >= 0 )
5129 {
5130 return m_clientArea->IsRowSelected(row);
5131 }
5132 return false;
5133 }
5134
5135 void wxDataViewCtrl::SetAlternateRowColour(const wxColour& colour)
5136 {
5137 m_alternateRowColour = colour;
5138 }
5139
5140 void wxDataViewCtrl::SelectAll()
5141 {
5142 m_clientArea->SelectAllRows(true);
5143 }
5144
5145 void wxDataViewCtrl::UnselectAll()
5146 {
5147 m_clientArea->SelectAllRows(false);
5148 }
5149
5150 void wxDataViewCtrl::EnsureVisible( int row, int column )
5151 {
5152 if( row < 0 )
5153 row = 0;
5154 if( row > (int) m_clientArea->GetRowCount() )
5155 row = m_clientArea->GetRowCount();
5156
5157 int first = m_clientArea->GetFirstVisibleRow();
5158 int last = m_clientArea->GetLastVisibleRow();
5159 if( row < first )
5160 m_clientArea->ScrollTo( row, column );
5161 else if( row > last )
5162 m_clientArea->ScrollTo( row - last + first, column );
5163 else
5164 m_clientArea->ScrollTo( first, column );
5165 }
5166
5167 void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataViewColumn * column )
5168 {
5169 ExpandAncestors( item );
5170
5171 m_clientArea->RecalculateDisplay();
5172
5173 int row = m_clientArea->GetRowByItem(item);
5174 if( row >= 0 )
5175 {
5176 if( column == NULL )
5177 EnsureVisible(row, -1);
5178 else
5179 EnsureVisible( row, GetColumnIndex(column) );
5180 }
5181
5182 }
5183
5184 void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item,
5185 wxDataViewColumn* &column ) const
5186 {
5187 m_clientArea->HitTest(point, item, column);
5188 }
5189
5190 wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item,
5191 const wxDataViewColumn* column ) const
5192 {
5193 return m_clientArea->GetItemRect(item, column);
5194 }
5195
5196 wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
5197 {
5198 return m_clientArea->GetItemByRow( row );
5199 }
5200
5201 int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
5202 {
5203 return m_clientArea->GetRowByItem( item );
5204 }
5205
5206 void wxDataViewCtrl::Expand( const wxDataViewItem & item )
5207 {
5208 ExpandAncestors( item );
5209
5210 int row = m_clientArea->GetRowByItem( item );
5211 if (row != -1)
5212 {
5213 m_clientArea->Expand(row);
5214 InvalidateColBestWidths();
5215 }
5216 }
5217
5218 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
5219 {
5220 int row = m_clientArea->GetRowByItem( item );
5221 if (row != -1)
5222 {
5223 m_clientArea->Collapse(row);
5224 InvalidateColBestWidths();
5225 }
5226 }
5227
5228 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
5229 {
5230 int row = m_clientArea->GetRowByItem( item );
5231 if (row != -1)
5232 return m_clientArea->IsExpanded(row);
5233 return false;
5234 }
5235
5236 void wxDataViewCtrl::EditItem(const wxDataViewItem& item, const wxDataViewColumn *column)
5237 {
5238 wxCHECK_RET( item.IsOk(), "invalid item" );
5239 wxCHECK_RET( column, "no column provided" );
5240
5241 m_clientArea->StartEditing(item, column);
5242 }
5243
5244 #endif // !wxUSE_GENERICDATAVIEWCTRL
5245
5246 #endif // wxUSE_DATAVIEWCTRL