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