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