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