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