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