]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
2f39cbcab82a87d4d6a261bb41ceb753df63dd09
[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 #endif
38
39 #include "wx/stockitem.h"
40 #include "wx/calctrl.h"
41 #include "wx/popupwin.h"
42 #include "wx/renderer.h"
43 #include "wx/dcbuffer.h"
44 #include "wx/icon.h"
45 #include "wx/list.h"
46 #include "wx/listimpl.cpp"
47 #include "wx/imaglist.h"
48 #include "wx/headerctrl.h"
49 #include "wx/dnd.h"
50
51 //-----------------------------------------------------------------------------
52 // classes
53 //-----------------------------------------------------------------------------
54
55 class wxDataViewCtrl;
56
57 static const int SCROLL_UNIT_X = 15;
58
59 // the cell padding on the left/right
60 static const int PADDING_RIGHTLEFT = 3;
61
62 // the expander space margin
63 static const int EXPANDER_MARGIN = 4;
64
65 #ifdef __WXMSW__
66 static const int EXPANDER_OFFSET = 4;
67 #else
68 static const int EXPANDER_OFFSET = 1;
69 #endif
70
71 //Below is the compare stuff
72 //For the generic implements, both the leaf nodes and the nodes are sorted for fast search when needed
73 static wxDataViewModel * g_model;
74 static int g_column = -2;
75 static bool g_asending = true;
76
77 //-----------------------------------------------------------------------------
78 // wxDataViewHeaderWindow
79 //-----------------------------------------------------------------------------
80
81 class wxDataViewHeaderWindow : public wxHeaderCtrl
82 {
83 public:
84 wxDataViewHeaderWindow(wxDataViewCtrl *parent)
85 : wxHeaderCtrl(parent)
86 {
87 }
88
89 wxDataViewCtrl *GetOwner() const
90 { return static_cast<wxDataViewCtrl *>(GetParent()); }
91
92 protected:
93 // implement/override wxHeaderCtrl functions by forwarding them to the main
94 // control
95 virtual const wxHeaderColumn& GetColumn(unsigned int idx) const
96 {
97 return *(GetOwner()->GetColumn(idx));
98 }
99
100 virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
101 {
102 wxDataViewCtrl * const owner = GetOwner();
103
104 int widthContents = owner->GetBestColumnWidth(idx);
105 owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents));
106 owner->OnColumnChange(idx);
107
108 return true;
109 }
110
111 private:
112 bool SendEvent(wxEventType type, unsigned int n)
113 {
114 wxDataViewCtrl * const owner = GetOwner();
115 wxDataViewEvent event(type, owner->GetId());
116
117 event.SetEventObject(owner);
118 event.SetColumn(n);
119 event.SetDataViewColumn(owner->GetColumn(n));
120 event.SetModel(owner->GetModel());
121
122 // for events created by wxDataViewHeaderWindow the
123 // row / value fields are not valid
124 return owner->GetEventHandler()->ProcessEvent(event);
125 }
126
127 void OnClick(wxHeaderCtrlEvent& event)
128 {
129 const unsigned idx = event.GetColumn();
130
131 if ( SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, idx) )
132 return;
133
134 // default handling for the column click is to sort by this column or
135 // toggle its sort order
136 wxDataViewCtrl * const owner = GetOwner();
137 wxDataViewColumn * const col = owner->GetColumn(idx);
138 if ( !col->IsSortable() )
139 {
140 // no default handling for non-sortable columns
141 event.Skip();
142 return;
143 }
144
145 if ( col->IsSortKey() )
146 {
147 // already using this column for sorting, just change the order
148 col->ToggleSortOrder();
149 }
150 else // not using this column for sorting yet
151 {
152 // first unset the old sort column if any
153 int oldSortKey = owner->GetSortingColumnIndex();
154 if ( oldSortKey != wxNOT_FOUND )
155 {
156 owner->GetColumn(oldSortKey)->UnsetAsSortKey();
157 owner->OnColumnChange(oldSortKey);
158 }
159
160 owner->SetSortingColumnIndex(idx);
161 col->SetAsSortKey();
162 }
163
164 wxDataViewModel * const model = owner->GetModel();
165 if ( model )
166 model->Resort();
167
168 owner->OnColumnChange(idx);
169 }
170
171 void OnRClick(wxHeaderCtrlEvent& event)
172 {
173 if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
174 event.GetColumn()) )
175 event.Skip();
176 }
177
178 void OnEndResize(wxHeaderCtrlEvent& event)
179 {
180 wxDataViewCtrl * const owner = GetOwner();
181
182 const unsigned col = event.GetColumn();
183 owner->GetColumn(col)->SetWidth(event.GetWidth());
184 GetOwner()->OnColumnChange(col);
185 }
186
187 void OnEndReorder(wxHeaderCtrlEvent& event)
188 {
189 wxDataViewCtrl * const owner = GetOwner();
190 owner->ColumnMoved(owner->GetColumn(event.GetColumn()),
191 event.GetNewOrder());
192 }
193
194 DECLARE_EVENT_TABLE()
195 DECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow)
196 };
197
198 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
199 EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick)
200 EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
201
202 EVT_HEADER_END_RESIZE(wxID_ANY, wxDataViewHeaderWindow::OnEndResize)
203
204 EVT_HEADER_END_REORDER(wxID_ANY, wxDataViewHeaderWindow::OnEndReorder)
205 END_EVENT_TABLE()
206
207 //-----------------------------------------------------------------------------
208 // wxDataViewRenameTimer
209 //-----------------------------------------------------------------------------
210
211 class wxDataViewRenameTimer: public wxTimer
212 {
213 private:
214 wxDataViewMainWindow *m_owner;
215
216 public:
217 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
218 void Notify();
219 };
220
221 //-----------------------------------------------------------------------------
222 // wxDataViewTreeNode
223 //-----------------------------------------------------------------------------
224 class wxDataViewTreeNode;
225 WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
226 WX_DEFINE_ARRAY( void* , wxDataViewTreeLeaves);
227
228 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1, wxDataViewTreeNode ** node2);
229 int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2);
230
231 class wxDataViewTreeNode
232 {
233 public:
234 wxDataViewTreeNode( wxDataViewTreeNode * parent = NULL )
235 {
236 m_parent = parent;
237 if (!parent)
238 m_open = true;
239 else
240 m_open = false;
241 m_hasChildren = false;
242 m_subTreeCount = 0;
243 }
244
245 ~wxDataViewTreeNode()
246 {
247 }
248
249 wxDataViewTreeNode * GetParent() const { return m_parent; }
250 void SetParent( wxDataViewTreeNode * parent ) { m_parent = parent; }
251 wxDataViewTreeNodes & GetNodes() { return m_nodes; }
252 wxDataViewTreeLeaves & GetChildren() { return m_leaves; }
253
254 void AddNode( wxDataViewTreeNode * node )
255 {
256 m_leaves.Add( node->GetItem().GetID() );
257 if (g_column >= -1)
258 m_leaves.Sort( &wxGenericTreeModelItemCmp );
259 m_nodes.Add( node );
260 if (g_column >= -1)
261 m_nodes.Sort( &wxGenericTreeModelNodeCmp );
262 }
263 void AddLeaf( void * leaf )
264 {
265 m_leaves.Add( leaf );
266 if (g_column >= -1)
267 m_leaves.Sort( &wxGenericTreeModelItemCmp );
268 }
269
270 wxDataViewItem & GetItem() { return m_item; }
271 const wxDataViewItem & GetItem() const { return m_item; }
272 void SetItem( const wxDataViewItem & item ) { m_item = item; }
273
274 unsigned int GetChildrenNumber() const { return m_leaves.GetCount(); }
275 unsigned int GetNodeNumber() const { return m_nodes.GetCount(); }
276 int GetIndentLevel() const
277 {
278 int ret = 0;
279 const wxDataViewTreeNode * node = this;
280 while( node->GetParent()->GetParent() != NULL )
281 {
282 node = node->GetParent();
283 ret ++;
284 }
285 return ret;
286 }
287
288 bool IsOpen() const
289 {
290 return m_open;
291 }
292
293 void ToggleOpen()
294 {
295 int len = m_nodes.GetCount();
296 int sum = 0;
297 for ( int i = 0;i < len; i ++)
298 sum += m_nodes[i]->GetSubTreeCount();
299
300 sum += m_leaves.GetCount();
301 if (m_open)
302 {
303 ChangeSubTreeCount(-sum);
304 m_open = !m_open;
305 }
306 else
307 {
308 m_open = !m_open;
309 ChangeSubTreeCount(sum);
310 }
311 }
312 bool HasChildren() const { return m_hasChildren; }
313 void SetHasChildren( bool has ){ m_hasChildren = has; }
314
315 void SetSubTreeCount( int num ) { m_subTreeCount = num; }
316 int GetSubTreeCount() const { return m_subTreeCount; }
317 void ChangeSubTreeCount( int num )
318 {
319 if( !m_open )
320 return;
321 m_subTreeCount += num;
322 if( m_parent )
323 m_parent->ChangeSubTreeCount(num);
324 }
325
326 void Resort()
327 {
328 if (g_column >= -1)
329 {
330 m_nodes.Sort( &wxGenericTreeModelNodeCmp );
331 int len = m_nodes.GetCount();
332 for (int i = 0; i < len; i ++)
333 m_nodes[i]->Resort();
334 m_leaves.Sort( &wxGenericTreeModelItemCmp );
335 }
336 }
337
338 private:
339 wxDataViewTreeNode *m_parent;
340 wxDataViewTreeNodes m_nodes;
341 wxDataViewTreeLeaves m_leaves;
342 wxDataViewItem m_item;
343 bool m_open;
344 bool m_hasChildren;
345 int m_subTreeCount;
346 };
347
348 int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1, wxDataViewTreeNode ** node2)
349 {
350 return g_model->Compare( (*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending );
351 }
352
353 int LINKAGEMODE wxGenericTreeModelItemCmp( void ** id1, void ** id2)
354 {
355 return g_model->Compare( *id1, *id2, g_column, g_asending );
356 }
357
358
359
360 //-----------------------------------------------------------------------------
361 // wxDataViewMainWindow
362 //-----------------------------------------------------------------------------
363
364 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
365 WXDLLIMPEXP_ADV);
366 WX_DECLARE_LIST(wxDataViewItem, ItemList);
367 WX_DEFINE_LIST(ItemList)
368
369 class wxDataViewMainWindow: public wxWindow
370 {
371 public:
372 wxDataViewMainWindow( wxDataViewCtrl *parent,
373 wxWindowID id,
374 const wxPoint &pos = wxDefaultPosition,
375 const wxSize &size = wxDefaultSize,
376 const wxString &name = wxT("wxdataviewctrlmainwindow") );
377 virtual ~wxDataViewMainWindow();
378
379 bool IsVirtualList() const { return m_root == NULL; }
380
381 // notifications from wxDataViewModel
382 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
383 bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item );
384 bool ItemChanged( const wxDataViewItem &item );
385 bool ValueChanged( const wxDataViewItem &item, unsigned int col );
386 bool Cleared();
387 void Resort()
388 {
389 if (!IsVirtualList())
390 {
391 SortPrepare();
392 m_root->Resort();
393 }
394 UpdateDisplay();
395 }
396
397 void SortPrepare()
398 {
399 g_model = GetOwner()->GetModel();
400 wxDataViewColumn* col = GetOwner()->GetSortingColumn();
401 if( !col )
402 {
403 if (g_model->HasDefaultCompare())
404 g_column = -1;
405 else
406 g_column = -2;
407
408 g_asending = true;
409 return;
410 }
411 g_column = col->GetModelColumn();
412 g_asending = col->IsSortOrderAscending();
413 }
414
415 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
416 wxDataViewCtrl *GetOwner() { return m_owner; }
417 const wxDataViewCtrl *GetOwner() const { return m_owner; }
418
419 void OnPaint( wxPaintEvent &event );
420 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
421 void OnChar( wxKeyEvent &event );
422 void OnMouse( wxMouseEvent &event );
423 void OnSetFocus( wxFocusEvent &event );
424 void OnKillFocus( wxFocusEvent &event );
425
426 void UpdateDisplay();
427 void RecalculateDisplay();
428 void OnInternalIdle();
429
430 void OnRenameTimer();
431
432 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
433 void ScrollTo( int rows, int column );
434
435 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
436 void ChangeCurrentRow( unsigned int row );
437
438 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
439 bool IsEmpty() { return GetRowCount() == 0; }
440
441 int GetCountPerPage() const;
442 int GetEndOfLastCol() const;
443 unsigned int GetFirstVisibleRow() const;
444 //I change this method to un const because in the tree view, the displaying number of the tree are changing along with the expanding/collapsing of the tree nodes
445 unsigned int GetLastVisibleRow();
446 unsigned int GetRowCount();
447
448 wxDataViewItem GetSelection() const;
449 wxDataViewSelection GetSelections(){ return m_selection; }
450 void SetSelections( const wxDataViewSelection & sel ) { m_selection = sel; UpdateDisplay(); }
451 void Select( const wxArrayInt& aSelections );
452 void SelectAllRows( bool on );
453 void SelectRow( unsigned int row, bool on );
454 void SelectRows( unsigned int from, unsigned int to, bool on );
455 void ReverseRowSelection( unsigned int row );
456 bool IsRowSelected( unsigned int row );
457 void SendSelectionChangedEvent( const wxDataViewItem& item);
458
459 void RefreshRow( unsigned int row );
460 void RefreshRows( unsigned int from, unsigned int to );
461 void RefreshRowsAfter( unsigned int firstRow );
462
463 // returns the colour to be used for drawing the rules
464 wxColour GetRuleColour() const
465 {
466 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
467 }
468
469 wxRect GetLineRect( unsigned int row ) const;
470
471 int GetLineStart( unsigned int row ) const; // row * m_lineHeight in fixed mode
472 int GetLineHeight( unsigned int row ) const; // m_lineHeight in fixed mode
473 int GetLineAt( unsigned int y ) const; // y / m_lineHeight in fixed mode
474
475 //Some useful functions for row and item mapping
476 wxDataViewItem GetItemByRow( unsigned int row ) const;
477 int GetRowByItem( const wxDataViewItem & item ) const;
478
479 //Methods for building the mapping tree
480 void BuildTree( wxDataViewModel * model );
481 void DestroyTree();
482 void HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column );
483 wxRect GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column );
484
485 void Expand( unsigned int row ) { OnExpanding( row ); }
486 void Collapse( unsigned int row ) { OnCollapsing( row ); }
487 bool IsExpanded( unsigned int row ) const;
488
489 bool EnableDragSource( const wxDataFormat &format )
490 {
491 m_dragFormat = format;
492 m_dragEnabled = format != wxDF_INVALID;
493 return true;
494 }
495 bool EnableDropTarget( const wxDataFormat &format )
496 {
497 m_dropFormat = format;
498 m_dropEnabled = format != wxDF_INVALID;
499 return true;
500 }
501
502 private:
503 wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row ) const;
504 //We did not need this temporarily
505 //wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
506
507 int RecalculateCount();
508
509 wxDataViewEvent SendExpanderEvent( wxEventType type, const wxDataViewItem & item );
510 void OnExpanding( unsigned int row );
511 void OnCollapsing( unsigned int row );
512
513 wxDataViewTreeNode * FindNode( const wxDataViewItem & item );
514
515 private:
516 wxDataViewCtrl *m_owner;
517 int m_lineHeight;
518 bool m_dirty;
519
520 wxDataViewColumn *m_currentCol;
521 unsigned int m_currentRow;
522 wxDataViewSelection m_selection;
523
524 wxDataViewRenameTimer *m_renameTimer;
525 bool m_lastOnSame;
526
527 bool m_hasFocus;
528
529 int m_dragCount;
530 wxPoint m_dragStart;
531
532 bool m_dragEnabled;
533 wxDataFormat m_dragFormat;
534
535 bool m_dropEnabled;
536 wxDataFormat m_dropFormat;
537
538 // for double click logic
539 unsigned int m_lineLastClicked,
540 m_lineBeforeLastClicked,
541 m_lineSelectSingleOnUp;
542
543 // the pen used to draw horiz/vertical rules
544 wxPen m_penRule;
545
546 // the pen used to draw the expander and the lines
547 wxPen m_penExpander;
548
549 //This is the tree structure of the model
550 wxDataViewTreeNode * m_root;
551 int m_count;
552 //This is the tree node under the cursor
553 wxDataViewTreeNode * m_underMouse;
554 private:
555 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
556 DECLARE_EVENT_TABLE()
557 };
558
559 // ---------------------------------------------------------
560 // wxGenericDataViewModelNotifier
561 // ---------------------------------------------------------
562
563 class wxGenericDataViewModelNotifier: public wxDataViewModelNotifier
564 {
565 public:
566 wxGenericDataViewModelNotifier( wxDataViewMainWindow *mainWindow )
567 { m_mainWindow = mainWindow; }
568
569 virtual bool ItemAdded( const wxDataViewItem & parent, const wxDataViewItem & item )
570 { return m_mainWindow->ItemAdded( parent , item ); }
571 virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
572 { return m_mainWindow->ItemDeleted( parent, item ); }
573 virtual bool ItemChanged( const wxDataViewItem & item )
574 { return m_mainWindow->ItemChanged(item); }
575 virtual bool ValueChanged( const wxDataViewItem & item , unsigned int col )
576 { return m_mainWindow->ValueChanged( item, col ); }
577 virtual bool Cleared()
578 { return m_mainWindow->Cleared(); }
579 virtual void Resort()
580 { m_mainWindow->Resort(); }
581
582 wxDataViewMainWindow *m_mainWindow;
583 };
584
585 // ---------------------------------------------------------
586 // wxDataViewRenderer
587 // ---------------------------------------------------------
588
589 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
590
591 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
592 wxDataViewCellMode mode,
593 int align) :
594 wxDataViewRendererBase( varianttype, mode, align )
595 {
596 m_dc = NULL;
597 m_align = align;
598 m_mode = mode;
599 m_wantsAttr = false;
600 m_hasAttr = false;
601 }
602
603 wxDataViewRenderer::~wxDataViewRenderer()
604 {
605 if (m_dc)
606 delete m_dc;
607 }
608
609 wxDC *wxDataViewRenderer::GetDC()
610 {
611 if (m_dc == NULL)
612 {
613 if (GetOwner() == NULL)
614 return NULL;
615 if (GetOwner()->GetOwner() == NULL)
616 return NULL;
617 m_dc = new wxClientDC( GetOwner()->GetOwner() );
618 }
619
620 return m_dc;
621 }
622
623 void wxDataViewRenderer::SetAlignment( int align )
624 {
625 m_align=align;
626 }
627
628 int wxDataViewRenderer::GetAlignment() const
629 {
630 return m_align;
631 }
632
633 int wxDataViewRenderer::CalculateAlignment() const
634 {
635 if (m_align == wxDVR_DEFAULT_ALIGNMENT)
636 {
637 if (GetOwner() == NULL)
638 return wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL;
639
640 return GetOwner()->GetAlignment() | wxALIGN_CENTRE_VERTICAL;
641 }
642
643 return m_align;
644 }
645
646 // ---------------------------------------------------------
647 // wxDataViewCustomRenderer
648 // ---------------------------------------------------------
649
650 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
651
652 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
653 wxDataViewCellMode mode, int align ) :
654 wxDataViewRenderer( varianttype, mode, align )
655 {
656 }
657
658 void wxDataViewCustomRenderer::RenderText( const wxString &text, int xoffset, wxRect cell, wxDC *dc, int state )
659 {
660 wxDataViewCtrl *view = GetOwner()->GetOwner();
661 wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ?
662 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) :
663 view->GetForegroundColour();
664 dc->SetTextForeground(col);
665 dc->DrawText( text, cell.x + xoffset, cell.y + ((cell.height - dc->GetCharHeight()) / 2));
666 }
667
668 // ---------------------------------------------------------
669 // wxDataViewTextRenderer
670 // ---------------------------------------------------------
671
672 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
673
674 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
675 wxDataViewCellMode mode, int align ) :
676 wxDataViewCustomRenderer( varianttype, mode, align )
677 {
678 }
679
680 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
681 {
682 m_text = value.GetString();
683
684 return true;
685 }
686
687 bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
688 {
689 return false;
690 }
691
692 bool wxDataViewTextRenderer::HasEditorCtrl()
693 {
694 return true;
695 }
696
697 wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
698 wxRect labelRect, const wxVariant &value )
699 {
700 return new wxTextCtrl( parent, wxID_ANY, value,
701 wxPoint(labelRect.x,labelRect.y),
702 wxSize(labelRect.width,labelRect.height) );
703 }
704
705 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
706 {
707 wxTextCtrl *text = (wxTextCtrl*) editor;
708 value = text->GetValue();
709 return true;
710 }
711
712 bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state )
713 {
714 RenderText( m_text, 0, cell, dc, state );
715 return true;
716 }
717
718 wxSize wxDataViewTextRenderer::GetSize() const
719 {
720 const wxDataViewCtrl *view = GetView();
721 if (!m_text.empty())
722 {
723 int x,y;
724 view->GetTextExtent( m_text, &x, &y );
725 return wxSize( x, y );
726 }
727 return wxSize(80,20);
728 }
729
730 // ---------------------------------------------------------
731 // wxDataViewTextRendererAttr
732 // ---------------------------------------------------------
733
734 IMPLEMENT_CLASS(wxDataViewTextRendererAttr, wxDataViewTextRenderer)
735
736 wxDataViewTextRendererAttr::wxDataViewTextRendererAttr( const wxString &varianttype,
737 wxDataViewCellMode mode, int align ) :
738 wxDataViewTextRenderer( varianttype, mode, align )
739 {
740 m_wantsAttr = true;
741 }
742
743 bool wxDataViewTextRendererAttr::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
744 {
745 wxFont font;
746 wxColour colour;
747
748 if (m_hasAttr)
749 {
750 if (m_attr.HasColour())
751 {
752 colour = dc->GetTextForeground();
753 dc->SetTextForeground( m_attr.GetColour() );
754 }
755
756 if (m_attr.GetBold() || m_attr.GetItalic())
757 {
758 font = dc->GetFont();
759 wxFont myfont = font;
760 if (m_attr.GetBold())
761 myfont.SetWeight( wxFONTWEIGHT_BOLD );
762 if (m_attr.GetItalic())
763 myfont.SetStyle( wxFONTSTYLE_ITALIC );
764 dc->SetFont( myfont );
765 }
766 }
767
768 dc->DrawText( m_text, cell.x, cell.y + ((cell.height - dc->GetCharHeight()) / 2));
769
770 // restore dc
771 if (m_hasAttr)
772 {
773 if (m_attr.HasColour())
774 dc->SetTextForeground( colour );
775
776 if (m_attr.GetBold() || m_attr.GetItalic())
777 dc->SetFont( font );
778 }
779
780 return true;
781 }
782
783
784 // ---------------------------------------------------------
785 // wxDataViewBitmapRenderer
786 // ---------------------------------------------------------
787
788 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
789
790 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
791 wxDataViewCellMode mode, int align ) :
792 wxDataViewCustomRenderer( varianttype, mode, align )
793 {
794 }
795
796 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
797 {
798 if (value.GetType() == wxT("wxBitmap"))
799 m_bitmap << value;
800 if (value.GetType() == wxT("wxIcon"))
801 m_icon << value;
802
803 return true;
804 }
805
806 bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
807 {
808 return false;
809 }
810
811 bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
812 {
813 if (m_bitmap.Ok())
814 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
815 else if (m_icon.Ok())
816 dc->DrawIcon( m_icon, cell.x, cell.y );
817
818 return true;
819 }
820
821 wxSize wxDataViewBitmapRenderer::GetSize() const
822 {
823 if (m_bitmap.Ok())
824 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
825 else if (m_icon.Ok())
826 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
827
828 return wxSize(16,16);
829 }
830
831 // ---------------------------------------------------------
832 // wxDataViewToggleRenderer
833 // ---------------------------------------------------------
834
835 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
836
837 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
838 wxDataViewCellMode mode, int align ) :
839 wxDataViewCustomRenderer( varianttype, mode, align )
840 {
841 m_toggle = false;
842 }
843
844 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
845 {
846 m_toggle = value.GetBool();
847
848 return true;
849 }
850
851 bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
852 {
853 return false;
854 }
855
856 bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
857 {
858 // User wxRenderer here
859
860 wxRect rect;
861 rect.x = cell.x + cell.width/2 - 10;
862 rect.width = 20;
863 rect.y = cell.y + cell.height/2 - 10;
864 rect.height = 20;
865
866 int flags = 0;
867 if (m_toggle)
868 flags |= wxCONTROL_CHECKED;
869 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
870 flags |= wxCONTROL_DISABLED;
871
872 wxRendererNative::Get().DrawCheckBox(
873 GetOwner()->GetOwner(),
874 *dc,
875 rect,
876 flags );
877
878 return true;
879 }
880
881 bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
882 wxDataViewModel *model,
883 const wxDataViewItem & item, unsigned int col)
884 {
885 bool value = !m_toggle;
886 wxVariant variant = value;
887 model->SetValue( variant, item, col);
888 model->ValueChanged( item, col );
889 return true;
890 }
891
892 wxSize wxDataViewToggleRenderer::GetSize() const
893 {
894 return wxSize(20,20);
895 }
896
897 // ---------------------------------------------------------
898 // wxDataViewProgressRenderer
899 // ---------------------------------------------------------
900
901 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
902
903 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
904 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
905 wxDataViewCustomRenderer( varianttype, mode, align )
906 {
907 m_label = label;
908 m_value = 0;
909 }
910
911 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
912 {
913 }
914
915 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
916 {
917 m_value = (long) value;
918
919 if (m_value < 0) m_value = 0;
920 if (m_value > 100) m_value = 100;
921
922 return true;
923 }
924
925 bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
926 {
927 value = (long) m_value;
928 return true;
929 }
930
931 bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
932 {
933 double pct = (double)m_value / 100.0;
934 wxRect bar = cell;
935 bar.width = (int)(cell.width * pct);
936 dc->SetPen( *wxTRANSPARENT_PEN );
937 dc->SetBrush( *wxBLUE_BRUSH );
938 dc->DrawRectangle( bar );
939
940 dc->SetBrush( *wxTRANSPARENT_BRUSH );
941 dc->SetPen( *wxBLACK_PEN );
942 dc->DrawRectangle( cell );
943
944 return true;
945 }
946
947 wxSize wxDataViewProgressRenderer::GetSize() const
948 {
949 return wxSize(40,12);
950 }
951
952 // ---------------------------------------------------------
953 // wxDataViewDateRenderer
954 // ---------------------------------------------------------
955
956 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
957
958 #if wxUSE_DATE_RENDERER_POPUP
959
960 class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
961 {
962 public:
963 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
964 wxDataViewModel *model, const wxDataViewItem & item, unsigned int col) :
965 wxPopupTransientWindow( parent, wxBORDER_SIMPLE ),
966 m_item( item )
967 {
968 m_model = model;
969 m_col = col;
970 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
971 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
972 sizer->Add( m_cal, 1, wxGROW );
973 SetSizer( sizer );
974 sizer->Fit( this );
975 }
976
977 void OnCalendar( wxCalendarEvent &event );
978
979 wxCalendarCtrl *m_cal;
980 wxDataViewModel *m_model;
981 unsigned int m_col;
982 const wxDataViewItem & m_item;
983
984 protected:
985 virtual void OnDismiss()
986 {
987 }
988
989 private:
990 DECLARE_EVENT_TABLE()
991 };
992
993 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
994 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
995 END_EVENT_TABLE()
996
997 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
998 {
999 wxDateTime date = event.GetDate();
1000 wxVariant value = date;
1001 m_model->SetValue( value, m_item, m_col );
1002 m_model->ValueChanged( m_item, m_col );
1003 DismissAndNotify();
1004 }
1005
1006 #endif // wxUSE_DATE_RENDERER_POPUP
1007
1008 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
1009
1010 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
1011 wxDataViewCellMode mode, int align ) :
1012 wxDataViewCustomRenderer( varianttype, mode, align )
1013 {
1014 }
1015
1016 bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
1017 {
1018 m_date = value.GetDateTime();
1019
1020 return true;
1021 }
1022
1023 bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
1024 {
1025 value = m_date;
1026 return true;
1027 }
1028
1029 bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state )
1030 {
1031 wxString tmp = m_date.FormatDate();
1032 RenderText( tmp, 0, cell, dc, state );
1033 return true;
1034 }
1035
1036 wxSize wxDataViewDateRenderer::GetSize() const
1037 {
1038 const wxDataViewCtrl* view = GetView();
1039 wxString tmp = m_date.FormatDate();
1040 wxCoord x,y,d;
1041 view->GetTextExtent( tmp, &x, &y, &d );
1042 return wxSize(x,y+d);
1043 }
1044
1045 bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewModel *model,
1046 const wxDataViewItem & item, unsigned int col )
1047 {
1048 wxVariant variant;
1049 model->GetValue( variant, item, col );
1050 wxDateTime value = variant.GetDateTime();
1051
1052 #if wxUSE_DATE_RENDERER_POPUP
1053 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
1054 GetOwner()->GetOwner()->GetParent(), &value, model, item, col);
1055 wxPoint pos = wxGetMousePosition();
1056 popup->Move( pos );
1057 popup->Layout();
1058 popup->Popup( popup->m_cal );
1059 #else // !wxUSE_DATE_RENDERER_POPUP
1060 wxMessageBox(value.Format());
1061 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
1062 return true;
1063 }
1064
1065 // ---------------------------------------------------------
1066 // wxDataViewIconTextRenderer
1067 // ---------------------------------------------------------
1068
1069 IMPLEMENT_CLASS(wxDataViewIconTextRenderer, wxDataViewCustomRenderer)
1070
1071 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(
1072 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
1073 wxDataViewCustomRenderer( varianttype, mode, align )
1074 {
1075 SetMode(mode);
1076 SetAlignment(align);
1077 }
1078
1079 wxDataViewIconTextRenderer::~wxDataViewIconTextRenderer()
1080 {
1081 }
1082
1083 bool wxDataViewIconTextRenderer::SetValue( const wxVariant &value )
1084 {
1085 m_value << value;
1086 return true;
1087 }
1088
1089 bool wxDataViewIconTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
1090 {
1091 return false;
1092 }
1093
1094 bool wxDataViewIconTextRenderer::Render( wxRect cell, wxDC *dc, int state )
1095 {
1096 int xoffset = 0;
1097 const wxIcon &icon = m_value.GetIcon();
1098 if (icon.IsOk())
1099 {
1100 dc->DrawIcon( icon, cell.x, cell.y + ((cell.height - icon.GetHeight()) / 2));
1101 xoffset = icon.GetWidth()+4;
1102 }
1103
1104 RenderText( m_value.GetText(), xoffset, cell, dc, state );
1105
1106 return true;
1107 }
1108
1109 wxSize wxDataViewIconTextRenderer::GetSize() const
1110 {
1111 const wxDataViewCtrl *view = GetView();
1112 if (!m_value.GetText().empty())
1113 {
1114 int x,y;
1115 view->GetTextExtent( m_value.GetText(), &x, &y );
1116
1117 if (m_value.GetIcon().IsOk())
1118 x += m_value.GetIcon().GetWidth() + 4;
1119 return wxSize( x, y );
1120 }
1121 return wxSize(80,20);
1122 }
1123
1124 wxControl *
1125 wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow * WXUNUSED(parent),
1126 wxRect WXUNUSED(labelRect),
1127 const wxVariant& WXUNUSED(value))
1128 {
1129 return NULL;
1130 }
1131
1132 bool
1133 wxDataViewIconTextRenderer::GetValueFromEditorCtrl(wxControl* WXUNUSED(editor),
1134 wxVariant& WXUNUSED(value))
1135 {
1136 return false;
1137 }
1138
1139 //-----------------------------------------------------------------------------
1140 // wxDataViewRenameTimer
1141 //-----------------------------------------------------------------------------
1142
1143 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1144 {
1145 m_owner = owner;
1146 }
1147
1148 void wxDataViewRenameTimer::Notify()
1149 {
1150 m_owner->OnRenameTimer();
1151 }
1152
1153 //-----------------------------------------------------------------------------
1154 // wxDataViewMainWindow
1155 //-----------------------------------------------------------------------------
1156
1157 //The tree building helper, declared firstly
1158 static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node);
1159
1160 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
1161 {
1162 if (row1 > row2) return 1;
1163 if (row1 == row2) return 0;
1164 return -1;
1165 }
1166
1167
1168 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1169
1170 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1171 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1172 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1173 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
1174 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1175 EVT_CHAR (wxDataViewMainWindow::OnChar)
1176 END_EVENT_TABLE()
1177
1178 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1179 const wxPoint &pos, const wxSize &size, const wxString &name ) :
1180 wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE, name ),
1181 m_selection( wxDataViewSelectionCmp )
1182
1183 {
1184 SetOwner( parent );
1185
1186 m_lastOnSame = false;
1187 m_renameTimer = new wxDataViewRenameTimer( this );
1188
1189 // TODO: user better initial values/nothing selected
1190 m_currentCol = NULL;
1191 m_currentRow = 0;
1192
1193 m_lineHeight = wxMax( 17, GetCharHeight() + 2 ); // 17 = mini icon height + 1
1194
1195 m_dragCount = 0;
1196 m_dragStart = wxPoint(0,0);
1197 m_lineLastClicked = (unsigned int) -1;
1198 m_lineBeforeLastClicked = (unsigned int) -1;
1199 m_lineSelectSingleOnUp = (unsigned int) -1;
1200
1201 m_dragEnabled = false;
1202 m_dropEnabled = false;
1203
1204 m_hasFocus = false;
1205
1206 SetBackgroundColour( *wxWHITE );
1207
1208 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
1209
1210 m_penRule = wxPen(GetRuleColour());
1211
1212 //Here I compose a pen can draw black lines, maybe there are something system colour to use
1213 m_penExpander = wxPen(wxColour(0,0,0));
1214 //Some new added code to deal with the tree structure
1215 m_root = new wxDataViewTreeNode( NULL );
1216 m_root->SetHasChildren(true);
1217
1218 //Make m_count = -1 will cause the class recaculate the real displaying number of rows.
1219 m_count = -1;
1220 m_underMouse = NULL;
1221 UpdateDisplay();
1222 }
1223
1224 wxDataViewMainWindow::~wxDataViewMainWindow()
1225 {
1226 DestroyTree();
1227 delete m_renameTimer;
1228 }
1229
1230 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1231 {
1232 wxDataViewModel *model = GetOwner()->GetModel();
1233 wxAutoBufferedPaintDC dc( this );
1234
1235 #ifdef __WXMSW__
1236 dc.SetPen( *wxTRANSPARENT_PEN );
1237 dc.SetBrush( wxBrush( GetBackgroundColour()) );
1238 dc.SetBrush( *wxWHITE_BRUSH );
1239 wxSize size( GetClientSize() );
1240 dc.DrawRectangle( 0,0,size.x,size.y );
1241 #endif
1242
1243 // prepare the DC
1244 GetOwner()->PrepareDC( dc );
1245 dc.SetFont( GetFont() );
1246
1247 wxRect update = GetUpdateRegion().GetBox();
1248 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1249
1250 // compute which items needs to be redrawn
1251 unsigned int item_start = GetLineAt( wxMax(0,update.y) );
1252 unsigned int item_count =
1253 wxMin( (int)( GetLineAt( wxMax(0,update.y+update.height) ) - item_start + 1),
1254 (int)(GetRowCount( ) - item_start));
1255 unsigned int item_last = item_start + item_count;
1256
1257 // compute which columns needs to be redrawn
1258 unsigned int cols = GetOwner()->GetColumnCount();
1259 unsigned int col_start = 0;
1260 unsigned int x_start;
1261 for (x_start = 0; col_start < cols; col_start++)
1262 {
1263 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_start);
1264 if (col->IsHidden())
1265 continue; // skip it!
1266
1267 unsigned int w = col->GetWidth();
1268 if (x_start+w >= (unsigned int)update.x)
1269 break;
1270
1271 x_start += w;
1272 }
1273
1274 unsigned int col_last = col_start;
1275 unsigned int x_last = x_start;
1276 for (; col_last < cols; col_last++)
1277 {
1278 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_last);
1279 if (col->IsHidden())
1280 continue; // skip it!
1281
1282 if (x_last > (unsigned int)update.GetRight())
1283 break;
1284
1285 x_last += col->GetWidth();
1286 }
1287
1288 // Draw horizontal rules if required
1289 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1290 {
1291 dc.SetPen(m_penRule);
1292 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1293
1294 for (unsigned int i = item_start; i <= item_last+1; i++)
1295 {
1296 int y = GetLineStart( i );
1297 dc.DrawLine(x_start, y, x_last, y);
1298 }
1299 }
1300
1301 // Draw vertical rules if required
1302 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1303 {
1304 dc.SetPen(m_penRule);
1305 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1306
1307 int x = x_start;
1308 for (unsigned int i = col_start; i < col_last; i++)
1309 {
1310 wxDataViewColumn *col = GetOwner()->GetColumnAt(i);
1311 if (col->IsHidden())
1312 continue; // skip it
1313
1314 dc.DrawLine(x, GetLineStart( item_start ),
1315 x, GetLineStart( item_last ) );
1316
1317 x += col->GetWidth();
1318 }
1319
1320 // Draw last vertical rule
1321 dc.DrawLine(x, GetLineStart( item_start ),
1322 x, GetLineStart( item_last ) );
1323 }
1324
1325 // redraw the background for the items which are selected/current
1326 for (unsigned int item = item_start; item < item_last; item++)
1327 {
1328 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1329 if (selected || item == m_currentRow)
1330 {
1331 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
1332 if (item == m_currentRow)
1333 flags |= wxCONTROL_CURRENT;
1334 if (m_hasFocus)
1335 flags |= wxCONTROL_FOCUSED;
1336
1337 wxRect rect( x_start, GetLineStart( item ), x_last, GetLineHeight( item ) );
1338 wxRendererNative::Get().DrawItemSelectionRect
1339 (
1340 this,
1341 dc,
1342 rect,
1343 flags
1344 );
1345 }
1346 }
1347
1348 wxDataViewColumn *expander = GetOwner()->GetExpanderColumn();
1349 if (!expander)
1350 {
1351 // TODO-RTL: last column for RTL support
1352 expander = GetOwner()->GetColumnAt( 0 );
1353 GetOwner()->SetExpanderColumn(expander);
1354 }
1355
1356 // redraw all cells for all rows which must be repainted and all columns
1357 wxRect cell_rect;
1358 cell_rect.x = x_start;
1359 for (unsigned int i = col_start; i < col_last; i++)
1360 {
1361
1362 wxDataViewColumn *col = GetOwner()->GetColumnAt( i );
1363 wxDataViewRenderer *cell = col->GetRenderer();
1364 cell_rect.width = col->GetWidth();
1365
1366 if (col->IsHidden())
1367 continue; // skip it!
1368
1369 for (unsigned int item = item_start; item < item_last; item++)
1370 {
1371 // get the cell value and set it into the renderer
1372 wxVariant value;
1373 wxDataViewTreeNode *node = NULL;
1374 wxDataViewItem dataitem;
1375
1376 if (!IsVirtualList())
1377 {
1378 node = GetTreeNodeByRow(item);
1379 if( node == NULL )
1380 continue;
1381
1382 dataitem = node->GetItem();
1383
1384 if ((i > 0) && model->IsContainer(dataitem) && !model->HasContainerColumns(dataitem))
1385 continue;
1386 }
1387 else
1388 {
1389 dataitem = wxDataViewItem( wxUIntToPtr(item) );
1390 }
1391
1392 model->GetValue( value, dataitem, col->GetModelColumn());
1393 cell->SetValue( value );
1394
1395 if (cell->GetWantsAttr())
1396 {
1397 wxDataViewItemAttr attr;
1398 bool ret = model->GetAttr( dataitem, col->GetModelColumn(), attr );
1399 if (ret)
1400 cell->SetAttr( attr );
1401 cell->SetHasAttr( ret );
1402 }
1403
1404 // update cell_rect
1405 cell_rect.y = GetLineStart( item );
1406 cell_rect.height = GetLineHeight( item ); // -1 is for the horizontal rules (?)
1407
1408 //Draw the expander here.
1409 int indent = 0;
1410 if ((!IsVirtualList()) && (col == expander))
1411 {
1412 indent = node->GetIndentLevel();
1413
1414 //Calculate the indent first
1415 indent = cell_rect.x + GetOwner()->GetIndent() * indent;
1416
1417 int expander_width = m_lineHeight - 2*EXPANDER_MARGIN;
1418 // change the cell_rect.x to the appropriate pos
1419 int expander_x = indent + EXPANDER_MARGIN;
1420 int expander_y = cell_rect.y + EXPANDER_MARGIN + (GetLineHeight(item) / 2) - (expander_width/2) - EXPANDER_OFFSET;
1421 indent = indent + m_lineHeight; //try to use the m_lineHeight as the expander space
1422 dc.SetPen( m_penExpander );
1423 dc.SetBrush( wxNullBrush );
1424 if( node->HasChildren() )
1425 {
1426 wxRect rect( expander_x , expander_y, expander_width, expander_width);
1427 int flag = 0;
1428 if (m_underMouse == node)
1429 {
1430 flag |= wxCONTROL_CURRENT;
1431 }
1432 if( node->IsOpen() )
1433 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, flag|wxCONTROL_EXPANDED );
1434 else
1435 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, flag);
1436 }
1437 //force the expander column to left-center align
1438 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
1439 }
1440 if (node && !node->HasChildren())
1441 {
1442 // Yes, if the node does not have any child, it must be a leaf which
1443 // mean that it is a temporarily created by GetTreeNodeByRow
1444 wxDELETE(node);
1445 }
1446
1447 // cannot be bigger than allocated space
1448 wxSize size = cell->GetSize();
1449 // Because of the tree structure indent, here we should minus the width of the cell for drawing
1450 size.x = wxMin( size.x + 2*PADDING_RIGHTLEFT, cell_rect.width - indent );
1451 // size.y = wxMin( size.y, cell_rect.height );
1452 size.y = cell_rect.height;
1453
1454 wxRect item_rect(cell_rect.GetTopLeft(), size);
1455 int align = cell->CalculateAlignment();
1456
1457 // horizontal alignment:
1458 item_rect.x = cell_rect.x;
1459 if (align & wxALIGN_CENTER_HORIZONTAL)
1460 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1461 else if (align & wxALIGN_RIGHT)
1462 item_rect.x = cell_rect.x + cell_rect.width - size.x;
1463 //else: wxALIGN_LEFT is the default
1464
1465 // vertical alignment:
1466 item_rect.y = cell_rect.y;
1467 if (align & wxALIGN_CENTER_VERTICAL)
1468 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1469 else if (align & wxALIGN_BOTTOM)
1470 item_rect.y = cell_rect.y + cell_rect.height - size.y;
1471 //else: wxALIGN_TOP is the default
1472
1473 // add padding
1474 item_rect.x += PADDING_RIGHTLEFT;
1475 item_rect.width = size.x - 2 * PADDING_RIGHTLEFT;
1476
1477 //Here we add the tree indent
1478 item_rect.x += indent;
1479
1480 int state = 0;
1481 if (m_hasFocus && (m_selection.Index(item) != wxNOT_FOUND))
1482 state |= wxDATAVIEW_CELL_SELECTED;
1483
1484 // TODO: it would be much more efficient to create a clipping
1485 // region for the entire column being rendered (in the OnPaint
1486 // of wxDataViewMainWindow) instead of a single clip region for
1487 // each cell. However it would mean that each renderer should
1488 // respect the given wxRect's top & bottom coords, eventually
1489 // violating only the left & right coords - however the user can
1490 // make its own renderer and thus we cannot be sure of that.
1491 dc.SetClippingRegion( item_rect );
1492 cell->Render( item_rect, &dc, state );
1493 dc.DestroyClippingRegion();
1494 }
1495
1496 cell_rect.x += cell_rect.width;
1497 }
1498 }
1499
1500 void wxDataViewMainWindow::OnRenameTimer()
1501 {
1502 // We have to call this here because changes may just have
1503 // been made and no screen update taken place.
1504 if ( m_dirty )
1505 wxSafeYield();
1506
1507 int xpos = 0;
1508 unsigned int cols = GetOwner()->GetColumnCount();
1509 unsigned int i;
1510 for (i = 0; i < cols; i++)
1511 {
1512 wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
1513 if (c->IsHidden())
1514 continue; // skip it!
1515
1516 if (c == m_currentCol)
1517 break;
1518 xpos += c->GetWidth();
1519 }
1520 wxRect labelRect( xpos,
1521 GetLineStart( m_currentRow ),
1522 m_currentCol->GetWidth(),
1523 GetLineHeight( m_currentRow ) );
1524
1525 GetOwner()->CalcScrolledPosition( labelRect.x, labelRect.y,
1526 &labelRect.x, &labelRect.y);
1527
1528 wxDataViewItem item = GetItemByRow( m_currentRow );
1529 m_currentCol->GetRenderer()->StartEditing( item, labelRect );
1530
1531 }
1532
1533 //------------------------------------------------------------------
1534 // Helper class for do operation on the tree node
1535 //------------------------------------------------------------------
1536 class DoJob
1537 {
1538 public:
1539 DoJob() { }
1540 virtual ~DoJob() { }
1541
1542 //The return value control how the tree-walker tranverse the tree
1543 // 0: Job done, stop tranverse and return
1544 // 1: Ignore the current node's subtree and continue
1545 // 2: Job not done, continue
1546 enum { OK = 0 , IGR = 1, CONT = 2 };
1547 virtual int operator() ( wxDataViewTreeNode * node ) = 0;
1548 virtual int operator() ( void * n ) = 0;
1549 };
1550
1551 bool Walker( wxDataViewTreeNode * node, DoJob & func )
1552 {
1553 if( node==NULL )
1554 return false;
1555
1556 switch( func( node ) )
1557 {
1558 case DoJob::OK :
1559 return true;
1560 case DoJob::IGR:
1561 return false;
1562 case DoJob::CONT:
1563 default:
1564 ;
1565 }
1566
1567 wxDataViewTreeNodes nodes = node->GetNodes();
1568 wxDataViewTreeLeaves leaves = node->GetChildren();
1569
1570 int len_nodes = nodes.GetCount();
1571 int len = leaves.GetCount();
1572 int i = 0, nodes_i = 0;
1573
1574 for(; i < len; i ++ )
1575 {
1576 void * n = leaves[i];
1577 if( nodes_i < len_nodes && n == nodes[nodes_i]->GetItem().GetID() )
1578 {
1579 wxDataViewTreeNode * nd = nodes[nodes_i];
1580 nodes_i++;
1581
1582 if( Walker( nd , func ) )
1583 return true;
1584
1585 }
1586 else
1587 switch( func( n ) )
1588 {
1589 case DoJob::OK :
1590 return true;
1591 case DoJob::IGR:
1592 continue;
1593 case DoJob::CONT:
1594 default:
1595 ;
1596 }
1597 }
1598 return false;
1599 }
1600
1601 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
1602 {
1603 if (!m_root)
1604 {
1605 m_count++;
1606 UpdateDisplay();
1607 return true;
1608 }
1609
1610 SortPrepare();
1611
1612 wxDataViewTreeNode * node;
1613 node = FindNode(parent);
1614
1615 if( node == NULL )
1616 return false;
1617
1618 node->SetHasChildren( true );
1619
1620 if( g_model->IsContainer( item ) )
1621 {
1622 wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node );
1623 newnode->SetItem(item);
1624 newnode->SetHasChildren( true );
1625 node->AddNode( newnode);
1626 }
1627 else
1628 node->AddLeaf( item.GetID() );
1629
1630 node->ChangeSubTreeCount(1);
1631
1632 m_count = -1;
1633 UpdateDisplay();
1634
1635 return true;
1636 }
1637
1638 static void DestroyTreeHelper( wxDataViewTreeNode * node);
1639
1640 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
1641 const wxDataViewItem& item)
1642 {
1643 if (!m_root)
1644 {
1645 m_count--;
1646 if( m_currentRow > GetRowCount() )
1647 m_currentRow = m_count - 1;
1648
1649 m_selection.Empty();
1650
1651 UpdateDisplay();
1652
1653 return true;
1654 }
1655
1656 wxDataViewTreeNode * node = FindNode(parent);
1657
1658 wxCHECK_MSG( node != NULL, false, "item not found" );
1659 wxCHECK_MSG( node->GetChildren().Index( item.GetID() ) != wxNOT_FOUND, false, "item not found" );
1660
1661 int sub = -1;
1662 node->GetChildren().Remove( item.GetID() );
1663 //Manuplate selection
1664 if( m_selection.GetCount() > 1 )
1665 {
1666 m_selection.Empty();
1667 }
1668 bool isContainer = false;
1669 wxDataViewTreeNodes nds = node->GetNodes();
1670 for (size_t i = 0; i < nds.GetCount(); i ++)
1671 {
1672 if (nds[i]->GetItem() == item)
1673 {
1674 isContainer = true;
1675 break;
1676 }
1677 }
1678 if( isContainer )
1679 {
1680 wxDataViewTreeNode * n = NULL;
1681 wxDataViewTreeNodes nodes = node->GetNodes();
1682 int len = nodes.GetCount();
1683 for( int i = 0; i < len; i ++)
1684 {
1685 if( nodes[i]->GetItem() == item )
1686 {
1687 n = nodes[i];
1688 break;
1689 }
1690 }
1691
1692 wxCHECK_MSG( n != NULL, false, "item not found" );
1693
1694 node->GetNodes().Remove( n );
1695 sub -= n->GetSubTreeCount();
1696 ::DestroyTreeHelper(n);
1697 }
1698 //Make the row number invalid and get a new valid one when user call GetRowCount
1699 m_count = -1;
1700 node->ChangeSubTreeCount(sub);
1701
1702 //Change the current row to the last row if the current exceed the max row number
1703 if( m_currentRow > GetRowCount() )
1704 m_currentRow = m_count - 1;
1705
1706 UpdateDisplay();
1707
1708 return true;
1709 }
1710
1711 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
1712 {
1713 SortPrepare();
1714 g_model->Resort();
1715
1716 //Send event
1717 wxWindow *parent = GetParent();
1718 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
1719 le.SetEventObject(parent);
1720 le.SetModel(GetOwner()->GetModel());
1721 le.SetItem(item);
1722 parent->GetEventHandler()->ProcessEvent(le);
1723
1724 return true;
1725 }
1726
1727 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int col )
1728 {
1729 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
1730 /*#define MAX_VIRTUAL_WIDTH 100000
1731
1732 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
1733 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1734 Refresh( true, &rect );
1735
1736 return true;
1737 */
1738 SortPrepare();
1739 g_model->Resort();
1740
1741 //Send event
1742 wxWindow *parent = GetParent();
1743 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
1744 le.SetEventObject(parent);
1745 le.SetModel(GetOwner()->GetModel());
1746 le.SetItem(item);
1747 le.SetColumn(col);
1748 le.SetDataViewColumn(GetOwner()->GetColumn(col));
1749 parent->GetEventHandler()->ProcessEvent(le);
1750
1751 return true;
1752 }
1753
1754 bool wxDataViewMainWindow::Cleared()
1755 {
1756 DestroyTree();
1757
1758 SortPrepare();
1759 BuildTree( GetOwner()->GetModel() );
1760
1761 UpdateDisplay();
1762
1763 return true;
1764 }
1765
1766 void wxDataViewMainWindow::UpdateDisplay()
1767 {
1768 m_dirty = true;
1769 }
1770
1771 void wxDataViewMainWindow::OnInternalIdle()
1772 {
1773 wxWindow::OnInternalIdle();
1774
1775 if (m_dirty)
1776 {
1777 RecalculateDisplay();
1778 m_dirty = false;
1779 }
1780 }
1781
1782 void wxDataViewMainWindow::RecalculateDisplay()
1783 {
1784 wxDataViewModel *model = GetOwner()->GetModel();
1785 if (!model)
1786 {
1787 Refresh();
1788 return;
1789 }
1790
1791 int width = GetEndOfLastCol();
1792 int height = GetLineStart( GetRowCount() );
1793
1794 SetVirtualSize( width, height );
1795 GetOwner()->SetScrollRate( 10, m_lineHeight );
1796
1797 Refresh();
1798 }
1799
1800 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1801 {
1802 wxWindow::ScrollWindow( dx, dy, rect );
1803
1804 if (GetOwner()->m_headerArea)
1805 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1806 }
1807
1808 void wxDataViewMainWindow::ScrollTo( int rows, int column )
1809 {
1810 int x, y;
1811 m_owner->GetScrollPixelsPerUnit( &x, &y );
1812 int sy = GetLineStart( rows )/y;
1813 int sx = 0;
1814 if( column != -1 )
1815 {
1816 wxRect rect = GetClientRect();
1817 int colnum = 0;
1818 int x_start, w = 0;
1819 int xx, yy, xe;
1820 m_owner->CalcUnscrolledPosition( rect.x, rect.y, &xx, &yy );
1821 for (x_start = 0; colnum < column; colnum++)
1822 {
1823 wxDataViewColumn *col = GetOwner()->GetColumnAt(colnum);
1824 if (col->IsHidden())
1825 continue; // skip it!
1826
1827 w = col->GetWidth();
1828 x_start += w;
1829 }
1830
1831 int x_end = x_start + w;
1832 xe = xx + rect.width;
1833 if( x_end > xe )
1834 {
1835 sx = ( xx + x_end - xe )/x;
1836 }
1837 if( x_start < xx )
1838 {
1839 sx = x_start/x;
1840 }
1841 }
1842 m_owner->Scroll( sx, sy );
1843 }
1844
1845 int wxDataViewMainWindow::GetCountPerPage() const
1846 {
1847 wxSize size = GetClientSize();
1848 return size.y / m_lineHeight;
1849 }
1850
1851 int wxDataViewMainWindow::GetEndOfLastCol() const
1852 {
1853 int width = 0;
1854 unsigned int i;
1855 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
1856 {
1857 const wxDataViewColumn *c =
1858 const_cast<wxDataViewCtrl*>(GetOwner())->GetColumnAt( i );
1859
1860 if (!c->IsHidden())
1861 width += c->GetWidth();
1862 }
1863 return width;
1864 }
1865
1866 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
1867 {
1868 int x = 0;
1869 int y = 0;
1870 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1871
1872 return GetLineAt( y );
1873 }
1874
1875 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
1876 {
1877 wxSize client_size = GetClientSize();
1878 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
1879 &client_size.x, &client_size.y );
1880
1881 //we should deal with the pixel here
1882 unsigned int row = GetLineAt(client_size.y) - 1;
1883
1884 return wxMin( GetRowCount()-1, row );
1885 }
1886
1887 unsigned int wxDataViewMainWindow::GetRowCount()
1888 {
1889 if ( m_count == -1 )
1890 {
1891 m_count = RecalculateCount();
1892 UpdateDisplay();
1893 }
1894 return m_count;
1895 }
1896
1897 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
1898 {
1899 m_currentRow = row;
1900
1901 // send event
1902 }
1903
1904 void wxDataViewMainWindow::SelectAllRows( bool on )
1905 {
1906 if (IsEmpty())
1907 return;
1908
1909 if (on)
1910 {
1911 m_selection.Clear();
1912 for (unsigned int i = 0; i < GetRowCount(); i++)
1913 m_selection.Add( i );
1914 Refresh();
1915 }
1916 else
1917 {
1918 unsigned int first_visible = GetFirstVisibleRow();
1919 unsigned int last_visible = GetLastVisibleRow();
1920 unsigned int i;
1921 for (i = 0; i < m_selection.GetCount(); i++)
1922 {
1923 unsigned int row = m_selection[i];
1924 if ((row >= first_visible) && (row <= last_visible))
1925 RefreshRow( row );
1926 }
1927 m_selection.Clear();
1928 }
1929 }
1930
1931 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
1932 {
1933 if (m_selection.Index( row ) == wxNOT_FOUND)
1934 {
1935 if (on)
1936 {
1937 m_selection.Add( row );
1938 RefreshRow( row );
1939 }
1940 }
1941 else
1942 {
1943 if (!on)
1944 {
1945 m_selection.Remove( row );
1946 RefreshRow( row );
1947 }
1948 }
1949 }
1950
1951 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
1952 {
1953 if (from > to)
1954 {
1955 unsigned int tmp = from;
1956 from = to;
1957 to = tmp;
1958 }
1959
1960 unsigned int i;
1961 for (i = from; i <= to; i++)
1962 {
1963 if (m_selection.Index( i ) == wxNOT_FOUND)
1964 {
1965 if (on)
1966 m_selection.Add( i );
1967 }
1968 else
1969 {
1970 if (!on)
1971 m_selection.Remove( i );
1972 }
1973 }
1974 RefreshRows( from, to );
1975 }
1976
1977 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
1978 {
1979 for (size_t i=0; i < aSelections.GetCount(); i++)
1980 {
1981 int n = aSelections[i];
1982
1983 m_selection.Add( n );
1984 RefreshRow( n );
1985 }
1986 }
1987
1988 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
1989 {
1990 if (m_selection.Index( row ) == wxNOT_FOUND)
1991 m_selection.Add( row );
1992 else
1993 m_selection.Remove( row );
1994 RefreshRow( row );
1995 }
1996
1997 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
1998 {
1999 return (m_selection.Index( row ) != wxNOT_FOUND);
2000 }
2001
2002 void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
2003 {
2004 wxWindow *parent = GetParent();
2005 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
2006
2007 le.SetEventObject(parent);
2008 le.SetModel(GetOwner()->GetModel());
2009 le.SetItem( item );
2010
2011 parent->GetEventHandler()->ProcessEvent(le);
2012 }
2013
2014 void wxDataViewMainWindow::RefreshRow( unsigned int row )
2015 {
2016 wxRect rect( 0, GetLineStart( row ), GetEndOfLastCol(), GetLineHeight( row ) );
2017 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2018
2019 wxSize client_size = GetClientSize();
2020 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2021 wxRect intersect_rect = client_rect.Intersect( rect );
2022 if (intersect_rect.width > 0)
2023 Refresh( true, &intersect_rect );
2024 }
2025
2026 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
2027 {
2028 if (from > to)
2029 {
2030 unsigned int tmp = to;
2031 to = from;
2032 from = tmp;
2033 }
2034
2035 wxRect rect( 0, GetLineStart( from ), GetEndOfLastCol(), GetLineStart( (to-from+1) ) );
2036 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2037
2038 wxSize client_size = GetClientSize();
2039 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2040 wxRect intersect_rect = client_rect.Intersect( rect );
2041 if (intersect_rect.width > 0)
2042 Refresh( true, &intersect_rect );
2043 }
2044
2045 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
2046 {
2047 wxSize client_size = GetClientSize();
2048 int start = GetLineStart( firstRow );
2049 m_owner->CalcScrolledPosition( start, 0, &start, NULL );
2050 if (start > client_size.y) return;
2051
2052 wxRect rect( 0, start, client_size.x, client_size.y - start );
2053
2054 Refresh( true, &rect );
2055 }
2056
2057 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
2058 {
2059 wxCHECK_RET( newCurrent < GetRowCount(),
2060 _T("invalid item index in OnArrowChar()") );
2061
2062 // if there is no selection, we cannot move it anywhere
2063 if (!HasCurrentRow())
2064 return;
2065
2066 unsigned int oldCurrent = m_currentRow;
2067
2068 // in single selection we just ignore Shift as we can't select several
2069 // items anyhow
2070 if ( event.ShiftDown() && !IsSingleSel() )
2071 {
2072 RefreshRow( oldCurrent );
2073
2074 ChangeCurrentRow( newCurrent );
2075
2076 // select all the items between the old and the new one
2077 if ( oldCurrent > newCurrent )
2078 {
2079 newCurrent = oldCurrent;
2080 oldCurrent = m_currentRow;
2081 }
2082
2083 SelectRows( oldCurrent, newCurrent, true );
2084 if (oldCurrent!=newCurrent)
2085 SendSelectionChangedEvent(GetItemByRow(m_selection[0]));
2086 }
2087 else // !shift
2088 {
2089 RefreshRow( oldCurrent );
2090
2091 // all previously selected items are unselected unless ctrl is held
2092 if ( !event.ControlDown() )
2093 SelectAllRows(false);
2094
2095 ChangeCurrentRow( newCurrent );
2096
2097 if ( !event.ControlDown() )
2098 {
2099 SelectRow( m_currentRow, true );
2100 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
2101 }
2102 else
2103 RefreshRow( m_currentRow );
2104 }
2105
2106 GetOwner()->EnsureVisible( m_currentRow, -1 );
2107 }
2108
2109 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2110 {
2111 wxRect rect;
2112 rect.x = 0;
2113 rect.y = GetLineStart( row );
2114 rect.width = GetEndOfLastCol();
2115 rect.height = GetLineHeight( row );
2116
2117 return rect;
2118 }
2119
2120 int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
2121 {
2122 const wxDataViewModel *model = GetOwner()->GetModel();
2123
2124 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2125 {
2126 // TODO make more efficient
2127
2128 int start = 0;
2129
2130 unsigned int r;
2131 for (r = 0; r < row; r++)
2132 {
2133 const wxDataViewTreeNode* node = GetTreeNodeByRow(r);
2134 if (!node) return start;
2135
2136 wxDataViewItem item = node->GetItem();
2137
2138 if (node && !node->HasChildren())
2139 {
2140 // Yes, if the node does not have any child, it must be a leaf which
2141 // mean that it is a temporarily created by GetTreeNodeByRow
2142 wxDELETE(node);
2143 }
2144
2145 unsigned int cols = GetOwner()->GetColumnCount();
2146 unsigned int col;
2147 int height = m_lineHeight;
2148 for (col = 0; col < cols; col++)
2149 {
2150 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2151 if (column->IsHidden())
2152 continue; // skip it!
2153
2154 if ((col != 0) && model->IsContainer(item) && !model->HasContainerColumns(item))
2155 continue; // skip it!
2156
2157 const wxDataViewRenderer *renderer = column->GetRenderer();
2158 wxVariant value;
2159 model->GetValue( value, item, column->GetModelColumn() );
2160 wxDataViewRenderer *renderer2 = const_cast<wxDataViewRenderer*>(renderer);
2161 renderer2->SetValue( value );
2162 height = wxMax( height, renderer->GetSize().y );
2163 }
2164
2165
2166 start += height;
2167 }
2168
2169 return start;
2170 }
2171 else
2172 {
2173 return row * m_lineHeight;
2174 }
2175 }
2176
2177 int wxDataViewMainWindow::GetLineAt( unsigned int y ) const
2178 {
2179 const wxDataViewModel *model = GetOwner()->GetModel();
2180
2181 // check for the easy case first
2182 if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
2183 return y / m_lineHeight;
2184
2185 // TODO make more efficient
2186 unsigned int row = 0;
2187 unsigned int yy = 0;
2188 for (;;)
2189 {
2190 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2191 if (!node)
2192 {
2193 // not really correct...
2194 return row + ((y-yy) / m_lineHeight);
2195 }
2196
2197 wxDataViewItem item = node->GetItem();
2198
2199 if (node && !node->HasChildren())
2200 {
2201 // Yes, if the node does not have any child, it must be a leaf which
2202 // mean that it is a temporarily created by GetTreeNodeByRow
2203 wxDELETE(node);
2204 }
2205
2206 unsigned int cols = GetOwner()->GetColumnCount();
2207 unsigned int col;
2208 int height = m_lineHeight;
2209 for (col = 0; col < cols; col++)
2210 {
2211 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2212 if (column->IsHidden())
2213 continue; // skip it!
2214
2215 if ((col != 0) && model->IsContainer(item) && !model->HasContainerColumns(item))
2216 continue; // skip it!
2217
2218 const wxDataViewRenderer *renderer = column->GetRenderer();
2219 wxVariant value;
2220 model->GetValue( value, item, column->GetModelColumn() );
2221 wxDataViewRenderer *renderer2 = const_cast<wxDataViewRenderer*>(renderer);
2222 renderer2->SetValue( value );
2223 height = wxMax( height, renderer->GetSize().y );
2224 }
2225
2226 yy += height;
2227 if (y < yy)
2228 return row;
2229
2230 row++;
2231 }
2232 }
2233
2234 int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
2235 {
2236 const wxDataViewModel *model = GetOwner()->GetModel();
2237
2238 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2239 {
2240 wxASSERT( !IsVirtualList() );
2241
2242 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2243 // wxASSERT( node );
2244 if (!node) return m_lineHeight;
2245
2246 wxDataViewItem item = node->GetItem();
2247
2248 if (node && !node->HasChildren())
2249 {
2250 // Yes, if the node does not have any child, it must be a leaf which
2251 // mean that it is a temporarily created by GetTreeNodeByRow
2252 wxDELETE(node);
2253 }
2254
2255 int height = m_lineHeight;
2256
2257 unsigned int cols = GetOwner()->GetColumnCount();
2258 unsigned int col;
2259 for (col = 0; col < cols; col++)
2260 {
2261 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2262 if (column->IsHidden())
2263 continue; // skip it!
2264
2265 if ((col != 0) && model->IsContainer(item) && !model->HasContainerColumns(item))
2266 continue; // skip it!
2267
2268 const wxDataViewRenderer *renderer = column->GetRenderer();
2269 wxVariant value;
2270 model->GetValue( value, item, column->GetModelColumn() );
2271 wxDataViewRenderer *renderer2 = const_cast<wxDataViewRenderer*>(renderer);
2272 renderer2->SetValue( value );
2273 height = wxMax( height, renderer->GetSize().y );
2274 }
2275
2276 return height;
2277 }
2278 else
2279 {
2280 return m_lineHeight;
2281 }
2282 }
2283
2284 class RowToItemJob: public DoJob
2285 {
2286 public:
2287 RowToItemJob( unsigned int row , int current ) { this->row = row; this->current = current;}
2288 virtual ~RowToItemJob() { }
2289
2290 virtual int operator() ( wxDataViewTreeNode * node )
2291 {
2292 current ++;
2293 if( current == static_cast<int>(row))
2294 {
2295 ret = node->GetItem();
2296 return DoJob::OK;
2297 }
2298
2299 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2300 {
2301 current += node->GetSubTreeCount();
2302 return DoJob::IGR;
2303 }
2304 else
2305 {
2306 //If the current has no child node, we can find the desired item of the row number directly.
2307 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
2308 if( node->GetNodes().GetCount() == 0)
2309 {
2310 int index = static_cast<int>(row) - current - 1;
2311 ret = node->GetChildren().Item( index );
2312 return DoJob::OK;
2313 }
2314 return DoJob::CONT;
2315 }
2316 }
2317
2318 virtual int operator() ( void * n )
2319 {
2320 current ++;
2321 if( current == static_cast<int>(row))
2322 {
2323 ret = wxDataViewItem( n );
2324 return DoJob::OK;
2325 }
2326 return DoJob::CONT;
2327 }
2328 wxDataViewItem GetResult(){ return ret; }
2329 private:
2330 unsigned int row;
2331 int current;
2332 wxDataViewItem ret;
2333 };
2334
2335 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const
2336 {
2337 if (!m_root)
2338 {
2339 return wxDataViewItem( wxUIntToPtr(row) );
2340 }
2341 else
2342 {
2343 RowToItemJob job( row, -2 );
2344 Walker( m_root , job );
2345 return job.GetResult();
2346 }
2347 }
2348
2349 class RowToTreeNodeJob: public DoJob
2350 {
2351 public:
2352 RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
2353 {
2354 this->row = row;
2355 this->current = current;
2356 ret = NULL;
2357 parent = node;
2358 }
2359 virtual ~RowToTreeNodeJob(){ }
2360
2361 virtual int operator() ( wxDataViewTreeNode * node )
2362 {
2363 current ++;
2364 if( current == static_cast<int>(row))
2365 {
2366 ret = node;
2367 return DoJob::OK;
2368 }
2369
2370 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2371 {
2372 current += node->GetSubTreeCount();
2373 return DoJob::IGR;
2374 }
2375 else
2376 {
2377 parent = node;
2378 //If the current has no child node, we can find the desired item of the row number directly.
2379 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
2380 if( node->GetNodes().GetCount() == 0)
2381 {
2382 int index = static_cast<int>(row) - current - 1;
2383 void * n = node->GetChildren().Item( index );
2384 ret = new wxDataViewTreeNode( parent );
2385 ret->SetItem( wxDataViewItem( n ));
2386 ret->SetHasChildren(false);
2387 return DoJob::OK;
2388 }
2389 return DoJob::CONT;
2390 }
2391
2392
2393 }
2394
2395 virtual int operator() ( void * n )
2396 {
2397 current ++;
2398 if( current == static_cast<int>(row))
2399 {
2400 ret = new wxDataViewTreeNode( parent );
2401 ret->SetItem( wxDataViewItem( n ));
2402 ret->SetHasChildren(false);
2403 return DoJob::OK;
2404 }
2405
2406 return DoJob::CONT;
2407 }
2408 wxDataViewTreeNode * GetResult(){ return ret; }
2409 private:
2410 unsigned int row;
2411 int current;
2412 wxDataViewTreeNode * ret;
2413 wxDataViewTreeNode * parent;
2414 };
2415
2416
2417 wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) const
2418 {
2419 wxASSERT( !IsVirtualList() );
2420
2421 RowToTreeNodeJob job( row , -2, m_root );
2422 Walker( m_root , job );
2423 return job.GetResult();
2424 }
2425
2426 wxDataViewEvent wxDataViewMainWindow::SendExpanderEvent( wxEventType type, const wxDataViewItem & item )
2427 {
2428 wxWindow *parent = GetParent();
2429 wxDataViewEvent le(type, parent->GetId());
2430
2431 le.SetEventObject(parent);
2432 le.SetModel(GetOwner()->GetModel());
2433 le.SetItem( item );
2434
2435 parent->GetEventHandler()->ProcessEvent(le);
2436 return le;
2437 }
2438
2439
2440 bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const
2441 {
2442 if (IsVirtualList())
2443 return false;
2444
2445 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2446 if (!node)
2447 return false;
2448
2449 if (!node->HasChildren())
2450 {
2451 delete node;
2452 return false;
2453 }
2454
2455 return node->IsOpen();
2456 }
2457
2458
2459 void wxDataViewMainWindow::OnExpanding( unsigned int row )
2460 {
2461 if (IsVirtualList())
2462 return;
2463
2464 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2465 if( node != NULL )
2466 {
2467 if( node->HasChildren())
2468 {
2469 if( !node->IsOpen())
2470 {
2471 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,node->GetItem());
2472 //Check if the user prevent expanding
2473 if( e.GetSkipped() )
2474 return;
2475
2476 node->ToggleOpen();
2477 //Here I build the children of current node
2478 if( node->GetChildrenNumber() == 0 )
2479 {
2480 SortPrepare();
2481 ::BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
2482 }
2483 m_count = -1;
2484 UpdateDisplay();
2485 //Send the expanded event
2486 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
2487 }
2488 else
2489 {
2490 SelectRow( row, false );
2491 SelectRow( row + 1, true );
2492 ChangeCurrentRow( row + 1 );
2493 SendSelectionChangedEvent( GetItemByRow(row+1));
2494 }
2495 }
2496 else
2497 delete node;
2498 }
2499 }
2500
2501 void wxDataViewMainWindow::OnCollapsing(unsigned int row)
2502 {
2503 if (IsVirtualList())
2504 return;
2505
2506 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2507 if( node != NULL )
2508 {
2509 wxDataViewTreeNode * nd = node;
2510
2511 if( node->HasChildren() && node->IsOpen() )
2512 {
2513 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem());
2514 if( e.GetSkipped() )
2515 return;
2516 node->ToggleOpen();
2517 m_count = -1;
2518 UpdateDisplay();
2519 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,nd->GetItem());
2520 }
2521 else
2522 {
2523 node = node->GetParent();
2524 if( node != NULL )
2525 {
2526 int parent = GetRowByItem( node->GetItem() );
2527 if( parent >= 0 )
2528 {
2529 SelectRow( row, false);
2530 SelectRow(parent , true );
2531 ChangeCurrentRow( parent );
2532 SendSelectionChangedEvent( node->GetItem() );
2533 }
2534 }
2535 }
2536 if( !nd->HasChildren())
2537 delete nd;
2538 }
2539 }
2540
2541 wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
2542 {
2543 wxDataViewModel * model = GetOwner()->GetModel();
2544 if( model == NULL )
2545 return NULL;
2546
2547 if (!item.IsOk())
2548 return m_root;
2549
2550 //Compose the a parent-chain of the finding item
2551 ItemList list;
2552 list.DeleteContents( true );
2553 wxDataViewItem it( item );
2554 while( it.IsOk() )
2555 {
2556 wxDataViewItem * pItem = new wxDataViewItem( it );
2557 list.Insert( pItem );
2558 it = model->GetParent( it );
2559 }
2560
2561 //Find the item along the parent-chain.
2562 //This algorithm is designed to speed up the node-finding method
2563 wxDataViewTreeNode * node = m_root;
2564 for( ItemList::const_iterator iter = list.begin(); iter !=list.end(); iter++ )
2565 {
2566 if( node->HasChildren() )
2567 {
2568 if( node->GetChildrenNumber() == 0 )
2569 {
2570 SortPrepare();
2571 ::BuildTreeHelper(model, node->GetItem(), node);
2572 }
2573
2574 wxDataViewTreeNodes nodes = node->GetNodes();
2575 unsigned int i;
2576 bool found = false;
2577
2578 for (i = 0; i < nodes.GetCount(); i ++)
2579 {
2580 if (nodes[i]->GetItem() == (**iter))
2581 {
2582 if (nodes[i]->GetItem() == item)
2583 return nodes[i];
2584
2585 node = nodes[i];
2586 found = true;
2587 break;
2588 }
2589 }
2590 if (!found)
2591 return NULL;
2592 }
2593 else
2594 return NULL;
2595 }
2596 return NULL;
2597 }
2598
2599 void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column )
2600 {
2601 wxDataViewColumn *col = NULL;
2602 unsigned int cols = GetOwner()->GetColumnCount();
2603 unsigned int colnum = 0;
2604 int x, y;
2605 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
2606 for (unsigned x_start = 0; colnum < cols; colnum++)
2607 {
2608 col = GetOwner()->GetColumnAt(colnum);
2609 if (col->IsHidden())
2610 continue; // skip it!
2611
2612 unsigned int w = col->GetWidth();
2613 if (x_start+w >= (unsigned int)x)
2614 break;
2615
2616 x_start += w;
2617 }
2618
2619 column = col;
2620 item = GetItemByRow( GetLineAt( y ) );
2621 }
2622
2623 wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column )
2624 {
2625 int row = GetRowByItem(item);
2626 int y = GetLineStart( row );
2627 int h = GetLineHeight( m_lineHeight );
2628 int x = 0;
2629 wxDataViewColumn *col = NULL;
2630 for( int i = 0, cols = GetOwner()->GetColumnCount(); i < cols; i ++ )
2631 {
2632 col = GetOwner()->GetColumnAt( i );
2633 x += col->GetWidth();
2634 if( GetOwner()->GetColumnAt(i+1) == column )
2635 break;
2636 }
2637 int w = col->GetWidth();
2638 m_owner->CalcScrolledPosition( x, y, &x, &y );
2639 return wxRect(x, y, w, h);
2640 }
2641
2642 int wxDataViewMainWindow::RecalculateCount()
2643 {
2644 if (!m_root)
2645 {
2646 wxDataViewIndexListModel *list_model = (wxDataViewIndexListModel*) GetOwner()->GetModel();
2647 #ifndef __WXMAC__
2648 return list_model->GetLastIndex() + 1;
2649 #else
2650 return list_model->GetLastIndex() - 1;
2651 #endif
2652 }
2653 else
2654 {
2655 return m_root->GetSubTreeCount();
2656 }
2657 }
2658
2659 class ItemToRowJob : public DoJob
2660 {
2661 public:
2662 ItemToRowJob(const wxDataViewItem& item_, ItemList::const_iterator iter)
2663 : m_iter(iter),
2664 item(item_)
2665 {
2666 ret = -1;
2667 }
2668
2669 //Maybe binary search will help to speed up this process
2670 virtual int operator() ( wxDataViewTreeNode * node)
2671 {
2672 ret ++;
2673 if( node->GetItem() == item )
2674 {
2675 return DoJob::OK;
2676 }
2677
2678 if( node->GetItem() == **m_iter )
2679 {
2680 m_iter++;
2681 return DoJob::CONT;
2682 }
2683 else
2684 {
2685 ret += node->GetSubTreeCount();
2686 return DoJob::IGR;
2687 }
2688
2689 }
2690
2691 virtual int operator() ( void * n )
2692 {
2693 ret ++;
2694 if( n == item.GetID() )
2695 return DoJob::OK;
2696 return DoJob::CONT;
2697 }
2698 //the row number is begin from zero
2699 int GetResult() { return ret -1; }
2700
2701 private:
2702 ItemList::const_iterator m_iter;
2703 wxDataViewItem item;
2704 int ret;
2705
2706 };
2707
2708 int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
2709 {
2710 const wxDataViewModel * model = GetOwner()->GetModel();
2711 if( model == NULL )
2712 return -1;
2713
2714 if (!m_root)
2715 {
2716 return wxPtrToUInt( item.GetID() );
2717 }
2718 else
2719 {
2720 if( !item.IsOk() )
2721 return -1;
2722
2723 //Compose the a parent-chain of the finding item
2724 ItemList list;
2725 wxDataViewItem * pItem;
2726 list.DeleteContents( true );
2727 wxDataViewItem it( item );
2728 while( it.IsOk() )
2729 {
2730 pItem = new wxDataViewItem( it );
2731 list.Insert( pItem );
2732 it = model->GetParent( it );
2733 }
2734 pItem = new wxDataViewItem( );
2735 list.Insert( pItem );
2736
2737 ItemToRowJob job( item, list.begin() );
2738 Walker(m_root , job );
2739 return job.GetResult();
2740 }
2741 }
2742
2743 static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node)
2744 {
2745 if( !model->IsContainer( item ) )
2746 return;
2747
2748 wxDataViewItemArray children;
2749 unsigned int num = model->GetChildren( item, children);
2750
2751 unsigned int index = 0;
2752 while( index < num )
2753 {
2754 if( model->IsContainer( children[index] ) )
2755 {
2756 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
2757 n->SetItem(children[index]);
2758 n->SetHasChildren( true );
2759 node->AddNode( n );
2760 }
2761 else
2762 {
2763 node->AddLeaf( children[index].GetID() );
2764 }
2765 index ++;
2766 }
2767 node->SetSubTreeCount( num );
2768 wxDataViewTreeNode * n = node->GetParent();
2769 if( n != NULL)
2770 n->ChangeSubTreeCount(num);
2771
2772 }
2773
2774 void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
2775 {
2776 DestroyTree();
2777
2778 if (GetOwner()->GetModel()->IsVirtualListModel())
2779 {
2780 m_count = -1;
2781 return;
2782 }
2783
2784 m_root = new wxDataViewTreeNode( NULL );
2785 m_root->SetHasChildren(true);
2786
2787 //First we define a invalid item to fetch the top-level elements
2788 wxDataViewItem item;
2789 SortPrepare();
2790 BuildTreeHelper( model, item, m_root);
2791 m_count = -1;
2792 }
2793
2794 static void DestroyTreeHelper( wxDataViewTreeNode * node )
2795 {
2796 if( node->GetNodeNumber() != 0 )
2797 {
2798 int len = node->GetNodeNumber();
2799 int i = 0;
2800 wxDataViewTreeNodes& nodes = node->GetNodes();
2801 for(; i < len; i ++ )
2802 {
2803 DestroyTreeHelper(nodes[i]);
2804 }
2805 }
2806 delete node;
2807 }
2808
2809 void wxDataViewMainWindow::DestroyTree()
2810 {
2811 if (!IsVirtualList())
2812 {
2813 ::DestroyTreeHelper(m_root);
2814 m_count = 0;
2815 m_root = NULL;
2816 }
2817 }
2818
2819 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
2820 {
2821 if ( GetParent()->HandleAsNavigationKey(event) )
2822 return;
2823
2824 // no item -> nothing to do
2825 if (!HasCurrentRow())
2826 {
2827 event.Skip();
2828 return;
2829 }
2830
2831 // don't use m_linesPerPage directly as it might not be computed yet
2832 const int pageSize = GetCountPerPage();
2833 wxCHECK_RET( pageSize, _T("should have non zero page size") );
2834
2835 switch ( event.GetKeyCode() )
2836 {
2837 case WXK_RETURN:
2838 {
2839 if (m_currentRow >= 0)
2840 {
2841 wxWindow *parent = GetParent();
2842 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
2843 le.SetItem( GetItemByRow(m_currentRow) );
2844 le.SetEventObject(parent);
2845 le.SetModel(GetOwner()->GetModel());
2846
2847 parent->GetEventHandler()->ProcessEvent(le);
2848 }
2849 break;
2850 }
2851 case WXK_UP:
2852 if ( m_currentRow > 0 )
2853 OnArrowChar( m_currentRow - 1, event );
2854 break;
2855
2856 case WXK_DOWN:
2857 if ( m_currentRow < GetRowCount() - 1 )
2858 OnArrowChar( m_currentRow + 1, event );
2859 break;
2860 //Add the process for tree expanding/collapsing
2861 case WXK_LEFT:
2862 OnCollapsing(m_currentRow);
2863 break;
2864 case WXK_RIGHT:
2865 OnExpanding( m_currentRow);
2866 break;
2867 case WXK_END:
2868 if (!IsEmpty())
2869 OnArrowChar( GetRowCount() - 1, event );
2870 break;
2871
2872 case WXK_HOME:
2873 if (!IsEmpty())
2874 OnArrowChar( 0, event );
2875 break;
2876
2877 case WXK_PAGEUP:
2878 {
2879 int steps = pageSize - 1;
2880 int index = m_currentRow - steps;
2881 if (index < 0)
2882 index = 0;
2883
2884 OnArrowChar( index, event );
2885 }
2886 break;
2887
2888 case WXK_PAGEDOWN:
2889 {
2890 int steps = pageSize - 1;
2891 unsigned int index = m_currentRow + steps;
2892 unsigned int count = GetRowCount();
2893 if ( index >= count )
2894 index = count - 1;
2895
2896 OnArrowChar( index, event );
2897 }
2898 break;
2899
2900 default:
2901 event.Skip();
2902 }
2903 }
2904
2905 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
2906 {
2907 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2908 {
2909 // let the base handle mouse wheel events.
2910 event.Skip();
2911 return;
2912 }
2913
2914 int x = event.GetX();
2915 int y = event.GetY();
2916 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2917 wxDataViewColumn *col = NULL;
2918
2919 int xpos = 0;
2920 unsigned int cols = GetOwner()->GetColumnCount();
2921 unsigned int i;
2922 for (i = 0; i < cols; i++)
2923 {
2924 wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
2925 if (c->IsHidden())
2926 continue; // skip it!
2927
2928 if (x < xpos + c->GetWidth())
2929 {
2930 col = c;
2931 break;
2932 }
2933 xpos += c->GetWidth();
2934 }
2935 if (!col)
2936 return;
2937
2938 wxDataViewRenderer *cell = col->GetRenderer();
2939 unsigned int current = GetLineAt( y );
2940 if ((current >= GetRowCount()) || (x > GetEndOfLastCol()))
2941 {
2942 // Unselect all if below the last row ?
2943 return;
2944 }
2945
2946 //Test whether the mouse is hovered on the tree item button
2947 bool hover = false;
2948 if ((!IsVirtualList()) && (GetOwner()->GetExpanderColumn() == col))
2949 {
2950 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
2951 if( node!=NULL && node->HasChildren() )
2952 {
2953 int indent = node->GetIndentLevel();
2954 indent = GetOwner()->GetIndent()*indent;
2955 wxRect rect( xpos + indent + EXPANDER_MARGIN,
2956 GetLineStart( current ) + EXPANDER_MARGIN + (GetLineHeight(current)/2) - (m_lineHeight/2) - EXPANDER_OFFSET,
2957 m_lineHeight-2*EXPANDER_MARGIN,
2958 m_lineHeight-2*EXPANDER_MARGIN + EXPANDER_OFFSET);
2959 if( rect.Contains( x, y) )
2960 {
2961 //So the mouse is over the expander
2962 hover = true;
2963 if (m_underMouse && m_underMouse != node)
2964 {
2965 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
2966 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
2967 }
2968 if (m_underMouse != node)
2969 {
2970 //wxLogMessage("Do the row: %d", current);
2971 RefreshRow(current);
2972 }
2973 m_underMouse = node;
2974 }
2975 }
2976 if (node!=NULL && !node->HasChildren())
2977 delete node;
2978 }
2979 if (!hover)
2980 {
2981 if (m_underMouse != NULL)
2982 {
2983 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
2984 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
2985 m_underMouse = NULL;
2986 }
2987 }
2988
2989 wxDataViewModel *model = GetOwner()->GetModel();
2990
2991 if (event.Dragging())
2992 {
2993 if (m_dragCount == 0)
2994 {
2995 // we have to report the raw, physical coords as we want to be
2996 // able to call HitTest(event.m_pointDrag) from the user code to
2997 // get the item being dragged
2998 m_dragStart = event.GetPosition();
2999 }
3000
3001 m_dragCount++;
3002
3003 if (m_dragCount != 3)
3004 return;
3005
3006 if (event.LeftIsDown())
3007 {
3008 m_owner->CalcUnscrolledPosition( m_dragStart.x, m_dragStart.y, &m_dragStart.x, &m_dragStart.y );
3009 unsigned int drag_item_row = GetLineAt( m_dragStart.y );
3010 wxDataViewItem item = GetItemByRow( drag_item_row );
3011
3012 // Notify cell about drag
3013 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
3014 event.SetEventObject( m_owner );
3015 event.SetItem( item );
3016 event.SetModel( model );
3017 if (!m_owner->HandleWindowEvent( event ))
3018 return;
3019
3020 if (!event.IsAllowed())
3021 return;
3022
3023 wxDataObject *obj = event.GetDataObject();
3024 if (!obj)
3025 return;
3026
3027 wxDropSource drag( m_owner );
3028 drag.SetData( *obj );
3029 // wxImage image( 80, 20 );
3030 // wxBitmap bitmap( image );
3031 wxDragResult res = drag.DoDragDrop();
3032 delete obj;
3033 }
3034 return;
3035 }
3036 else
3037 {
3038 m_dragCount = 0;
3039 }
3040
3041 bool forceClick = false;
3042
3043 if (event.ButtonDClick())
3044 {
3045 m_renameTimer->Stop();
3046 m_lastOnSame = false;
3047 }
3048
3049 wxDataViewItem item = GetItemByRow(current);
3050 bool ignore_other_columns =
3051 ((GetOwner()->GetExpanderColumn() != col) &&
3052 (model->IsContainer(item)) &&
3053 (!model->HasContainerColumns(item)));
3054
3055 if (event.LeftDClick())
3056 {
3057 if ( current == m_lineLastClicked )
3058 {
3059 if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE))
3060 {
3061 wxVariant value;
3062 model->GetValue( value, item, col->GetModelColumn() );
3063 cell->SetValue( value );
3064 wxRect cell_rect( xpos, GetLineStart( current ),
3065 col->GetWidth(), GetLineHeight( current ) );
3066 cell->Activate( cell_rect, model, item, col->GetModelColumn() );
3067
3068 }
3069 else
3070 {
3071 wxWindow *parent = GetParent();
3072 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
3073 le.SetItem( item );
3074 le.SetEventObject(parent);
3075 le.SetModel(GetOwner()->GetModel());
3076
3077 parent->GetEventHandler()->ProcessEvent(le);
3078 }
3079 return;
3080 }
3081 else
3082 {
3083 // The first click was on another item, so don't interpret this as
3084 // a double click, but as a simple click instead
3085 forceClick = true;
3086 }
3087 }
3088
3089 if (event.LeftUp())
3090 {
3091 if (m_lineSelectSingleOnUp != (unsigned int)-1)
3092 {
3093 // select single line
3094 SelectAllRows( false );
3095 SelectRow( m_lineSelectSingleOnUp, true );
3096 SendSelectionChangedEvent( GetItemByRow(m_lineSelectSingleOnUp) );
3097 }
3098
3099 //Process the event of user clicking the expander
3100 bool expander = false;
3101 if ((!IsVirtualList()) && (GetOwner()->GetExpanderColumn() == col))
3102 {
3103 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
3104 if( node!=NULL && node->HasChildren() )
3105 {
3106 int indent = node->GetIndentLevel();
3107 indent = GetOwner()->GetIndent()*indent;
3108 wxRect rect( xpos + indent + EXPANDER_MARGIN,
3109 GetLineStart( current ) + EXPANDER_MARGIN + (GetLineHeight(current)/2) - (m_lineHeight/2) - EXPANDER_OFFSET,
3110 m_lineHeight-2*EXPANDER_MARGIN,
3111 m_lineHeight-2*EXPANDER_MARGIN + EXPANDER_OFFSET);
3112
3113 if( rect.Contains( x, y) )
3114 {
3115 expander = true;
3116 if( node->IsOpen() )
3117 OnCollapsing(current);
3118 else
3119 OnExpanding( current );
3120 }
3121 }
3122 if (node && !node->HasChildren())
3123 delete node;
3124 }
3125 //If the user click the expander, we do not do editing even if the column with expander are editable
3126 if (m_lastOnSame && !expander && !ignore_other_columns)
3127 {
3128 if ((col == m_currentCol) && (current == m_currentRow) &&
3129 (cell->GetMode() & wxDATAVIEW_CELL_EDITABLE) )
3130 {
3131 m_renameTimer->Start( 100, true );
3132 }
3133 }
3134
3135 m_lastOnSame = false;
3136 m_lineSelectSingleOnUp = (unsigned int)-1;
3137 }
3138 else
3139 {
3140 // This is necessary, because after a DnD operation in
3141 // from and to ourself, the up event is swallowed by the
3142 // DnD code. So on next non-up event (which means here and
3143 // now) m_lineSelectSingleOnUp should be reset.
3144 m_lineSelectSingleOnUp = (unsigned int)-1;
3145 }
3146
3147 if (event.RightDown())
3148 {
3149 m_lineBeforeLastClicked = m_lineLastClicked;
3150 m_lineLastClicked = current;
3151
3152 // If the item is already selected, do not update the selection.
3153 // Multi-selections should not be cleared if a selected item is clicked.
3154 if (!IsRowSelected(current))
3155 {
3156 SelectAllRows(false);
3157 ChangeCurrentRow(current);
3158 SelectRow(m_currentRow,true);
3159 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3160 }
3161
3162 wxVariant value;
3163 model->GetValue( value, item, col->GetModelColumn() );
3164 wxWindow *parent = GetParent();
3165 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
3166 le.SetItem( item );
3167 le.SetEventObject(parent);
3168 le.SetModel(GetOwner()->GetModel());
3169 le.SetValue(value);
3170 parent->GetEventHandler()->ProcessEvent(le);
3171 }
3172 else if (event.MiddleDown())
3173 {
3174 }
3175 if (event.LeftDown() || forceClick)
3176 {
3177 SetFocus();
3178
3179 m_lineBeforeLastClicked = m_lineLastClicked;
3180 m_lineLastClicked = current;
3181
3182 unsigned int oldCurrentRow = m_currentRow;
3183 bool oldWasSelected = IsRowSelected(m_currentRow);
3184
3185 bool cmdModifierDown = event.CmdDown();
3186 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
3187 {
3188 if ( IsSingleSel() || !IsRowSelected(current) )
3189 {
3190 SelectAllRows( false );
3191 ChangeCurrentRow(current);
3192 SelectRow(m_currentRow,true);
3193 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
3194 }
3195 else // multi sel & current is highlighted & no mod keys
3196 {
3197 m_lineSelectSingleOnUp = current;
3198 ChangeCurrentRow(current); // change focus
3199 }
3200 }
3201 else // multi sel & either ctrl or shift is down
3202 {
3203 if (cmdModifierDown)
3204 {
3205 ChangeCurrentRow(current);
3206 ReverseRowSelection(m_currentRow);
3207 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
3208 }
3209 else if (event.ShiftDown())
3210 {
3211 ChangeCurrentRow(current);
3212
3213 unsigned int lineFrom = oldCurrentRow,
3214 lineTo = current;
3215
3216 if ( lineTo < lineFrom )
3217 {
3218 lineTo = lineFrom;
3219 lineFrom = m_currentRow;
3220 }
3221
3222 SelectRows(lineFrom, lineTo, true);
3223 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
3224 }
3225 else // !ctrl, !shift
3226 {
3227 // test in the enclosing if should make it impossible
3228 wxFAIL_MSG( _T("how did we get here?") );
3229 }
3230 }
3231
3232 if (m_currentRow != oldCurrentRow)
3233 RefreshRow( oldCurrentRow );
3234
3235 wxDataViewColumn *oldCurrentCol = m_currentCol;
3236
3237 // Update selection here...
3238 m_currentCol = col;
3239
3240 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
3241 (current == oldCurrentRow)) && oldWasSelected;
3242
3243 // Call LeftClick after everything else as under GTK+
3244 if (cell->GetMode() & wxDATAVIEW_CELL_ACTIVATABLE)
3245 {
3246 // notify cell about right click
3247 wxVariant value;
3248 model->GetValue( value, item, col->GetModelColumn() );
3249 cell->SetValue( value );
3250 wxRect cell_rect( xpos, GetLineStart( current ),
3251 col->GetWidth(), GetLineHeight( current ) );
3252 /* ignore ret */ cell->LeftClick( event.GetPosition(), cell_rect, model, item, col->GetModelColumn());
3253 }
3254 }
3255 }
3256
3257 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
3258 {
3259 m_hasFocus = true;
3260
3261 if (HasCurrentRow())
3262 Refresh();
3263
3264 event.Skip();
3265 }
3266
3267 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
3268 {
3269 m_hasFocus = false;
3270
3271 if (HasCurrentRow())
3272 Refresh();
3273
3274 event.Skip();
3275 }
3276
3277 wxDataViewItem wxDataViewMainWindow::GetSelection() const
3278 {
3279 if( m_selection.GetCount() != 1 )
3280 return wxDataViewItem();
3281
3282 return GetItemByRow( m_selection.Item(0));
3283 }
3284
3285 //-----------------------------------------------------------------------------
3286 // wxDataViewCtrl
3287 //-----------------------------------------------------------------------------
3288 WX_DEFINE_LIST(wxDataViewColumnList)
3289
3290 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
3291
3292 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
3293 EVT_SIZE(wxDataViewCtrl::OnSize)
3294 END_EVENT_TABLE()
3295
3296 wxDataViewCtrl::~wxDataViewCtrl()
3297 {
3298 if (m_notifier)
3299 GetModel()->RemoveNotifier( m_notifier );
3300
3301 m_cols.Clear();
3302 }
3303
3304 void wxDataViewCtrl::Init()
3305 {
3306 m_cols.DeleteContents(true);
3307 m_notifier = NULL;
3308
3309 // No sorting column at start
3310 m_sortingColumnIdx = wxNOT_FOUND;
3311
3312 m_headerArea = NULL;
3313 }
3314
3315 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
3316 const wxPoint& pos, const wxSize& size,
3317 long style, const wxValidator& validator )
3318 {
3319 if ( (style & wxBORDER_MASK) == 0)
3320 style |= wxBORDER_SUNKEN;
3321
3322 Init();
3323
3324 if (!wxControl::Create( parent, id, pos, size,
3325 style | wxScrolledWindowStyle, validator))
3326 return false;
3327
3328 SetInitialSize(size);
3329
3330 #ifdef __WXMAC__
3331 MacSetClipChildren( true );
3332 #endif
3333
3334 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
3335
3336 if (HasFlag(wxDV_NO_HEADER))
3337 m_headerArea = NULL;
3338 else
3339 m_headerArea = new wxDataViewHeaderWindow(this);
3340
3341 SetTargetWindow( m_clientArea );
3342
3343 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
3344 if (m_headerArea)
3345 sizer->Add( m_headerArea, 0, wxGROW );
3346 sizer->Add( m_clientArea, 1, wxGROW );
3347 SetSizer( sizer );
3348
3349 return true;
3350 }
3351
3352 #ifdef __WXMSW__
3353 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
3354 WXWPARAM wParam,
3355 WXLPARAM lParam)
3356 {
3357 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
3358
3359 #ifndef __WXWINCE__
3360 // we need to process arrows ourselves for scrolling
3361 if ( nMsg == WM_GETDLGCODE )
3362 {
3363 rc |= DLGC_WANTARROWS;
3364 }
3365 #endif
3366
3367 return rc;
3368 }
3369 #endif
3370
3371 wxSize wxDataViewCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
3372 {
3373 wxSize newsize = size;
3374 if (!HasFlag(wxDV_NO_HEADER) && (m_headerArea))
3375 newsize.y -= m_headerArea->GetSize().y;
3376
3377 return newsize;
3378 }
3379
3380 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
3381 {
3382 // We need to override OnSize so that our scrolled
3383 // window a) does call Layout() to use sizers for
3384 // positioning the controls but b) does not query
3385 // the sizer for their size and use that for setting
3386 // the scrollable area as set that ourselves by
3387 // calling SetScrollbar() further down.
3388
3389 Layout();
3390
3391 AdjustScrollbars();
3392 }
3393
3394 void wxDataViewCtrl::SetFocus()
3395 {
3396 if (m_clientArea)
3397 m_clientArea->SetFocus();
3398 }
3399
3400 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
3401 {
3402 if (!wxDataViewCtrlBase::AssociateModel( model ))
3403 return false;
3404
3405 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
3406
3407 model->AddNotifier( m_notifier );
3408
3409 m_clientArea->DestroyTree();
3410
3411 m_clientArea->BuildTree(model);
3412
3413 m_clientArea->UpdateDisplay();
3414
3415 return true;
3416 }
3417
3418 bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format )
3419 {
3420 return m_clientArea->EnableDragSource( format );
3421 }
3422
3423 bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format )
3424 {
3425 return m_clientArea->EnableDropTarget( format );
3426 }
3427
3428 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
3429 {
3430 if (!wxDataViewCtrlBase::AppendColumn(col))
3431 return false;
3432
3433 m_cols.Append( col );
3434 OnColumnsCountChanged();
3435 return true;
3436 }
3437
3438 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
3439 {
3440 if (!wxDataViewCtrlBase::PrependColumn(col))
3441 return false;
3442
3443 m_cols.Insert( col );
3444 OnColumnsCountChanged();
3445 return true;
3446 }
3447
3448 bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
3449 {
3450 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
3451 return false;
3452
3453 m_cols.Insert( pos, col );
3454 OnColumnsCountChanged();
3455 return true;
3456 }
3457
3458 void wxDataViewCtrl::OnColumnChange(unsigned int idx)
3459 {
3460 if ( m_headerArea )
3461 m_headerArea->UpdateColumn(idx);
3462
3463 m_clientArea->UpdateDisplay();
3464 }
3465
3466 void wxDataViewCtrl::OnColumnsCountChanged()
3467 {
3468 if (m_headerArea)
3469 m_headerArea->SetColumnCount(GetColumnCount());
3470
3471 m_clientArea->UpdateDisplay();
3472 }
3473
3474 void wxDataViewCtrl::DoSetExpanderColumn()
3475 {
3476 m_clientArea->UpdateDisplay();
3477 }
3478
3479 void wxDataViewCtrl::DoSetIndent()
3480 {
3481 m_clientArea->UpdateDisplay();
3482 }
3483
3484 unsigned int wxDataViewCtrl::GetColumnCount() const
3485 {
3486 return m_cols.GetCount();
3487 }
3488
3489 wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
3490 {
3491 return m_cols[idx];
3492 }
3493
3494 wxDataViewColumn *wxDataViewCtrl::GetColumnAt(unsigned int pos) const
3495 {
3496 // columns can't be reordered if there is no header window which allows
3497 // to do this
3498 const unsigned idx = m_headerArea ? m_headerArea->GetColumnsOrder()[pos]
3499 : pos;
3500
3501 return GetColumn(idx);
3502 }
3503
3504 int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn *column) const
3505 {
3506 const unsigned count = m_cols.size();
3507 for ( unsigned n = 0; n < count; n++ )
3508 {
3509 if ( m_cols[n] == column )
3510 return n;
3511 }
3512
3513 return wxNOT_FOUND;
3514 }
3515
3516 void wxDataViewCtrl::ColumnMoved(wxDataViewColumn * WXUNUSED(col),
3517 unsigned int WXUNUSED(new_pos))
3518 {
3519 // do _not_ reorder m_cols elements here, they should always be in the
3520 // order in which columns were added, we only display the columns in
3521 // different order
3522 m_clientArea->UpdateDisplay();
3523 }
3524
3525 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
3526 {
3527 wxDataViewColumnList::compatibility_iterator ret = m_cols.Find( column );
3528 if (!ret)
3529 return false;
3530
3531 m_cols.Erase(ret);
3532 OnColumnsCountChanged();
3533
3534 return true;
3535 }
3536
3537 bool wxDataViewCtrl::ClearColumns()
3538 {
3539 m_cols.Clear();
3540 OnColumnsCountChanged();
3541 return true;
3542 }
3543
3544 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
3545 {
3546 int ret = 0,
3547 dummy = 0;
3548 unsigned int len = GetColumnCount();
3549 for ( unsigned int i = 0; i < len; i++ )
3550 {
3551 wxDataViewColumn * col = GetColumnAt(i);
3552 if (col->IsHidden())
3553 continue;
3554 ret += col->GetWidth();
3555 if (column==col)
3556 {
3557 CalcScrolledPosition( ret, dummy, &ret, &dummy );
3558 break;
3559 }
3560 }
3561 return ret;
3562 }
3563
3564 wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
3565 {
3566 return m_sortingColumnIdx == wxNOT_FOUND ? NULL
3567 : GetColumn(m_sortingColumnIdx);
3568 }
3569
3570 //Selection code with wxDataViewItem as parameters
3571 wxDataViewItem wxDataViewCtrl::GetSelection() const
3572 {
3573 return m_clientArea->GetSelection();
3574 }
3575
3576 int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
3577 {
3578 sel.Empty();
3579 wxDataViewSelection selection = m_clientArea->GetSelections();
3580 int len = selection.GetCount();
3581 for( int i = 0; i < len; i ++)
3582 {
3583 unsigned int row = selection[i];
3584 sel.Add( m_clientArea->GetItemByRow( row ) );
3585 }
3586 return len;
3587 }
3588
3589 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
3590 {
3591 wxDataViewSelection selection(wxDataViewSelectionCmp);
3592
3593 wxDataViewItem last_parent;
3594
3595 int len = sel.GetCount();
3596 for( int i = 0; i < len; i ++ )
3597 {
3598 wxDataViewItem item = sel[i];
3599 wxDataViewItem parent = GetModel()->GetParent( item );
3600 if (parent)
3601 {
3602 if (parent != last_parent)
3603 ExpandAncestors(item);
3604 }
3605
3606 last_parent = parent;
3607 int row = m_clientArea->GetRowByItem( item );
3608 if( row >= 0 )
3609 selection.Add( static_cast<unsigned int>(row) );
3610 }
3611
3612 m_clientArea->SetSelections( selection );
3613 }
3614
3615 void wxDataViewCtrl::Select( const wxDataViewItem & item )
3616 {
3617 ExpandAncestors( item );
3618
3619 int row = m_clientArea->GetRowByItem( item );
3620 if( row >= 0 )
3621 {
3622 //Unselect all rows before select another in the single select mode
3623 if (m_clientArea->IsSingleSel())
3624 m_clientArea->SelectAllRows(false);
3625 m_clientArea->SelectRow(row, true);
3626 }
3627 }
3628
3629 void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
3630 {
3631 int row = m_clientArea->GetRowByItem( item );
3632 if( row >= 0 )
3633 m_clientArea->SelectRow(row, false);
3634 }
3635
3636 bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
3637 {
3638 int row = m_clientArea->GetRowByItem( item );
3639 if( row >= 0 )
3640 {
3641 return m_clientArea->IsRowSelected(row);
3642 }
3643 return false;
3644 }
3645
3646 //Selection code with row number as parameter
3647 int wxDataViewCtrl::GetSelections( wxArrayInt & sel ) const
3648 {
3649 sel.Empty();
3650 wxDataViewSelection selection = m_clientArea->GetSelections();
3651 int len = selection.GetCount();
3652 for( int i = 0; i < len; i ++)
3653 {
3654 unsigned int row = selection[i];
3655 sel.Add( row );
3656 }
3657 return len;
3658 }
3659
3660 void wxDataViewCtrl::SetSelections( const wxArrayInt & sel )
3661 {
3662 wxDataViewSelection selection(wxDataViewSelectionCmp);
3663 int len = sel.GetCount();
3664 for( int i = 0; i < len; i ++ )
3665 {
3666 int row = sel[i];
3667 if( row >= 0 )
3668 selection.Add( static_cast<unsigned int>(row) );
3669 }
3670 m_clientArea->SetSelections( selection );
3671 }
3672
3673 void wxDataViewCtrl::Select( int row )
3674 {
3675 if( row >= 0 )
3676 {
3677 if (m_clientArea->IsSingleSel())
3678 m_clientArea->SelectAllRows(false);
3679 m_clientArea->SelectRow( row, true );
3680 }
3681 }
3682
3683 void wxDataViewCtrl::Unselect( int row )
3684 {
3685 if( row >= 0 )
3686 m_clientArea->SelectRow(row, false);
3687 }
3688
3689 bool wxDataViewCtrl::IsSelected( int row ) const
3690 {
3691 if( row >= 0 )
3692 return m_clientArea->IsRowSelected(row);
3693 return false;
3694 }
3695
3696 void wxDataViewCtrl::SelectRange( int from, int to )
3697 {
3698 wxArrayInt sel;
3699 for( int i = from; i < to; i ++ )
3700 sel.Add( i );
3701 m_clientArea->Select(sel);
3702 }
3703
3704 void wxDataViewCtrl::UnselectRange( int from, int to )
3705 {
3706 wxDataViewSelection sel = m_clientArea->GetSelections();
3707 for( int i = from; i < to; i ++ )
3708 if( sel.Index( i ) != wxNOT_FOUND )
3709 sel.Remove( i );
3710 m_clientArea->SetSelections(sel);
3711 }
3712
3713 void wxDataViewCtrl::SelectAll()
3714 {
3715 m_clientArea->SelectAllRows(true);
3716 }
3717
3718 void wxDataViewCtrl::UnselectAll()
3719 {
3720 m_clientArea->SelectAllRows(false);
3721 }
3722
3723 void wxDataViewCtrl::EnsureVisible( int row, int column )
3724 {
3725 if( row < 0 )
3726 row = 0;
3727 if( row > (int) m_clientArea->GetRowCount() )
3728 row = m_clientArea->GetRowCount();
3729
3730 int first = m_clientArea->GetFirstVisibleRow();
3731 int last = m_clientArea->GetLastVisibleRow();
3732 if( row < first )
3733 m_clientArea->ScrollTo( row, column );
3734 else if( row > last )
3735 m_clientArea->ScrollTo( row - last + first, column );
3736 else
3737 m_clientArea->ScrollTo( first, column );
3738 }
3739
3740 void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataViewColumn * column )
3741 {
3742 ExpandAncestors( item );
3743
3744 m_clientArea->RecalculateDisplay();
3745
3746 int row = m_clientArea->GetRowByItem(item);
3747 if( row >= 0 )
3748 {
3749 if( column == NULL )
3750 EnsureVisible(row, -1);
3751 else
3752 EnsureVisible( row, GetColumnIndex(column) );
3753 }
3754
3755 }
3756
3757 void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column ) const
3758 {
3759 m_clientArea->HitTest(point, item, column);
3760 }
3761
3762 wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column ) const
3763 {
3764 return m_clientArea->GetItemRect(item, column);
3765 }
3766
3767 wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
3768 {
3769 return m_clientArea->GetItemByRow( row );
3770 }
3771
3772 int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
3773 {
3774 return m_clientArea->GetRowByItem( item );
3775 }
3776
3777 void wxDataViewCtrl::Expand( const wxDataViewItem & item )
3778 {
3779 int row = m_clientArea->GetRowByItem( item );
3780 if (row != -1)
3781 m_clientArea->Expand(row);
3782 }
3783
3784 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
3785 {
3786 int row = m_clientArea->GetRowByItem( item );
3787 if (row != -1)
3788 m_clientArea->Collapse(row);
3789 }
3790
3791 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
3792 {
3793 int row = m_clientArea->GetRowByItem( item );
3794 if (row != -1)
3795 return m_clientArea->IsExpanded(row);
3796 return false;
3797 }
3798
3799
3800 #endif
3801 // !wxUSE_GENERICDATAVIEWCTRL
3802
3803 #endif
3804 // wxUSE_DATAVIEWCTRL