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