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