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