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