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