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