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