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