Harmonize wxDataViewCtrl::GetSelection() behaviour in all ports.
[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 int itemPosInNode = node ? node->GetChildren().Index(item.GetID()) : wxNOT_FOUND;
2043
2044 // Notice that it is possible that the item being deleted is not in the
2045 // tree at all, for example we could be deleting a never shown (because
2046 // collapsed) item in a tree model. So it's not an error if we don't know
2047 // about this item, just return without doing anything then.
2048 if ( !node || itemPosInNode == wxNOT_FOUND )
2049 return false;
2050
2051 bool isContainer = false;
2052 wxDataViewTreeNode *itemNode = NULL;
2053
2054 const wxDataViewTreeNodes nds = node->GetNodes();
2055 for (size_t i = 0; i < nds.GetCount(); i ++)
2056 {
2057 if (nds[i]->GetItem() == item)
2058 {
2059 isContainer = true;
2060 itemNode = nds[i];
2061 break;
2062 }
2063 }
2064
2065 // Delete the item from wxDataViewTreeNode representation:
2066 int itemsDeleted = 1;
2067 node->GetChildren().Remove( item.GetID() );
2068
2069 if( isContainer )
2070 {
2071 wxDataViewTreeNode *n = node->FindItemAsNode(item);
2072
2073 wxCHECK_MSG( n != NULL, false, "item not found" );
2074
2075 node->GetNodes().Remove( n );
2076 itemsDeleted += n->GetSubTreeCount();
2077 ::DestroyTreeHelper(n);
2078 }
2079
2080 // Make the row number invalid and get a new valid one when user call GetRowCount
2081 m_count = -1;
2082 node->ChangeSubTreeCount(-itemsDeleted);
2083
2084 // Update selection by removing 'item' and its entire children tree from the selection.
2085 if ( !m_selection.empty() )
2086 {
2087 // we can't call GetRowByItem() on 'item', as it's already deleted, so compute it from
2088 // the parent ('node') and position in its list of children
2089 int itemRow;
2090 if ( itemPosInNode == 0 )
2091 {
2092 // 1st child, row number is that of the parent node + 1
2093 itemRow = GetRowByItem(node->GetItem()) + 1;
2094 }
2095 else
2096 {
2097 // row number is that of the sibling above 'item' + its subtree if any + 1
2098 const wxDataViewItem sibling = wxDataViewItem(node->GetChildren()[itemPosInNode - 1]);
2099 const wxDataViewTreeNode *siblingNode = node->FindItemAsNode(sibling);
2100
2101 itemRow = GetRowByItem(sibling);
2102 if ( siblingNode )
2103 itemRow += siblingNode->GetSubTreeCount();
2104 itemRow += 1;
2105 }
2106
2107 wxDataViewSelection newsel(wxDataViewSelectionCmp);
2108
2109 const size_t numSelections = m_selection.size();
2110 for ( size_t i = 0; i < numSelections; ++i )
2111 {
2112 const int s = m_selection[i];
2113 if ( s < itemRow )
2114 newsel.push_back(s);
2115 else if ( s >= itemRow + itemsDeleted )
2116 newsel.push_back(s - itemsDeleted);
2117 // else: deleted item, remove from selection
2118 }
2119
2120 m_selection = newsel;
2121 }
2122 }
2123
2124 // Change the current row to the last row if the current exceed the max row number
2125 if( m_currentRow > GetRowCount() )
2126 ChangeCurrentRow(m_count - 1);
2127
2128 GetOwner()->UpdateColBestWidths();
2129 UpdateDisplay();
2130
2131 return true;
2132 }
2133
2134 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
2135 {
2136 SortPrepare();
2137 g_model->Resort();
2138
2139 GetOwner()->UpdateColBestWidths();
2140
2141 // Send event
2142 wxWindow *parent = GetParent();
2143 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
2144 le.SetEventObject(parent);
2145 le.SetModel(GetOwner()->GetModel());
2146 le.SetItem(item);
2147 parent->GetEventHandler()->ProcessEvent(le);
2148
2149 return true;
2150 }
2151
2152 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int model_column )
2153 {
2154 int view_column = -1;
2155 unsigned int n_col = m_owner->GetColumnCount();
2156 for (unsigned i = 0; i < n_col; i++)
2157 {
2158 wxDataViewColumn *column = m_owner->GetColumn( i );
2159 if (column->GetModelColumn() == model_column)
2160 {
2161 view_column = (int) i;
2162 break;
2163 }
2164 }
2165 if (view_column == -1)
2166 return false;
2167
2168 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
2169 /*#define MAX_VIRTUAL_WIDTH 100000
2170
2171 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
2172 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2173 Refresh( true, &rect );
2174
2175 return true;
2176 */
2177 SortPrepare();
2178 g_model->Resort();
2179
2180 GetOwner()->UpdateColBestWidth(view_column);
2181
2182 // Send event
2183 wxWindow *parent = GetParent();
2184 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
2185 le.SetEventObject(parent);
2186 le.SetModel(GetOwner()->GetModel());
2187 le.SetItem(item);
2188 le.SetColumn(view_column);
2189 le.SetDataViewColumn(GetOwner()->GetColumn(view_column));
2190 parent->GetEventHandler()->ProcessEvent(le);
2191
2192 return true;
2193 }
2194
2195 bool wxDataViewMainWindow::Cleared()
2196 {
2197 DestroyTree();
2198 m_selection.Clear();
2199
2200 SortPrepare();
2201 BuildTree( GetOwner()->GetModel() );
2202
2203 GetOwner()->UpdateColBestWidths();
2204 UpdateDisplay();
2205
2206 return true;
2207 }
2208
2209 void wxDataViewMainWindow::UpdateDisplay()
2210 {
2211 m_dirty = true;
2212 m_underMouse = NULL;
2213 }
2214
2215 void wxDataViewMainWindow::OnInternalIdle()
2216 {
2217 wxWindow::OnInternalIdle();
2218
2219 if (m_dirty)
2220 {
2221 RecalculateDisplay();
2222 m_dirty = false;
2223 }
2224 }
2225
2226 void wxDataViewMainWindow::RecalculateDisplay()
2227 {
2228 wxDataViewModel *model = GetOwner()->GetModel();
2229 if (!model)
2230 {
2231 Refresh();
2232 return;
2233 }
2234
2235 int width = GetEndOfLastCol();
2236 int height = GetLineStart( GetRowCount() );
2237
2238 SetVirtualSize( width, height );
2239 GetOwner()->SetScrollRate( 10, m_lineHeight );
2240
2241 Refresh();
2242 }
2243
2244 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
2245 {
2246 m_underMouse = NULL;
2247
2248 wxWindow::ScrollWindow( dx, dy, rect );
2249
2250 if (GetOwner()->m_headerArea)
2251 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
2252 }
2253
2254 void wxDataViewMainWindow::ScrollTo( int rows, int column )
2255 {
2256 m_underMouse = NULL;
2257
2258 int x, y;
2259 m_owner->GetScrollPixelsPerUnit( &x, &y );
2260 int sy = GetLineStart( rows )/y;
2261 int sx = 0;
2262 if( column != -1 )
2263 {
2264 wxRect rect = GetClientRect();
2265 int colnum = 0;
2266 int x_start, w = 0;
2267 int xx, yy, xe;
2268 m_owner->CalcUnscrolledPosition( rect.x, rect.y, &xx, &yy );
2269 for (x_start = 0; colnum < column; colnum++)
2270 {
2271 wxDataViewColumn *col = GetOwner()->GetColumnAt(colnum);
2272 if (col->IsHidden())
2273 continue; // skip it!
2274
2275 w = col->GetWidth();
2276 x_start += w;
2277 }
2278
2279 int x_end = x_start + w;
2280 xe = xx + rect.width;
2281 if( x_end > xe )
2282 {
2283 sx = ( xx + x_end - xe )/x;
2284 }
2285 if( x_start < xx )
2286 {
2287 sx = x_start/x;
2288 }
2289 }
2290 m_owner->Scroll( sx, sy );
2291 }
2292
2293 int wxDataViewMainWindow::GetCountPerPage() const
2294 {
2295 wxSize size = GetClientSize();
2296 return size.y / m_lineHeight;
2297 }
2298
2299 int wxDataViewMainWindow::GetEndOfLastCol() const
2300 {
2301 int width = 0;
2302 unsigned int i;
2303 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
2304 {
2305 const wxDataViewColumn *c =
2306 const_cast<wxDataViewCtrl*>(GetOwner())->GetColumnAt( i );
2307
2308 if (!c->IsHidden())
2309 width += c->GetWidth();
2310 }
2311 return width;
2312 }
2313
2314 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
2315 {
2316 int x = 0;
2317 int y = 0;
2318 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2319
2320 return GetLineAt( y );
2321 }
2322
2323 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
2324 {
2325 wxSize client_size = GetClientSize();
2326 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
2327 &client_size.x, &client_size.y );
2328
2329 // we should deal with the pixel here
2330 unsigned int row = GetLineAt(client_size.y) - 1;
2331
2332 return wxMin( GetRowCount()-1, row );
2333 }
2334
2335 unsigned int wxDataViewMainWindow::GetRowCount()
2336 {
2337 if ( m_count == -1 )
2338 {
2339 m_count = RecalculateCount();
2340 UpdateDisplay();
2341 }
2342 return m_count;
2343 }
2344
2345 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
2346 {
2347 m_currentRow = row;
2348
2349 // send event
2350 }
2351
2352 void wxDataViewMainWindow::SelectAllRows( bool on )
2353 {
2354 if (IsEmpty())
2355 return;
2356
2357 if (on)
2358 {
2359 m_selection.Clear();
2360 for (unsigned int i = 0; i < GetRowCount(); i++)
2361 m_selection.Add( i );
2362 Refresh();
2363 }
2364 else
2365 {
2366 unsigned int first_visible = GetFirstVisibleRow();
2367 unsigned int last_visible = GetLastVisibleRow();
2368 unsigned int i;
2369 for (i = 0; i < m_selection.GetCount(); i++)
2370 {
2371 unsigned int row = m_selection[i];
2372 if ((row >= first_visible) && (row <= last_visible))
2373 RefreshRow( row );
2374 }
2375 m_selection.Clear();
2376 }
2377 }
2378
2379 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
2380 {
2381 if (m_selection.Index( row ) == wxNOT_FOUND)
2382 {
2383 if (on)
2384 {
2385 m_selection.Add( row );
2386 RefreshRow( row );
2387 }
2388 }
2389 else
2390 {
2391 if (!on)
2392 {
2393 m_selection.Remove( row );
2394 RefreshRow( row );
2395 }
2396 }
2397 }
2398
2399 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
2400 {
2401 if (from > to)
2402 {
2403 unsigned int tmp = from;
2404 from = to;
2405 to = tmp;
2406 }
2407
2408 unsigned int i;
2409 for (i = from; i <= to; i++)
2410 {
2411 if (m_selection.Index( i ) == wxNOT_FOUND)
2412 {
2413 if (on)
2414 m_selection.Add( i );
2415 }
2416 else
2417 {
2418 if (!on)
2419 m_selection.Remove( i );
2420 }
2421 }
2422 RefreshRows( from, to );
2423 }
2424
2425 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2426 {
2427 for (size_t i=0; i < aSelections.GetCount(); i++)
2428 {
2429 int n = aSelections[i];
2430
2431 m_selection.Add( n );
2432 RefreshRow( n );
2433 }
2434 }
2435
2436 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
2437 {
2438 if (m_selection.Index( row ) == wxNOT_FOUND)
2439 m_selection.Add( row );
2440 else
2441 m_selection.Remove( row );
2442 RefreshRow( row );
2443 }
2444
2445 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
2446 {
2447 return (m_selection.Index( row ) != wxNOT_FOUND);
2448 }
2449
2450 void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
2451 {
2452 wxWindow *parent = GetParent();
2453 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
2454
2455 le.SetEventObject(parent);
2456 le.SetModel(GetOwner()->GetModel());
2457 le.SetItem( item );
2458
2459 parent->GetEventHandler()->ProcessEvent(le);
2460 }
2461
2462 void wxDataViewMainWindow::RefreshRow( unsigned int row )
2463 {
2464 wxRect rect( 0, GetLineStart( row ), GetEndOfLastCol(), GetLineHeight( row ) );
2465 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2466
2467 wxSize client_size = GetClientSize();
2468 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2469 wxRect intersect_rect = client_rect.Intersect( rect );
2470 if (intersect_rect.width > 0)
2471 Refresh( true, &intersect_rect );
2472 }
2473
2474 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
2475 {
2476 if (from > to)
2477 {
2478 unsigned int tmp = to;
2479 to = from;
2480 from = tmp;
2481 }
2482
2483 wxRect rect( 0, GetLineStart( from ), GetEndOfLastCol(), GetLineStart( (to-from+1) ) );
2484 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2485
2486 wxSize client_size = GetClientSize();
2487 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2488 wxRect intersect_rect = client_rect.Intersect( rect );
2489 if (intersect_rect.width > 0)
2490 Refresh( true, &intersect_rect );
2491 }
2492
2493 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
2494 {
2495 wxSize client_size = GetClientSize();
2496 int start = GetLineStart( firstRow );
2497 m_owner->CalcScrolledPosition( start, 0, &start, NULL );
2498 if (start > client_size.y) return;
2499
2500 wxRect rect( 0, start, client_size.x, client_size.y - start );
2501
2502 Refresh( true, &rect );
2503 }
2504
2505 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
2506 {
2507 wxCHECK_RET( newCurrent < GetRowCount(),
2508 wxT("invalid item index in OnArrowChar()") );
2509
2510 // if there is no selection, we cannot move it anywhere
2511 if (!HasCurrentRow())
2512 return;
2513
2514 unsigned int oldCurrent = m_currentRow;
2515
2516 // in single selection we just ignore Shift as we can't select several
2517 // items anyhow
2518 if ( event.ShiftDown() && !IsSingleSel() )
2519 {
2520 RefreshRow( oldCurrent );
2521
2522 ChangeCurrentRow( newCurrent );
2523
2524 // select all the items between the old and the new one
2525 if ( oldCurrent > newCurrent )
2526 {
2527 newCurrent = oldCurrent;
2528 oldCurrent = m_currentRow;
2529 }
2530
2531 SelectRows( oldCurrent, newCurrent, true );
2532 if (oldCurrent!=newCurrent)
2533 SendSelectionChangedEvent(GetItemByRow(m_selection[0]));
2534 }
2535 else // !shift
2536 {
2537 RefreshRow( oldCurrent );
2538
2539 // all previously selected items are unselected unless ctrl is held
2540 if ( !event.ControlDown() )
2541 SelectAllRows(false);
2542
2543 ChangeCurrentRow( newCurrent );
2544
2545 if ( !event.ControlDown() )
2546 {
2547 SelectRow( m_currentRow, true );
2548 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
2549 }
2550 else
2551 RefreshRow( m_currentRow );
2552 }
2553
2554 GetOwner()->EnsureVisible( m_currentRow, -1 );
2555 }
2556
2557 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2558 {
2559 wxRect rect;
2560 rect.x = 0;
2561 rect.y = GetLineStart( row );
2562 rect.width = GetEndOfLastCol();
2563 rect.height = GetLineHeight( row );
2564
2565 return rect;
2566 }
2567
2568 int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
2569 {
2570 const wxDataViewModel *model = GetOwner()->GetModel();
2571
2572 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2573 {
2574 // TODO make more efficient
2575
2576 int start = 0;
2577
2578 unsigned int r;
2579 for (r = 0; r < row; r++)
2580 {
2581 const wxDataViewTreeNode* node = GetTreeNodeByRow(r);
2582 if (!node) return start;
2583
2584 wxDataViewItem item = node->GetItem();
2585
2586 if (node && !node->HasChildren())
2587 {
2588 // Yes, if the node does not have any child, it must be a leaf which
2589 // mean that it is a temporarily created by GetTreeNodeByRow
2590 wxDELETE(node);
2591 }
2592
2593 unsigned int cols = GetOwner()->GetColumnCount();
2594 unsigned int col;
2595 int height = m_lineHeight;
2596 for (col = 0; col < cols; col++)
2597 {
2598 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2599 if (column->IsHidden())
2600 continue; // skip it!
2601
2602 if ((col != 0) &&
2603 model->IsContainer(item) &&
2604 !model->HasContainerColumns(item))
2605 continue; // skip it!
2606
2607 wxDataViewRenderer *renderer =
2608 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2609 renderer->PrepareForItem(model, item, column->GetModelColumn());
2610
2611 height = wxMax( height, renderer->GetSize().y );
2612 }
2613
2614 start += height;
2615 }
2616
2617 return start;
2618 }
2619 else
2620 {
2621 return row * m_lineHeight;
2622 }
2623 }
2624
2625 int wxDataViewMainWindow::GetLineAt( unsigned int y ) const
2626 {
2627 const wxDataViewModel *model = GetOwner()->GetModel();
2628
2629 // check for the easy case first
2630 if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
2631 return y / m_lineHeight;
2632
2633 // TODO make more efficient
2634 unsigned int row = 0;
2635 unsigned int yy = 0;
2636 for (;;)
2637 {
2638 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2639 if (!node)
2640 {
2641 // not really correct...
2642 return row + ((y-yy) / m_lineHeight);
2643 }
2644
2645 wxDataViewItem item = node->GetItem();
2646
2647 if (node && !node->HasChildren())
2648 {
2649 // Yes, if the node does not have any child, it must be a leaf which
2650 // mean that it is a temporarily created by GetTreeNodeByRow
2651 wxDELETE(node);
2652 }
2653
2654 unsigned int cols = GetOwner()->GetColumnCount();
2655 unsigned int col;
2656 int height = m_lineHeight;
2657 for (col = 0; col < cols; col++)
2658 {
2659 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2660 if (column->IsHidden())
2661 continue; // skip it!
2662
2663 if ((col != 0) &&
2664 model->IsContainer(item) &&
2665 !model->HasContainerColumns(item))
2666 continue; // skip it!
2667
2668 wxDataViewRenderer *renderer =
2669 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2670 renderer->PrepareForItem(model, item, column->GetModelColumn());
2671
2672 height = wxMax( height, renderer->GetSize().y );
2673 }
2674
2675 yy += height;
2676 if (y < yy)
2677 return row;
2678
2679 row++;
2680 }
2681 }
2682
2683 int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
2684 {
2685 const wxDataViewModel *model = GetOwner()->GetModel();
2686
2687 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2688 {
2689 wxASSERT( !IsVirtualList() );
2690
2691 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2692 // wxASSERT( node );
2693 if (!node) return m_lineHeight;
2694
2695 wxDataViewItem item = node->GetItem();
2696
2697 if (node && !node->HasChildren())
2698 {
2699 // Yes, if the node does not have any child, it must be a leaf which
2700 // mean that it is a temporarily created by GetTreeNodeByRow
2701 wxDELETE(node);
2702 }
2703
2704 int height = m_lineHeight;
2705
2706 unsigned int cols = GetOwner()->GetColumnCount();
2707 unsigned int col;
2708 for (col = 0; col < cols; col++)
2709 {
2710 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2711 if (column->IsHidden())
2712 continue; // skip it!
2713
2714 if ((col != 0) &&
2715 model->IsContainer(item) &&
2716 !model->HasContainerColumns(item))
2717 continue; // skip it!
2718
2719 wxDataViewRenderer *renderer =
2720 const_cast<wxDataViewRenderer*>(column->GetRenderer());
2721 renderer->PrepareForItem(model, item, column->GetModelColumn());
2722
2723 height = wxMax( height, renderer->GetSize().y );
2724 }
2725
2726 return height;
2727 }
2728 else
2729 {
2730 return m_lineHeight;
2731 }
2732 }
2733
2734 class RowToItemJob: public DoJob
2735 {
2736 public:
2737 RowToItemJob( unsigned int row , int current )
2738 { this->row = row; this->current = current; }
2739 virtual ~RowToItemJob() {}
2740
2741 virtual int operator() ( wxDataViewTreeNode * node )
2742 {
2743 current ++;
2744 if( current == static_cast<int>(row))
2745 {
2746 ret = node->GetItem();
2747 return DoJob::OK;
2748 }
2749
2750 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2751 {
2752 current += node->GetSubTreeCount();
2753 return DoJob::IGR;
2754 }
2755 else
2756 {
2757 // If the current has no child node, we can find the desired item of the row
2758 // number directly.
2759 // This if can speed up finding in some case, and will has a very good effect
2760 // when it comes to list view
2761 if( node->GetNodes().GetCount() == 0)
2762 {
2763 int index = static_cast<int>(row) - current - 1;
2764 ret = wxDataViewItem(node->GetChildren().Item( index ));
2765 return DoJob::OK;
2766 }
2767 return DoJob::CONT;
2768 }
2769 }
2770
2771 virtual int operator() ( void * n )
2772 {
2773 current ++;
2774 if( current == static_cast<int>(row))
2775 {
2776 ret = wxDataViewItem( n );
2777 return DoJob::OK;
2778 }
2779 return DoJob::CONT;
2780 }
2781
2782 wxDataViewItem GetResult() const
2783 { return ret; }
2784
2785 private:
2786 unsigned int row;
2787 int current;
2788 wxDataViewItem ret;
2789 };
2790
2791 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const
2792 {
2793 if (IsVirtualList())
2794 {
2795 return wxDataViewItem( wxUIntToPtr(row+1) );
2796 }
2797 else
2798 {
2799 RowToItemJob job( row, -2 );
2800 Walker( m_root , job );
2801 return job.GetResult();
2802 }
2803 }
2804
2805 class RowToTreeNodeJob: public DoJob
2806 {
2807 public:
2808 RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
2809 {
2810 this->row = row;
2811 this->current = current;
2812 ret = NULL;
2813 parent = node;
2814 }
2815 virtual ~RowToTreeNodeJob(){ }
2816
2817 virtual int operator() ( wxDataViewTreeNode * node )
2818 {
2819 current ++;
2820 if( current == static_cast<int>(row))
2821 {
2822 ret = node;
2823 return DoJob::OK;
2824 }
2825
2826 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2827 {
2828 current += node->GetSubTreeCount();
2829 return DoJob::IGR;
2830 }
2831 else
2832 {
2833 parent = node;
2834
2835 // If the current node has no children, we can find the desired item of the
2836 // row number directly.
2837 // This if can speed up finding in some case, and will have a very good
2838 // effect for list views.
2839 if( node->GetNodes().GetCount() == 0)
2840 {
2841 int index = static_cast<int>(row) - current - 1;
2842 void * n = node->GetChildren().Item( index );
2843 ret = new wxDataViewTreeNode( parent );
2844 ret->SetItem( wxDataViewItem( n ));
2845 ret->SetHasChildren(false);
2846 return DoJob::OK;
2847 }
2848 return DoJob::CONT;
2849 }
2850 }
2851
2852 virtual int operator() ( void * n )
2853 {
2854 current ++;
2855 if( current == static_cast<int>(row))
2856 {
2857 ret = new wxDataViewTreeNode( parent );
2858 ret->SetItem( wxDataViewItem( n ));
2859 ret->SetHasChildren(false);
2860 return DoJob::OK;
2861 }
2862
2863 return DoJob::CONT;
2864 }
2865
2866 wxDataViewTreeNode * GetResult() const
2867 { return ret; }
2868
2869 private:
2870 unsigned int row;
2871 int current;
2872 wxDataViewTreeNode * ret;
2873 wxDataViewTreeNode * parent;
2874 };
2875
2876 wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) const
2877 {
2878 wxASSERT( !IsVirtualList() );
2879
2880 RowToTreeNodeJob job( row , -2, m_root );
2881 Walker( m_root , job );
2882 return job.GetResult();
2883 }
2884
2885 wxDataViewEvent wxDataViewMainWindow::SendExpanderEvent( wxEventType type,
2886 const wxDataViewItem & item )
2887 {
2888 wxWindow *parent = GetParent();
2889 wxDataViewEvent le(type, parent->GetId());
2890
2891 le.SetEventObject(parent);
2892 le.SetModel(GetOwner()->GetModel());
2893 le.SetItem( item );
2894
2895 parent->GetEventHandler()->ProcessEvent(le);
2896 return le;
2897 }
2898
2899 bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const
2900 {
2901 if (IsList())
2902 return false;
2903
2904 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2905 if (!node)
2906 return false;
2907
2908 if (!node->HasChildren())
2909 {
2910 delete node;
2911 return false;
2912 }
2913
2914 return node->IsOpen();
2915 }
2916
2917 bool wxDataViewMainWindow::HasChildren( unsigned int row ) const
2918 {
2919 if (IsList())
2920 return false;
2921
2922 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2923 if (!node)
2924 return false;
2925
2926 if (!node->HasChildren())
2927 {
2928 delete node;
2929 return false;
2930 }
2931
2932 return true;
2933 }
2934
2935 void wxDataViewMainWindow::Expand( unsigned int row )
2936 {
2937 if (IsList())
2938 return;
2939
2940 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2941 if (!node)
2942 return;
2943
2944 if (!node->HasChildren())
2945 {
2946 delete node;
2947 return;
2948 }
2949
2950 if (!node->IsOpen())
2951 {
2952 wxDataViewEvent e =
2953 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, node->GetItem());
2954
2955 // Check if the user prevent expanding
2956 if( e.GetSkipped() )
2957 return;
2958
2959 node->ToggleOpen();
2960
2961 // build the children of current node
2962 if( node->GetChildrenNumber() == 0 )
2963 {
2964 SortPrepare();
2965 ::BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
2966 }
2967
2968 // By expanding the node all row indices that are currently in the selection list
2969 // and are greater than our node have become invalid. So we have to correct that now.
2970 const unsigned rowAdjustment = node->GetSubTreeCount();
2971 for(unsigned i=0; i<m_selection.size(); ++i)
2972 {
2973 const unsigned testRow = m_selection[i];
2974 // all rows above us are not affected, so skip them
2975 if(testRow <= row)
2976 continue;
2977
2978 m_selection[i] += rowAdjustment;
2979 }
2980
2981 if(m_currentRow > row)
2982 ChangeCurrentRow(m_currentRow + rowAdjustment);
2983
2984 m_count = -1;
2985 UpdateDisplay();
2986 // Send the expanded event
2987 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
2988 }
2989 }
2990
2991 void wxDataViewMainWindow::Collapse(unsigned int row)
2992 {
2993 if (IsList())
2994 return;
2995
2996 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
2997 if (!node)
2998 return;
2999
3000 if (!node->HasChildren())
3001 {
3002 delete node;
3003 return;
3004 }
3005
3006 if (node->IsOpen())
3007 {
3008 wxDataViewEvent e =
3009 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem());
3010 if( e.GetSkipped() )
3011 return;
3012
3013 // Find out if there are selected items below the current node.
3014 bool selectCollapsingRow = false;
3015 const unsigned rowAdjustment = node->GetSubTreeCount();
3016 unsigned maxRowToBeTested = row + rowAdjustment;
3017 for(unsigned i=0; i<m_selection.size(); ++i)
3018 {
3019 const unsigned testRow = m_selection[i];
3020 if(testRow > row && testRow <= maxRowToBeTested)
3021 {
3022 selectCollapsingRow = true;
3023 // get out as soon as we have found a node that is selected
3024 break;
3025 }
3026 }
3027
3028 node->ToggleOpen();
3029
3030 // If the node to be closed has selected items the user won't see those any longer.
3031 // We select the collapsing node in this case.
3032 if(selectCollapsingRow)
3033 {
3034 SelectAllRows(false);
3035 ChangeCurrentRow(row);
3036 SelectRow(row, true);
3037 SendSelectionChangedEvent(GetItemByRow(row));
3038 }
3039 else
3040 {
3041 // if there were no selected items below our node we still need to "fix" the
3042 // selection list to adjust for the changing of the row indices.
3043 // We actually do the opposite of what we are doing in Expand().
3044 for(unsigned i=0; i<m_selection.size(); ++i)
3045 {
3046 const unsigned testRow = m_selection[i];
3047 // all rows above us are not affected, so skip them
3048 if(testRow <= row)
3049 continue;
3050
3051 m_selection[i] -= rowAdjustment;
3052 }
3053
3054 // if the "current row" is being collapsed away we change it to the current row ;-)
3055 if(m_currentRow > row && m_currentRow <= maxRowToBeTested)
3056 ChangeCurrentRow(row);
3057 else if(m_currentRow > row)
3058 ChangeCurrentRow(m_currentRow - rowAdjustment);
3059 }
3060
3061 m_count = -1;
3062 UpdateDisplay();
3063 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,node->GetItem());
3064 }
3065 }
3066
3067 wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
3068 {
3069 const wxDataViewModel * model = GetOwner()->GetModel();
3070 if( model == NULL )
3071 return NULL;
3072
3073 if (!item.IsOk())
3074 return m_root;
3075
3076 // Compose the parent-chain for the item we are looking for
3077 wxVector<wxDataViewItem> parentChain;
3078 wxDataViewItem it( item );
3079 while( it.IsOk() )
3080 {
3081 parentChain.push_back(it);
3082 it = model->GetParent(it);
3083 }
3084
3085 // Find the item along the parent-chain.
3086 // This algorithm is designed to speed up the node-finding method
3087 wxDataViewTreeNode* node = m_root;
3088 for( unsigned iter = parentChain.size()-1; ; --iter )
3089 {
3090 if( node->HasChildren() )
3091 {
3092 if( node->GetChildrenNumber() == 0 )
3093 {
3094 SortPrepare();
3095 ::BuildTreeHelper(model, node->GetItem(), node);
3096 }
3097
3098 const wxDataViewTreeNodes& nodes = node->GetNodes();
3099 bool found = false;
3100
3101 for (unsigned i = 0; i < nodes.GetCount(); ++i)
3102 {
3103 wxDataViewTreeNode* currentNode = nodes[i];
3104 if (currentNode->GetItem() == parentChain[iter])
3105 {
3106 if (currentNode->GetItem() == item)
3107 return currentNode;
3108
3109 node = currentNode;
3110 found = true;
3111 break;
3112 }
3113 }
3114 if (!found)
3115 return NULL;
3116 }
3117 else
3118 return NULL;
3119
3120 if ( !iter )
3121 break;
3122 }
3123 return NULL;
3124 }
3125
3126 void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item,
3127 wxDataViewColumn* &column )
3128 {
3129 wxDataViewColumn *col = NULL;
3130 unsigned int cols = GetOwner()->GetColumnCount();
3131 unsigned int colnum = 0;
3132 int x, y;
3133 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
3134 for (unsigned x_start = 0; colnum < cols; colnum++)
3135 {
3136 col = GetOwner()->GetColumnAt(colnum);
3137 if (col->IsHidden())
3138 continue; // skip it!
3139
3140 unsigned int w = col->GetWidth();
3141 if (x_start+w >= (unsigned int)x)
3142 break;
3143
3144 x_start += w;
3145 }
3146
3147 column = col;
3148 item = GetItemByRow( GetLineAt( y ) );
3149 }
3150
3151 wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item,
3152 const wxDataViewColumn* column )
3153 {
3154 int xpos = 0;
3155 int width = 0;
3156
3157 unsigned int cols = GetOwner()->GetColumnCount();
3158 // If column is null the loop will compute the combined width of all columns.
3159 // Otherwise, it will compute the x position of the column we are looking for.
3160 for (unsigned int i = 0; i < cols; i++)
3161 {
3162 wxDataViewColumn* col = GetOwner()->GetColumnAt( i );
3163
3164 if (col == column)
3165 break;
3166
3167 if (col->IsHidden())
3168 continue; // skip it!
3169
3170 xpos += col->GetWidth();
3171 width += col->GetWidth();
3172 }
3173
3174 if(column != 0)
3175 {
3176 // If we have a column, we need can get its width directly.
3177 if(column->IsHidden())
3178 width = 0;
3179 else
3180 width = column->GetWidth();
3181
3182 }
3183 else
3184 {
3185 // If we have no column, we reset the x position back to zero.
3186 xpos = 0;
3187 }
3188
3189 // we have to take an expander column into account and compute its indentation
3190 // to get the correct x position where the actual text is
3191 int indent = 0;
3192 int row = GetRowByItem(item);
3193 if (!IsList() && (column == 0 || GetOwner()->GetExpanderColumn() == column) )
3194 {
3195 wxDataViewTreeNode* node = GetTreeNodeByRow(row);
3196 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
3197 indent = indent + m_lineHeight; // use m_lineHeight as the width of the expander
3198
3199 if(!node->HasChildren())
3200 delete node;
3201 }
3202
3203 wxRect itemRect( xpos + indent,
3204 GetLineStart( row ),
3205 width - indent,
3206 GetLineHeight( row ) );
3207
3208 GetOwner()->CalcScrolledPosition( itemRect.x, itemRect.y,
3209 &itemRect.x, &itemRect.y );
3210
3211 return itemRect;
3212 }
3213
3214 int wxDataViewMainWindow::RecalculateCount()
3215 {
3216 if (IsVirtualList())
3217 {
3218 wxDataViewVirtualListModel *list_model =
3219 (wxDataViewVirtualListModel*) GetOwner()->GetModel();
3220
3221 return list_model->GetCount();
3222 }
3223 else
3224 {
3225 return m_root->GetSubTreeCount();
3226 }
3227 }
3228
3229 class ItemToRowJob : public DoJob
3230 {
3231 public:
3232 ItemToRowJob(const wxDataViewItem& item_, wxVector<wxDataViewItem>::reverse_iterator iter)
3233 : m_iter(iter),
3234 item(item_)
3235 {
3236 ret = -1;
3237 }
3238
3239 // Maybe binary search will help to speed up this process
3240 virtual int operator() ( wxDataViewTreeNode * node)
3241 {
3242 ret ++;
3243 if( node->GetItem() == item )
3244 {
3245 return DoJob::OK;
3246 }
3247
3248 if( node->GetItem() == *m_iter )
3249 {
3250 m_iter++;
3251 return DoJob::CONT;
3252 }
3253 else
3254 {
3255 ret += node->GetSubTreeCount();
3256 return DoJob::IGR;
3257 }
3258
3259 }
3260
3261 virtual int operator() ( void * n )
3262 {
3263 ret ++;
3264 if( n == item.GetID() )
3265 return DoJob::OK;
3266 return DoJob::CONT;
3267 }
3268
3269 // the row number is begin from zero
3270 int GetResult() const
3271 { return ret -1; }
3272
3273 private:
3274 wxVector<wxDataViewItem>::reverse_iterator m_iter;
3275 wxDataViewItem item;
3276 int ret;
3277
3278 };
3279
3280 int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
3281 {
3282 const wxDataViewModel * model = GetOwner()->GetModel();
3283 if( model == NULL )
3284 return -1;
3285
3286 if (IsVirtualList())
3287 {
3288 return wxPtrToUInt( item.GetID() ) -1;
3289 }
3290 else
3291 {
3292 if( !item.IsOk() )
3293 return -1;
3294
3295 // Compose the parent-chain of the item we are looking for
3296 wxVector<wxDataViewItem> parentChain;
3297 wxDataViewItem it( item );
3298 while( it.IsOk() )
3299 {
3300 parentChain.push_back(it);
3301 it = model->GetParent(it);
3302 }
3303
3304 // add an 'invalid' item to represent our 'invisible' root node
3305 parentChain.push_back(wxDataViewItem());
3306
3307 // the parent chain was created by adding the deepest parent first.
3308 // so if we want to start at the root node, we have to iterate backwards through the vector
3309 ItemToRowJob job( item, parentChain.rbegin() );
3310 Walker( m_root, job );
3311 return job.GetResult();
3312 }
3313 }
3314
3315 static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
3316 wxDataViewTreeNode * node)
3317 {
3318 if( !model->IsContainer( item ) )
3319 return;
3320
3321 wxDataViewItemArray children;
3322 unsigned int num = model->GetChildren( item, children);
3323
3324 unsigned int index = 0;
3325 while( index < num )
3326 {
3327 if( model->IsContainer( children[index] ) )
3328 {
3329 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
3330 n->SetItem(children[index]);
3331 n->SetHasChildren( true );
3332 node->AddNode( n );
3333 }
3334 else
3335 {
3336 node->AddLeaf( children[index].GetID() );
3337 }
3338 index ++;
3339 }
3340 node->SetSubTreeCount( num );
3341 wxDataViewTreeNode * n = node->GetParent();
3342 if( n != NULL)
3343 n->ChangeSubTreeCount(num);
3344
3345 }
3346
3347 void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
3348 {
3349 DestroyTree();
3350
3351 if (GetOwner()->GetModel()->IsVirtualListModel())
3352 {
3353 m_count = -1;
3354 return;
3355 }
3356
3357 m_root = new wxDataViewTreeNode( NULL );
3358 m_root->SetHasChildren(true);
3359
3360 // First we define a invalid item to fetch the top-level elements
3361 wxDataViewItem item;
3362 SortPrepare();
3363 BuildTreeHelper( model, item, m_root);
3364 m_count = -1;
3365 }
3366
3367 static void DestroyTreeHelper( wxDataViewTreeNode * node )
3368 {
3369 if( node->GetNodeNumber() != 0 )
3370 {
3371 int len = node->GetNodeNumber();
3372 wxDataViewTreeNodes& nodes = node->GetNodes();
3373 for (int i = 0; i < len; i++)
3374 DestroyTreeHelper(nodes[i]);
3375 }
3376 delete node;
3377 }
3378
3379 void wxDataViewMainWindow::DestroyTree()
3380 {
3381 if (!IsVirtualList())
3382 {
3383 ::DestroyTreeHelper(m_root);
3384 m_count = 0;
3385 m_root = NULL;
3386 }
3387 }
3388
3389 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
3390 {
3391 wxWindow * const parent = GetParent();
3392
3393 // propagate the char event upwards
3394 wxKeyEvent eventForParent(event);
3395 eventForParent.SetEventObject(parent);
3396 if ( parent->ProcessWindowEvent(eventForParent) )
3397 return;
3398
3399 if ( parent->HandleAsNavigationKey(event) )
3400 return;
3401
3402 // no item -> nothing to do
3403 if (!HasCurrentRow())
3404 {
3405 event.Skip();
3406 return;
3407 }
3408
3409 // don't use m_linesPerPage directly as it might not be computed yet
3410 const int pageSize = GetCountPerPage();
3411 wxCHECK_RET( pageSize, wxT("should have non zero page size") );
3412
3413 switch ( event.GetKeyCode() )
3414 {
3415 case WXK_RETURN:
3416 {
3417 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,
3418 parent->GetId());
3419 le.SetItem( GetItemByRow(m_currentRow) );
3420 le.SetEventObject(parent);
3421 le.SetModel(GetOwner()->GetModel());
3422
3423 parent->GetEventHandler()->ProcessEvent(le);
3424 }
3425 break;
3426
3427 case WXK_UP:
3428 if ( m_currentRow > 0 )
3429 OnArrowChar( m_currentRow - 1, event );
3430 break;
3431
3432 case WXK_DOWN:
3433 if ( m_currentRow + 1 < GetRowCount() )
3434 OnArrowChar( m_currentRow + 1, event );
3435 break;
3436 // Add the process for tree expanding/collapsing
3437 case WXK_LEFT:
3438 {
3439 if (IsList())
3440 break;
3441
3442 wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow);
3443 if (!node)
3444 break;
3445
3446 if (node->HasChildren() && node->IsOpen())
3447 {
3448 Collapse(m_currentRow);
3449 }
3450 else // if the node is already closed we move the selection to its parent
3451 {
3452 wxDataViewTreeNode *parent_node = node->GetParent();
3453
3454 if(!node->HasChildren())
3455 delete node;
3456
3457 if (parent_node)
3458 {
3459 int parent = GetRowByItem( parent_node->GetItem() );
3460 if ( parent >= 0 )
3461 {
3462 unsigned int row = m_currentRow;
3463 SelectRow( row, false);
3464 SelectRow( parent, true );
3465 ChangeCurrentRow( parent );
3466 GetOwner()->EnsureVisible( parent, -1 );
3467 SendSelectionChangedEvent( parent_node->GetItem() );
3468 }
3469 }
3470 }
3471 break;
3472 }
3473 case WXK_RIGHT:
3474 {
3475 if (!IsExpanded( m_currentRow ))
3476 Expand( m_currentRow );
3477 else
3478 {
3479 unsigned int row = m_currentRow;
3480 SelectRow( row, false );
3481 SelectRow( row + 1, true );
3482 ChangeCurrentRow( row + 1 );
3483 GetOwner()->EnsureVisible( row + 1, -1 );
3484 SendSelectionChangedEvent( GetItemByRow(row+1) );
3485 }
3486 break;
3487 }
3488 case WXK_END:
3489 {
3490 if (!IsEmpty())
3491 OnArrowChar( GetRowCount() - 1, event );
3492 break;
3493 }
3494 case WXK_HOME:
3495 if (!IsEmpty())
3496 OnArrowChar( 0, event );
3497 break;
3498
3499 case WXK_PAGEUP:
3500 {
3501 int steps = pageSize - 1;
3502 int index = m_currentRow - steps;
3503 if (index < 0)
3504 index = 0;
3505
3506 OnArrowChar( index, event );
3507 }
3508 break;
3509
3510 case WXK_PAGEDOWN:
3511 {
3512 int steps = pageSize - 1;
3513 unsigned int index = m_currentRow + steps;
3514 unsigned int count = GetRowCount();
3515 if ( index >= count )
3516 index = count - 1;
3517
3518 OnArrowChar( index, event );
3519 }
3520 break;
3521
3522 case WXK_F2:
3523 {
3524 if(m_selection.size() == 1)
3525 {
3526 // TODO: we need to revise that when we have a concept for a 'current column'
3527 GetOwner()->StartEditor(GetItemByRow(m_selection[0]), 0);
3528 }
3529 }
3530 break;
3531
3532 default:
3533 event.Skip();
3534 }
3535 }
3536
3537 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
3538 {
3539 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
3540 {
3541 // let the base handle mouse wheel events.
3542 event.Skip();
3543 return;
3544 }
3545
3546 // set the focus to ourself if any of the mouse buttons are pressed
3547 if(event.ButtonDown() && !HasFocus())
3548 SetFocus();
3549
3550 int x = event.GetX();
3551 int y = event.GetY();
3552 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
3553 wxDataViewColumn *col = NULL;
3554
3555 int xpos = 0;
3556 unsigned int cols = GetOwner()->GetColumnCount();
3557 unsigned int i;
3558 for (i = 0; i < cols; i++)
3559 {
3560 wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
3561 if (c->IsHidden())
3562 continue; // skip it!
3563
3564 if (x < xpos + c->GetWidth())
3565 {
3566 col = c;
3567 break;
3568 }
3569 xpos += c->GetWidth();
3570 }
3571 if (!col)
3572 {
3573 event.Skip();
3574 return;
3575 }
3576
3577 wxDataViewRenderer *cell = col->GetRenderer();
3578 unsigned int current = GetLineAt( y );
3579 if ((current >= GetRowCount()) || (x > GetEndOfLastCol()))
3580 {
3581 // Unselect all if below the last row ?
3582 event.Skip();
3583 return;
3584 }
3585
3586 // Test whether the mouse is hovering over the expander (a.k.a tree "+"
3587 // button) and also determine the offset of the real cell start, skipping
3588 // the indentation and the expander itself.
3589 bool hoverOverExpander = false;
3590 int expanderOffset = 0;
3591 if ((!IsList()) && (GetOwner()->GetExpanderColumn() == col))
3592 {
3593 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
3594 if( node!=NULL && node->HasChildren() )
3595 {
3596 int indent = node->GetIndentLevel();
3597 indent = GetOwner()->GetIndent()*indent;
3598
3599 // we make the rectangle we are looking in a bit bigger than the actual
3600 // visual expander so the user can hit that little thing reliably
3601 wxRect rect( xpos + indent,
3602 GetLineStart( current ) + (GetLineHeight(current) - m_lineHeight)/2,
3603 m_lineHeight, m_lineHeight);
3604
3605 expanderOffset = indent + m_lineHeight;
3606
3607 if( rect.Contains(x, y) )
3608 {
3609 // So the mouse is over the expander
3610 hoverOverExpander = true;
3611 if (m_underMouse && m_underMouse != node)
3612 {
3613 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
3614 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
3615 }
3616 if (m_underMouse != node)
3617 {
3618 // wxLogMessage("Do the row: %d", current);
3619 RefreshRow(current);
3620 }
3621 m_underMouse = node;
3622 }
3623 }
3624 if (node!=NULL && !node->HasChildren())
3625 delete node;
3626 }
3627 if (!hoverOverExpander)
3628 {
3629 if (m_underMouse != NULL)
3630 {
3631 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
3632 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
3633 m_underMouse = NULL;
3634 }
3635 }
3636
3637 wxDataViewModel *model = GetOwner()->GetModel();
3638
3639 #if wxUSE_DRAG_AND_DROP
3640 if (event.Dragging())
3641 {
3642 if (m_dragCount == 0)
3643 {
3644 // we have to report the raw, physical coords as we want to be
3645 // able to call HitTest(event.m_pointDrag) from the user code to
3646 // get the item being dragged
3647 m_dragStart = event.GetPosition();
3648 }
3649
3650 m_dragCount++;
3651
3652 if (m_dragCount != 3)
3653 return;
3654
3655 if (event.LeftIsDown())
3656 {
3657 m_owner->CalcUnscrolledPosition( m_dragStart.x, m_dragStart.y,
3658 &m_dragStart.x, &m_dragStart.y );
3659 unsigned int drag_item_row = GetLineAt( m_dragStart.y );
3660 wxDataViewItem item = GetItemByRow( drag_item_row );
3661
3662 // Notify cell about drag
3663 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
3664 event.SetEventObject( m_owner );
3665 event.SetItem( item );
3666 event.SetModel( model );
3667 if (!m_owner->HandleWindowEvent( event ))
3668 return;
3669
3670 if (!event.IsAllowed())
3671 return;
3672
3673 wxDataObject *obj = event.GetDataObject();
3674 if (!obj)
3675 return;
3676
3677 wxDataViewDropSource drag( this, drag_item_row );
3678 drag.SetData( *obj );
3679 /* wxDragResult res = */ drag.DoDragDrop();
3680 delete obj;
3681 }
3682 return;
3683 }
3684 else
3685 {
3686 m_dragCount = 0;
3687 }
3688 #endif // wxUSE_DRAG_AND_DROP
3689
3690 bool simulateClick = false;
3691
3692 if (event.ButtonDClick())
3693 {
3694 m_renameTimer->Stop();
3695 m_lastOnSame = false;
3696 }
3697
3698 wxDataViewItem item = GetItemByRow(current);
3699 bool ignore_other_columns =
3700 ((GetOwner()->GetExpanderColumn() != col) &&
3701 (model->IsContainer(item)) &&
3702 (!model->HasContainerColumns(item)));
3703
3704 if (event.LeftDClick())
3705 {
3706 if(hoverOverExpander)
3707 {
3708 // a double click on the expander will be converted into a "simulated" normal click
3709 simulateClick = true;
3710 }
3711 else if ( current == m_lineLastClicked )
3712 {
3713 if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE))
3714 {
3715 const unsigned colIdx = col->GetModelColumn();
3716
3717 cell->PrepareForItem(model, item, colIdx);
3718
3719 wxRect cell_rect( xpos, GetLineStart( current ),
3720 col->GetWidth(), GetLineHeight( current ) );
3721 cell->WXOnActivate( cell_rect, model, item, colIdx );
3722 }
3723 else
3724 {
3725 wxWindow *parent = GetParent();
3726 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
3727 le.SetItem( item );
3728 le.SetColumn( col->GetModelColumn() );
3729 le.SetDataViewColumn( col );
3730 le.SetEventObject(parent);
3731 le.SetModel(GetOwner()->GetModel());
3732
3733 parent->GetEventHandler()->ProcessEvent(le);
3734 }
3735 return;
3736 }
3737 else
3738 {
3739 // The first click was on another item, so don't interpret this as
3740 // a double click, but as a simple click instead
3741 simulateClick = true;
3742 }
3743 }
3744
3745 if (event.LeftUp() && !hoverOverExpander)
3746 {
3747 if (m_lineSelectSingleOnUp != (unsigned int)-1)
3748 {
3749 // select single line
3750 SelectAllRows( false );
3751 SelectRow( m_lineSelectSingleOnUp, true );
3752 SendSelectionChangedEvent( GetItemByRow(m_lineSelectSingleOnUp) );
3753 }
3754
3755 // If the user click the expander, we do not do editing even if the column
3756 // with expander are editable
3757 if (m_lastOnSame && !ignore_other_columns)
3758 {
3759 if ((col == m_currentCol) && (current == m_currentRow) &&
3760 (cell->GetMode() & wxDATAVIEW_CELL_EDITABLE) )
3761 {
3762 m_renameTimer->Start( 100, true );
3763 }
3764 }
3765
3766 m_lastOnSame = false;
3767 m_lineSelectSingleOnUp = (unsigned int)-1;
3768 }
3769 else if(!event.LeftUp())
3770 {
3771 // This is necessary, because after a DnD operation in
3772 // from and to ourself, the up event is swallowed by the
3773 // DnD code. So on next non-up event (which means here and
3774 // now) m_lineSelectSingleOnUp should be reset.
3775 m_lineSelectSingleOnUp = (unsigned int)-1;
3776 }
3777
3778 if (event.RightDown())
3779 {
3780 m_lineBeforeLastClicked = m_lineLastClicked;
3781 m_lineLastClicked = current;
3782
3783 // If the item is already selected, do not update the selection.
3784 // Multi-selections should not be cleared if a selected item is clicked.
3785 if (!IsRowSelected(current))
3786 {
3787 SelectAllRows(false);
3788 const unsigned oldCurrent = m_currentRow;
3789 ChangeCurrentRow(current);
3790 SelectRow(m_currentRow,true);
3791 RefreshRow(oldCurrent);
3792 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3793 }
3794 }
3795 else if (event.RightUp())
3796 {
3797 wxVariant value;
3798 model->GetValue( value, item, col->GetModelColumn() );
3799 wxWindow *parent = GetParent();
3800 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
3801 le.SetItem( item );
3802 le.SetColumn( col->GetModelColumn() );
3803 le.SetDataViewColumn( col );
3804 le.SetEventObject(parent);
3805 le.SetModel(GetOwner()->GetModel());
3806 le.SetValue(value);
3807 parent->GetEventHandler()->ProcessEvent(le);
3808 }
3809 else if (event.MiddleDown())
3810 {
3811 }
3812
3813 if((event.LeftDown() || simulateClick) && hoverOverExpander)
3814 {
3815 wxDataViewTreeNode* node = GetTreeNodeByRow(current);
3816
3817 // hoverOverExpander being true tells us that our node must be
3818 // valid and have children.
3819 // So we don't need any extra checks.
3820 if( node->IsOpen() )
3821 Collapse(current);
3822 else
3823 Expand(current);
3824 }
3825 else if ((event.LeftDown() || simulateClick) && !hoverOverExpander)
3826 {
3827 m_lineBeforeLastClicked = m_lineLastClicked;
3828 m_lineLastClicked = current;
3829
3830 unsigned int oldCurrentRow = m_currentRow;
3831 bool oldWasSelected = IsRowSelected(m_currentRow);
3832
3833 bool cmdModifierDown = event.CmdDown();
3834 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
3835 {
3836 if ( IsSingleSel() || !IsRowSelected(current) )
3837 {
3838 SelectAllRows( false );
3839 ChangeCurrentRow(current);
3840 SelectRow(m_currentRow,true);
3841 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3842 }
3843 else // multi sel & current is highlighted & no mod keys
3844 {
3845 m_lineSelectSingleOnUp = current;
3846 ChangeCurrentRow(current); // change focus
3847 }
3848 }
3849 else // multi sel & either ctrl or shift is down
3850 {
3851 if (cmdModifierDown)
3852 {
3853 ChangeCurrentRow(current);
3854 ReverseRowSelection(m_currentRow);
3855 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
3856 }
3857 else if (event.ShiftDown())
3858 {
3859 ChangeCurrentRow(current);
3860
3861 unsigned int lineFrom = oldCurrentRow,
3862 lineTo = current;
3863
3864 if ( lineTo < lineFrom )
3865 {
3866 lineTo = lineFrom;
3867 lineFrom = m_currentRow;
3868 }
3869
3870 SelectRows(lineFrom, lineTo, true);
3871 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
3872 }
3873 else // !ctrl, !shift
3874 {
3875 // test in the enclosing if should make it impossible
3876 wxFAIL_MSG( wxT("how did we get here?") );
3877 }
3878 }
3879
3880 if (m_currentRow != oldCurrentRow)
3881 RefreshRow( oldCurrentRow );
3882
3883 wxDataViewColumn *oldCurrentCol = m_currentCol;
3884
3885 // Update selection here...
3886 m_currentCol = col;
3887
3888 m_lastOnSame = !simulateClick && ((col == oldCurrentCol) &&
3889 (current == oldCurrentRow)) && oldWasSelected;
3890
3891 // Call LeftClick after everything else as under GTK+
3892 if (cell->GetMode() & wxDATAVIEW_CELL_ACTIVATABLE)
3893 {
3894 // notify cell about click
3895 cell->PrepareForItem(model, item, col->GetModelColumn());
3896
3897 wxRect cell_rect( xpos + expanderOffset,
3898 GetLineStart( current ),
3899 col->GetWidth() - expanderOffset,
3900 GetLineHeight( current ) );
3901
3902 // Report position relative to the cell's custom area, i.e.
3903 // no the entire space as given by the control but the one
3904 // used by the renderer after calculation of alignment etc.
3905
3906 // adjust the rectangle ourselves to account for the alignment
3907 wxRect rectItem = cell_rect;
3908 const int align = cell->GetAlignment();
3909 if ( align != wxDVR_DEFAULT_ALIGNMENT )
3910 {
3911 const wxSize size = cell->GetSize();
3912
3913 if ( size.x >= 0 && size.x < cell_rect.width )
3914 {
3915 if ( align & wxALIGN_CENTER_HORIZONTAL )
3916 rectItem.x += (cell_rect.width - size.x)/2;
3917 else if ( align & wxALIGN_RIGHT )
3918 rectItem.x += cell_rect.width - size.x;
3919 // else: wxALIGN_LEFT is the default
3920 }
3921
3922 if ( size.y >= 0 && size.y < cell_rect.height )
3923 {
3924 if ( align & wxALIGN_CENTER_VERTICAL )
3925 rectItem.y += (cell_rect.height - size.y)/2;
3926 else if ( align & wxALIGN_BOTTOM )
3927 rectItem.y += cell_rect.height - size.y;
3928 // else: wxALIGN_TOP is the default
3929 }
3930 }
3931
3932 wxPoint pos( event.GetPosition() );
3933 pos.x -= rectItem.x;
3934 pos.y -= rectItem.y;
3935
3936 m_owner->CalcUnscrolledPosition( pos.x, pos.y, &pos.x, &pos.y );
3937
3938 /* ignore ret */ cell->WXOnLeftClick( pos, cell_rect,
3939 model, item, col->GetModelColumn());
3940 }
3941 }
3942 }
3943
3944 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
3945 {
3946 m_hasFocus = true;
3947
3948 if (HasCurrentRow())
3949 Refresh();
3950
3951 event.Skip();
3952 }
3953
3954 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
3955 {
3956 m_hasFocus = false;
3957
3958 if (HasCurrentRow())
3959 Refresh();
3960
3961 event.Skip();
3962 }
3963
3964 //-----------------------------------------------------------------------------
3965 // wxDataViewCtrl
3966 //-----------------------------------------------------------------------------
3967
3968 WX_DEFINE_LIST(wxDataViewColumnList)
3969
3970 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
3971 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
3972 EVT_SIZE(wxDataViewCtrl::OnSize)
3973 END_EVENT_TABLE()
3974
3975 wxDataViewCtrl::~wxDataViewCtrl()
3976 {
3977 if (m_notifier)
3978 GetModel()->RemoveNotifier( m_notifier );
3979
3980 m_cols.Clear();
3981 m_colsBestWidths.clear();
3982 }
3983
3984 void wxDataViewCtrl::Init()
3985 {
3986 m_cols.DeleteContents(true);
3987 m_notifier = NULL;
3988
3989 // No sorting column at start
3990 m_sortingColumnIdx = wxNOT_FOUND;
3991
3992 m_headerArea = NULL;
3993 }
3994
3995 bool wxDataViewCtrl::Create(wxWindow *parent,
3996 wxWindowID id,
3997 const wxPoint& pos,
3998 const wxSize& size,
3999 long style,
4000 const wxValidator& validator,
4001 const wxString& name)
4002 {
4003 // if ( (style & wxBORDER_MASK) == 0)
4004 // style |= wxBORDER_SUNKEN;
4005
4006 Init();
4007
4008 if (!wxControl::Create( parent, id, pos, size,
4009 style | wxScrolledWindowStyle, validator, name))
4010 return false;
4011
4012 SetInitialSize(size);
4013
4014 #ifdef __WXMAC__
4015 MacSetClipChildren( true );
4016 #endif
4017
4018 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
4019
4020 // We use the cursor keys for moving the selection, not scrolling, so call
4021 // this method to ensure wxScrollHelperEvtHandler doesn't catch all
4022 // keyboard events forwarded to us from wxListMainWindow.
4023 DisableKeyboardScrolling();
4024
4025 if (HasFlag(wxDV_NO_HEADER))
4026 m_headerArea = NULL;
4027 else
4028 m_headerArea = new wxDataViewHeaderWindow(this);
4029
4030 SetTargetWindow( m_clientArea );
4031
4032 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
4033 if (m_headerArea)
4034 sizer->Add( m_headerArea, 0, wxGROW );
4035 sizer->Add( m_clientArea, 1, wxGROW );
4036 SetSizer( sizer );
4037
4038 return true;
4039 }
4040
4041 wxBorder wxDataViewCtrl::GetDefaultBorder() const
4042 {
4043 return wxBORDER_THEME;
4044 }
4045
4046 #ifdef __WXMSW__
4047 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
4048 WXWPARAM wParam,
4049 WXLPARAM lParam)
4050 {
4051 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
4052
4053 #ifndef __WXWINCE__
4054 // we need to process arrows ourselves for scrolling
4055 if ( nMsg == WM_GETDLGCODE )
4056 {
4057 rc |= DLGC_WANTARROWS;
4058 }
4059 #endif
4060
4061 return rc;
4062 }
4063 #endif
4064
4065 wxSize wxDataViewCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
4066 {
4067 wxSize newsize = size;
4068 if (!HasFlag(wxDV_NO_HEADER) && (m_headerArea))
4069 newsize.y -= m_headerArea->GetSize().y;
4070
4071 return newsize;
4072 }
4073
4074 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
4075 {
4076 // We need to override OnSize so that our scrolled
4077 // window a) does call Layout() to use sizers for
4078 // positioning the controls but b) does not query
4079 // the sizer for their size and use that for setting
4080 // the scrollable area as set that ourselves by
4081 // calling SetScrollbar() further down.
4082
4083 Layout();
4084
4085 AdjustScrollbars();
4086
4087 // We must redraw the headers if their height changed. Normally this
4088 // shouldn't happen as the control shouldn't let itself be resized beneath
4089 // its minimal height but avoid the display artefacts that appear if it
4090 // does happen, e.g. because there is really not enough vertical space.
4091 if ( !HasFlag(wxDV_NO_HEADER) && m_headerArea &&
4092 m_headerArea->GetSize().y <= m_headerArea->GetBestSize(). y )
4093 {
4094 m_headerArea->Refresh();
4095 }
4096 }
4097
4098 void wxDataViewCtrl::SetFocus()
4099 {
4100 if (m_clientArea)
4101 m_clientArea->SetFocus();
4102 }
4103
4104 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
4105 {
4106 if (!wxDataViewCtrlBase::AssociateModel( model ))
4107 return false;
4108
4109 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
4110
4111 model->AddNotifier( m_notifier );
4112
4113 m_clientArea->DestroyTree();
4114
4115 m_clientArea->BuildTree(model);
4116
4117 m_clientArea->UpdateDisplay();
4118
4119 return true;
4120 }
4121
4122 #if wxUSE_DRAG_AND_DROP
4123
4124 bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format )
4125 {
4126 return m_clientArea->EnableDragSource( format );
4127 }
4128
4129 bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format )
4130 {
4131 return m_clientArea->EnableDropTarget( format );
4132 }
4133
4134 #endif // wxUSE_DRAG_AND_DROP
4135
4136 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
4137 {
4138 if (!wxDataViewCtrlBase::AppendColumn(col))
4139 return false;
4140
4141 m_cols.Append( col );
4142 m_colsBestWidths.push_back(0);
4143 OnColumnsCountChanged();
4144 return true;
4145 }
4146
4147 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
4148 {
4149 if (!wxDataViewCtrlBase::PrependColumn(col))
4150 return false;
4151
4152 m_cols.Insert( col );
4153 m_colsBestWidths.insert(m_colsBestWidths.begin(), 0);
4154 OnColumnsCountChanged();
4155 return true;
4156 }
4157
4158 bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
4159 {
4160 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
4161 return false;
4162
4163 m_cols.Insert( pos, col );
4164 m_colsBestWidths.insert(m_colsBestWidths.begin() + pos, 0);
4165 OnColumnsCountChanged();
4166 return true;
4167 }
4168
4169 void wxDataViewCtrl::OnColumnChange(unsigned int idx)
4170 {
4171 if ( m_headerArea )
4172 m_headerArea->UpdateColumn(idx);
4173
4174 m_clientArea->UpdateDisplay();
4175 }
4176
4177 void wxDataViewCtrl::OnColumnsCountChanged()
4178 {
4179 if (m_headerArea)
4180 m_headerArea->SetColumnCount(GetColumnCount());
4181
4182 m_clientArea->UpdateDisplay();
4183 }
4184
4185 void wxDataViewCtrl::DoSetExpanderColumn()
4186 {
4187 m_clientArea->UpdateDisplay();
4188 }
4189
4190 void wxDataViewCtrl::DoSetIndent()
4191 {
4192 m_clientArea->UpdateDisplay();
4193 }
4194
4195 unsigned int wxDataViewCtrl::GetColumnCount() const
4196 {
4197 return m_cols.GetCount();
4198 }
4199
4200 bool wxDataViewCtrl::SetRowHeight( int lineHeight )
4201 {
4202 if ( !m_clientArea )
4203 return false;
4204
4205 m_clientArea->SetRowHeight(lineHeight);
4206
4207 return true;
4208 }
4209
4210 wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
4211 {
4212 return m_cols[idx];
4213 }
4214
4215 wxDataViewColumn *wxDataViewCtrl::GetColumnAt(unsigned int pos) const
4216 {
4217 // columns can't be reordered if there is no header window which allows
4218 // to do this
4219 const unsigned idx = m_headerArea ? m_headerArea->GetColumnsOrder()[pos]
4220 : pos;
4221
4222 return GetColumn(idx);
4223 }
4224
4225 int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn *column) const
4226 {
4227 const unsigned count = m_cols.size();
4228 for ( unsigned n = 0; n < count; n++ )
4229 {
4230 if ( m_cols[n] == column )
4231 return n;
4232 }
4233
4234 return wxNOT_FOUND;
4235 }
4236
4237 unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
4238 {
4239 if ( m_colsBestWidths[idx] != 0 )
4240 return m_colsBestWidths[idx];
4241
4242 const int count = m_clientArea->GetRowCount();
4243 wxDataViewColumn *column = GetColumn(idx);
4244 wxDataViewRenderer *renderer =
4245 const_cast<wxDataViewRenderer*>(column->GetRenderer());
4246
4247 class MaxWidthCalculator
4248 {
4249 public:
4250 MaxWidthCalculator(wxDataViewMainWindow *clientArea,
4251 wxDataViewRenderer *renderer,
4252 const wxDataViewModel *model,
4253 unsigned column)
4254 : m_width(0),
4255 m_clientArea(clientArea),
4256 m_renderer(renderer),
4257 m_model(model),
4258 m_column(column)
4259 {
4260 }
4261
4262 void UpdateWithWidth(int width)
4263 {
4264 m_width = wxMax(m_width, width);
4265 }
4266
4267 void UpdateWithRow(int row)
4268 {
4269 wxDataViewItem item = m_clientArea->GetItemByRow(row);
4270 m_renderer->PrepareForItem(m_model, item, m_column);
4271 m_width = wxMax(m_width, m_renderer->GetSize().x);
4272 }
4273
4274 int GetMaxWidth() const { return m_width; }
4275
4276 private:
4277 int m_width;
4278 wxDataViewMainWindow *m_clientArea;
4279 wxDataViewRenderer *m_renderer;
4280 const wxDataViewModel *m_model;
4281 unsigned m_column;
4282 };
4283
4284 MaxWidthCalculator calculator(m_clientArea, renderer,
4285 GetModel(), column->GetModelColumn());
4286
4287 if ( m_headerArea )
4288 {
4289 int header_width = m_headerArea->GetTextExtent(column->GetTitle()).x;
4290 // Labels on native MSW header are indented on both sides
4291 header_width +=
4292 wxRendererNative::Get().GetHeaderButtonMargin(m_headerArea);
4293 calculator.UpdateWithWidth(header_width);
4294 }
4295
4296 // The code below deserves some explanation. For very large controls, we
4297 // simply can't afford to calculate sizes for all items, it takes too
4298 // long. So the best we can do is to check the first and the last N/2
4299 // items in the control for some sufficiently large N and calculate best
4300 // sizes from that. That can result in the calculated best width being too
4301 // small for some outliers, but it's better to get slightly imperfect
4302 // result than to wait several seconds after every update. To avoid highly
4303 // visible miscalculations, we also include all currently visible items
4304 // no matter what. Finally, the value of N is determined dynamically by
4305 // measuring how much time we spent on the determining item widths so far.
4306
4307 #if wxUSE_STOPWATCH
4308 int top_part_end = count;
4309 static const long CALC_TIMEOUT = 20/*ms*/;
4310 // don't call wxStopWatch::Time() too often
4311 static const unsigned CALC_CHECK_FREQ = 100;
4312 wxStopWatch timer;
4313 #else
4314 // use some hard-coded limit, that's the best we can do without timer
4315 int top_part_end = wxMin(500, count);
4316 #endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH
4317
4318 int row = 0;
4319
4320 for ( row = 0; row < top_part_end; row++ )
4321 {
4322 #if wxUSE_STOPWATCH
4323 if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 &&
4324 timer.Time() > CALC_TIMEOUT )
4325 break;
4326 #endif // wxUSE_STOPWATCH
4327 calculator.UpdateWithRow(row);
4328 }
4329
4330 // row is the first unmeasured item now; that's our value of N/2
4331
4332 if ( row < count )
4333 {
4334 top_part_end = row;
4335
4336 // add bottom N/2 items now:
4337 const int bottom_part_start = wxMax(row, count - row);
4338 for ( row = bottom_part_start; row < count; row++ )
4339 {
4340 calculator.UpdateWithRow(row);
4341 }
4342
4343 // finally, include currently visible items in the calculation:
4344 const wxPoint origin = CalcUnscrolledPosition(wxPoint(0, 0));
4345 int first_visible = m_clientArea->GetLineAt(origin.y);
4346 int last_visible = m_clientArea->GetLineAt(origin.y + GetClientSize().y);
4347
4348 first_visible = wxMax(first_visible, top_part_end);
4349 last_visible = wxMin(bottom_part_start, last_visible);
4350
4351 for ( row = first_visible; row < last_visible; row++ )
4352 {
4353 calculator.UpdateWithRow(row);
4354 }
4355
4356 wxLogTrace("dataview",
4357 "determined best size from %d top, %d bottom plus %d more visible items out of %d total",
4358 top_part_end,
4359 count - bottom_part_start,
4360 wxMax(0, last_visible - first_visible),
4361 count);
4362 }
4363
4364 int max_width = calculator.GetMaxWidth();
4365 if ( max_width > 0 )
4366 max_width += 2 * PADDING_RIGHTLEFT;
4367
4368 const_cast<wxDataViewCtrl*>(this)->m_colsBestWidths[idx] = max_width;
4369 return max_width;
4370 }
4371
4372 void wxDataViewCtrl::ColumnMoved(wxDataViewColumn * WXUNUSED(col),
4373 unsigned int WXUNUSED(new_pos))
4374 {
4375 // do _not_ reorder m_cols elements here, they should always be in the
4376 // order in which columns were added, we only display the columns in
4377 // different order
4378 m_clientArea->UpdateDisplay();
4379 }
4380
4381 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
4382 {
4383 wxDataViewColumnList::compatibility_iterator ret = m_cols.Find( column );
4384 if (!ret)
4385 return false;
4386
4387 m_colsBestWidths.erase(m_colsBestWidths.begin() + GetColumnIndex(column));
4388 m_cols.Erase(ret);
4389 OnColumnsCountChanged();
4390
4391 return true;
4392 }
4393
4394 bool wxDataViewCtrl::ClearColumns()
4395 {
4396 m_cols.Clear();
4397 m_colsBestWidths.clear();
4398 OnColumnsCountChanged();
4399 return true;
4400 }
4401
4402 void wxDataViewCtrl::UpdateColBestWidth(int idx)
4403 {
4404 m_colsBestWidths[idx] = 0;
4405
4406 if ( m_headerArea )
4407 m_headerArea->UpdateColumn(idx);
4408 }
4409
4410 void wxDataViewCtrl::UpdateColBestWidths()
4411 {
4412 m_colsBestWidths.clear();
4413 m_colsBestWidths.resize(m_cols.size());
4414
4415 if ( m_headerArea )
4416 {
4417 const unsigned cols = m_headerArea->GetColumnCount();
4418 for ( unsigned i = 0; i < cols; i++ )
4419 m_headerArea->UpdateColumn(i);
4420 }
4421 }
4422
4423 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
4424 {
4425 #if 1
4426 unsigned int len = GetColumnCount();
4427 for ( unsigned int i = 0; i < len; i++ )
4428 {
4429 wxDataViewColumn * col = GetColumnAt(i);
4430 if (column==col)
4431 return i;
4432 }
4433
4434 return wxNOT_FOUND;
4435 #else
4436 // This returns the position in pixels which is not what we want.
4437 int ret = 0,
4438 dummy = 0;
4439 unsigned int len = GetColumnCount();
4440 for ( unsigned int i = 0; i < len; i++ )
4441 {
4442 wxDataViewColumn * col = GetColumnAt(i);
4443 if (col->IsHidden())
4444 continue;
4445 ret += col->GetWidth();
4446 if (column==col)
4447 {
4448 CalcScrolledPosition( ret, dummy, &ret, &dummy );
4449 break;
4450 }
4451 }
4452 return ret;
4453 #endif
4454 }
4455
4456 wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
4457 {
4458 return m_sortingColumnIdx == wxNOT_FOUND ? NULL
4459 : GetColumn(m_sortingColumnIdx);
4460 }
4461
4462 wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const
4463 {
4464 return GetItemByRow(m_clientArea->GetCurrentRow());
4465 }
4466
4467 void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item)
4468 {
4469 const int row = m_clientArea->GetRowByItem(item);
4470
4471 const unsigned oldCurrent = m_clientArea->GetCurrentRow();
4472 if ( static_cast<unsigned>(row) != oldCurrent )
4473 {
4474 m_clientArea->ChangeCurrentRow(row);
4475 m_clientArea->RefreshRow(oldCurrent);
4476 m_clientArea->RefreshRow(row);
4477 }
4478 }
4479
4480 int wxDataViewCtrl::GetSelectedItemsCount() const
4481 {
4482 return m_clientArea->GetSelections().size();
4483 }
4484
4485 int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
4486 {
4487 sel.Empty();
4488 const wxDataViewSelection& selections = m_clientArea->GetSelections();
4489
4490 const size_t len = selections.size();
4491 for ( size_t i = 0; i < len; i++ )
4492 {
4493 wxDataViewItem item = m_clientArea->GetItemByRow(selections[i]);
4494 if ( item.IsOk() )
4495 {
4496 sel.Add(item);
4497 }
4498 else
4499 {
4500 wxFAIL_MSG( "invalid item in selection - bad internal state" );
4501 }
4502 }
4503
4504 return sel.size();
4505 }
4506
4507 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
4508 {
4509 wxDataViewSelection selection(wxDataViewSelectionCmp);
4510
4511 wxDataViewItem last_parent;
4512
4513 int len = sel.GetCount();
4514 for( int i = 0; i < len; i ++ )
4515 {
4516 wxDataViewItem item = sel[i];
4517 wxDataViewItem parent = GetModel()->GetParent( item );
4518 if (parent)
4519 {
4520 if (parent != last_parent)
4521 ExpandAncestors(item);
4522 }
4523
4524 last_parent = parent;
4525 int row = m_clientArea->GetRowByItem( item );
4526 if( row >= 0 )
4527 selection.Add( static_cast<unsigned int>(row) );
4528 }
4529
4530 m_clientArea->SetSelections( selection );
4531 }
4532
4533 void wxDataViewCtrl::Select( const wxDataViewItem & item )
4534 {
4535 ExpandAncestors( item );
4536
4537 int row = m_clientArea->GetRowByItem( item );
4538 if( row >= 0 )
4539 {
4540 // Unselect all rows before select another in the single select mode
4541 if (m_clientArea->IsSingleSel())
4542 m_clientArea->SelectAllRows(false);
4543
4544 m_clientArea->SelectRow(row, true);
4545
4546 // Also set focus to the selected item
4547 m_clientArea->ChangeCurrentRow( row );
4548 }
4549 }
4550
4551 void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
4552 {
4553 int row = m_clientArea->GetRowByItem( item );
4554 if( row >= 0 )
4555 m_clientArea->SelectRow(row, false);
4556 }
4557
4558 bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
4559 {
4560 int row = m_clientArea->GetRowByItem( item );
4561 if( row >= 0 )
4562 {
4563 return m_clientArea->IsRowSelected(row);
4564 }
4565 return false;
4566 }
4567
4568 void wxDataViewCtrl::SelectAll()
4569 {
4570 m_clientArea->SelectAllRows(true);
4571 }
4572
4573 void wxDataViewCtrl::UnselectAll()
4574 {
4575 m_clientArea->SelectAllRows(false);
4576 }
4577
4578 void wxDataViewCtrl::EnsureVisible( int row, int column )
4579 {
4580 if( row < 0 )
4581 row = 0;
4582 if( row > (int) m_clientArea->GetRowCount() )
4583 row = m_clientArea->GetRowCount();
4584
4585 int first = m_clientArea->GetFirstVisibleRow();
4586 int last = m_clientArea->GetLastVisibleRow();
4587 if( row < first )
4588 m_clientArea->ScrollTo( row, column );
4589 else if( row > last )
4590 m_clientArea->ScrollTo( row - last + first, column );
4591 else
4592 m_clientArea->ScrollTo( first, column );
4593 }
4594
4595 void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataViewColumn * column )
4596 {
4597 ExpandAncestors( item );
4598
4599 m_clientArea->RecalculateDisplay();
4600
4601 int row = m_clientArea->GetRowByItem(item);
4602 if( row >= 0 )
4603 {
4604 if( column == NULL )
4605 EnsureVisible(row, -1);
4606 else
4607 EnsureVisible( row, GetColumnIndex(column) );
4608 }
4609
4610 }
4611
4612 void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item,
4613 wxDataViewColumn* &column ) const
4614 {
4615 m_clientArea->HitTest(point, item, column);
4616 }
4617
4618 wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item,
4619 const wxDataViewColumn* column ) const
4620 {
4621 return m_clientArea->GetItemRect(item, column);
4622 }
4623
4624 wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
4625 {
4626 return m_clientArea->GetItemByRow( row );
4627 }
4628
4629 int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
4630 {
4631 return m_clientArea->GetRowByItem( item );
4632 }
4633
4634 void wxDataViewCtrl::Expand( const wxDataViewItem & item )
4635 {
4636 ExpandAncestors( item );
4637
4638 int row = m_clientArea->GetRowByItem( item );
4639 if (row != -1)
4640 m_clientArea->Expand(row);
4641 }
4642
4643 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
4644 {
4645 int row = m_clientArea->GetRowByItem( item );
4646 if (row != -1)
4647 m_clientArea->Collapse(row);
4648 }
4649
4650 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
4651 {
4652 int row = m_clientArea->GetRowByItem( item );
4653 if (row != -1)
4654 return m_clientArea->IsExpanded(row);
4655 return false;
4656 }
4657
4658 void wxDataViewCtrl::StartEditor( const wxDataViewItem & item, unsigned int column )
4659 {
4660 wxDataViewColumn* col = GetColumn( column );
4661 if (!col)
4662 return;
4663
4664 wxRect itemRect = GetItemRect(item, col);
4665 wxDataViewRenderer* renderer = col->GetRenderer();
4666 if (renderer->GetMode() == wxDATAVIEW_CELL_EDITABLE)
4667 renderer->StartEditing(item, itemRect);
4668 }
4669
4670 #endif // !wxUSE_GENERICDATAVIEWCTRL
4671
4672 #endif // wxUSE_DATAVIEWCTRL