]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
fix warnings due to using deprecated-but-used-internally methods from the header...
[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, Otto Wyss
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 1
70
71 // NB: for some reason, this class must be dllexport'ed or we get warnings from
72 // MSVC in DLL build
73 class WXDLLIMPEXP_ADV wxDataViewHeaderWindowBase : public wxControl
74 {
75 public:
76 wxDataViewHeaderWindowBase()
77 { m_owner = NULL; }
78
79 bool Create(wxDataViewCtrl *parent, wxWindowID id,
80 const wxPoint &pos, const wxSize &size,
81 const wxString &name)
82 {
83 return wxWindow::Create(parent, id, pos, size, wxNO_BORDER, name);
84 }
85
86 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
87 wxDataViewCtrl *GetOwner() { return m_owner; }
88
89 // called on column addition/removal
90 virtual void UpdateDisplay() { /* by default, do nothing */ }
91
92 // returns the n-th column
93 virtual wxDataViewColumn *GetColumn(unsigned int n)
94 {
95 wxASSERT(m_owner);
96 wxDataViewColumn *ret = m_owner->GetColumn(n);
97 wxASSERT(ret);
98
99 return ret;
100 }
101
102 protected:
103 wxDataViewCtrl *m_owner;
104
105 // sends an event generated from the n-th wxDataViewColumn
106 void SendEvent(wxEventType type, unsigned int n);
107 };
108
109 // on wxMSW the header window (only that part however) can be made native!
110 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
111
112 #define COLUMN_WIDTH_OFFSET 2
113 #define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow
114
115 class wxDataViewHeaderWindowMSW : public wxDataViewHeaderWindowBase
116 {
117 public:
118
119 wxDataViewHeaderWindowMSW( wxDataViewCtrl *parent,
120 wxWindowID id,
121 const wxPoint &pos = wxDefaultPosition,
122 const wxSize &size = wxDefaultSize,
123 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
124 {
125 Create(parent, id, pos, size, name);
126 }
127
128 bool Create(wxDataViewCtrl *parent, wxWindowID id,
129 const wxPoint &pos, const wxSize &size,
130 const wxString &name);
131
132 ~wxDataViewHeaderWindowMSW();
133
134 // called when any column setting is changed and/or changed
135 // the column count
136 virtual void UpdateDisplay();
137
138 // called when the main window gets scrolled
139 virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL);
140
141 protected:
142 virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result);
143 virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags);
144
145 unsigned int GetColumnIdxFromHeader(NMHEADER *nmHDR);
146
147 wxDataViewColumn *GetColumnFromHeader(NMHEADER *nmHDR)
148 { return GetColumn(GetColumnIdxFromHeader(nmHDR)); }
149
150 private:
151 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW)
152 };
153
154 #else // !defined(__WXMSW__)
155
156 #define HEADER_WINDOW_HEIGHT 25
157 #define HEADER_HORIZ_BORDER 5
158 #define HEADER_VERT_BORDER 3
159 #define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow
160
161 class wxGenericDataViewHeaderWindow : public wxDataViewHeaderWindowBase
162 {
163 public:
164 wxGenericDataViewHeaderWindow( wxDataViewCtrl *parent,
165 wxWindowID id,
166 const wxPoint &pos = wxDefaultPosition,
167 const wxSize &size = wxDefaultSize,
168 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
169 {
170 Init();
171 Create(parent, id, pos, size, name);
172 }
173
174 bool Create(wxDataViewCtrl *parent, wxWindowID id,
175 const wxPoint &pos, const wxSize &size,
176 const wxString &name);
177
178 ~wxGenericDataViewHeaderWindow()
179 {
180 delete m_resizeCursor;
181 }
182
183 virtual void UpdateDisplay() { Refresh(); }
184
185 // event handlers:
186
187 void OnPaint( wxPaintEvent &event );
188 void OnMouse( wxMouseEvent &event );
189 void OnSetFocus( wxFocusEvent &event );
190
191
192 protected:
193
194 // vars used for column resizing:
195
196 wxCursor *m_resizeCursor;
197 const wxCursor *m_currentCursor;
198 bool m_isDragging;
199
200 bool m_dirty; // needs refresh?
201 int m_column; // index of the column being resized
202 int m_currentX; // divider line position in logical (unscrolled) coords
203 int m_minX; // minimal position beyond which the divider line
204 // can't be dragged in logical coords
205
206 // the pen used to draw the current column width drag line
207 // when resizing the columsn
208 wxPen m_penCurrent;
209
210
211 // internal utilities:
212
213 void Init()
214 {
215 m_currentCursor = (wxCursor *) NULL;
216 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
217
218 m_isDragging = false;
219 m_dirty = false;
220
221 m_column = wxNOT_FOUND;
222 m_currentX = 0;
223 m_minX = 0;
224
225 wxColour col = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
226 m_penCurrent = wxPen(col, 1, wxSOLID);
227 }
228
229 void DrawCurrent();
230 void AdjustDC(wxDC& dc);
231
232 private:
233 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow)
234 DECLARE_EVENT_TABLE()
235 };
236
237 #endif // defined(__WXMSW__)
238
239 //-----------------------------------------------------------------------------
240 // wxDataViewRenameTimer
241 //-----------------------------------------------------------------------------
242
243 class wxDataViewRenameTimer: public wxTimer
244 {
245 private:
246 wxDataViewMainWindow *m_owner;
247
248 public:
249 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
250 void Notify();
251 };
252
253 //-----------------------------------------------------------------------------
254 // wxDataViewTreeNode
255 //-----------------------------------------------------------------------------
256 class wxDataViewTreeNode;
257 WX_DEFINE_ARRAY_PTR( wxDataViewTreeNode *, wxDataViewTreeNodes );
258
259 class wxDataViewTreeNode
260 {
261 public:
262 wxDataViewTreeNode( wxDataViewTreeNode * parent )
263 { this->parent = parent;
264 if( parent == NULL )
265 open = true;
266 else
267 open = false;
268 hasChildren = false;
269 }
270 //I don't know what I need to do in the destructure
271 ~wxDataViewTreeNode()
272 { }
273
274 wxDataViewTreeNode * GetParent() { return parent; }
275 void SetParent( wxDataViewTreeNode * parent ) { this->parent = parent; }
276 wxDataViewTreeNodes & GetChildren() { return children; }
277 void SetChildren( wxDataViewTreeNodes children ) { this->children = children; }
278
279 wxDataViewTreeNode * GetChild( unsigned int n ) { return children.Item( n ); }
280 void InsertChild( wxDataViewTreeNode * child, unsigned int n) { children.Insert( child, n); }
281 void AppendChild( wxDataViewTreeNode * child ) { children.Add( child ); }
282
283 wxDataViewItem & GetItem() { return item; }
284 void SetItem( const wxDataViewItem & item ) { this->item = item; }
285
286 unsigned int GetChildrenNumber() { return children.GetCount(); }
287 int GetIndentLevel()
288 {
289 int ret = 0 ;
290 wxDataViewTreeNode * node = this;
291 while( node->GetParent()->GetParent() != NULL )
292 {
293 node = node->GetParent();
294 ret ++;
295 }
296 return ret;
297 }
298
299 bool IsOpen() { return open; }
300 void ToggleOpen(){ open = !open; }
301 bool HasChildren() { return hasChildren; }
302 void SetHasChildren( bool has ){ hasChildren = has; }
303 private:
304 wxDataViewTreeNode * parent;
305 wxDataViewTreeNodes children;
306 wxDataViewItem item;
307 bool open;
308 bool hasChildren;
309 };
310
311 //-----------------------------------------------------------------------------
312 // wxDataViewMainWindow
313 //-----------------------------------------------------------------------------
314
315 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
316 WXDLLIMPEXP_ADV);
317 WX_DECLARE_LIST(wxDataViewItem, ItemList);
318 WX_DEFINE_LIST(ItemList);
319
320 class wxDataViewMainWindow: public wxWindow
321 {
322 public:
323 wxDataViewMainWindow( wxDataViewCtrl *parent,
324 wxWindowID id,
325 const wxPoint &pos = wxDefaultPosition,
326 const wxSize &size = wxDefaultSize,
327 const wxString &name = wxT("wxdataviewctrlmainwindow") );
328 virtual ~wxDataViewMainWindow();
329
330 // notifications from wxDataViewModel
331 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
332 bool ItemDeleted( const wxDataViewItem &item );
333 bool ItemChanged( const wxDataViewItem &item );
334 bool ValueChanged( const wxDataViewItem &item, unsigned int col );
335 bool Cleared();
336
337 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
338 wxDataViewCtrl *GetOwner() { return m_owner; }
339 const wxDataViewCtrl *GetOwner() const { return m_owner; }
340
341 void OnPaint( wxPaintEvent &event );
342 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
343 void OnChar( wxKeyEvent &event );
344 void OnMouse( wxMouseEvent &event );
345 void OnSetFocus( wxFocusEvent &event );
346 void OnKillFocus( wxFocusEvent &event );
347
348 void UpdateDisplay();
349 void RecalculateDisplay();
350 void OnInternalIdle();
351
352 void OnRenameTimer();
353
354 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
355
356 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
357 void ChangeCurrentRow( unsigned int row );
358
359 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
360 bool IsEmpty() { return GetRowCount() == 0; }
361
362 int GetCountPerPage() const;
363 int GetEndOfLastCol() const;
364 unsigned int GetFirstVisibleRow() const;
365 //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
366 unsigned int GetLastVisibleRow();
367 unsigned int GetRowCount() ;
368
369 wxDataViewItem GetSelection();
370
371 void Select( const wxArrayInt& aSelections );
372 void SelectAllRows( bool on );
373 void SelectRow( unsigned int row, bool on );
374 void SelectRows( unsigned int from, unsigned int to, bool on );
375 void ReverseRowSelection( unsigned int row );
376 bool IsRowSelected( unsigned int row );
377
378 void RefreshRow( unsigned int row );
379 void RefreshRows( unsigned int from, unsigned int to );
380 void RefreshRowsAfter( unsigned int firstRow );
381
382 // returns the colour to be used for drawing the rules
383 wxColour GetRuleColour() const
384 {
385 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
386 }
387
388 //void EnsureVisible( unsigned int row );
389 wxRect GetLineRect( unsigned int row ) const;
390
391 //Some useful functions for row and item mapping
392 wxDataViewItem GetItemByRow( unsigned int row );
393 unsigned int GetRowByItem( const wxDataViewItem & item );
394
395 //Methods for building the mapping tree
396 void BuildTree( wxDataViewModel * model );
397 void DestroyTree();
398 private:
399 wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row );
400 //We did not need this temporarily
401 //wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
402
403 int RecalculateCount() ;
404
405 void OnExpanding( unsigned int row );
406 void OnCollapsing( unsigned int row );
407
408 wxDataViewTreeNode * FindNode( const wxDataViewItem & item );
409
410 private:
411 wxDataViewCtrl *m_owner;
412 int m_lineHeight;
413 bool m_dirty;
414
415 wxDataViewColumn *m_currentCol;
416 unsigned int m_currentRow;
417 wxDataViewSelection m_selection;
418
419 wxDataViewRenameTimer *m_renameTimer;
420 bool m_lastOnSame;
421
422 bool m_hasFocus;
423
424 int m_dragCount;
425 wxPoint m_dragStart;
426
427 // for double click logic
428 unsigned int m_lineLastClicked,
429 m_lineBeforeLastClicked,
430 m_lineSelectSingleOnUp;
431
432 // the pen used to draw horiz/vertical rules
433 wxPen m_penRule;
434
435 // the pen used to draw the expander and the lines
436 wxPen m_penExpander;
437
438 //This is the tree structure of the model
439 wxDataViewTreeNode * m_root;
440 int m_count;
441 private:
442 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
443 DECLARE_EVENT_TABLE()
444 };
445
446 // ---------------------------------------------------------
447 // wxGenericDataViewModelNotifier
448 // ---------------------------------------------------------
449
450 class wxGenericDataViewModelNotifier: public wxDataViewModelNotifier
451 {
452 public:
453 wxGenericDataViewModelNotifier( wxDataViewMainWindow *mainWindow )
454 { m_mainWindow = mainWindow; }
455
456 virtual bool ItemAdded( const wxDataViewItem & parent, const wxDataViewItem & item )
457 { return m_mainWindow->ItemAdded( parent , item ); }
458 virtual bool ItemDeleted( const wxDataViewItem & item )
459 { return m_mainWindow->ItemDeleted( item ); }
460 virtual bool ItemChanged( const wxDataViewItem & item )
461 { return m_mainWindow->ItemChanged(item); }
462 virtual bool ValueChanged( const wxDataViewItem & item , unsigned int col )
463 { return m_mainWindow->ValueChanged( item, col ); }
464 virtual bool Cleared()
465 { return m_mainWindow->Cleared(); }
466
467 wxDataViewMainWindow *m_mainWindow;
468 };
469
470 // ---------------------------------------------------------
471 // wxDataViewRenderer
472 // ---------------------------------------------------------
473
474 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
475
476 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
477 wxDataViewCellMode mode,
478 int align) :
479 wxDataViewRendererBase( varianttype, mode, align )
480 {
481 m_dc = NULL;
482 m_align = align;
483 m_mode = mode;
484 }
485
486 wxDataViewRenderer::~wxDataViewRenderer()
487 {
488 if (m_dc)
489 delete m_dc;
490 }
491
492 wxDC *wxDataViewRenderer::GetDC()
493 {
494 if (m_dc == NULL)
495 {
496 if (GetOwner() == NULL)
497 return NULL;
498 if (GetOwner()->GetOwner() == NULL)
499 return NULL;
500 m_dc = new wxClientDC( GetOwner()->GetOwner() );
501 }
502
503 return m_dc;
504 }
505
506 // ---------------------------------------------------------
507 // wxDataViewCustomRenderer
508 // ---------------------------------------------------------
509
510 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
511
512 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
513 wxDataViewCellMode mode, int align ) :
514 wxDataViewRenderer( varianttype, mode, align )
515 {
516 }
517
518 // ---------------------------------------------------------
519 // wxDataViewTextRenderer
520 // ---------------------------------------------------------
521
522 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
523
524 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
525 wxDataViewCellMode mode, int align ) :
526 wxDataViewCustomRenderer( varianttype, mode, align )
527 {
528 }
529
530 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
531 {
532 m_text = value.GetString();
533
534 return true;
535 }
536
537 bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
538 {
539 return false;
540 }
541
542 bool wxDataViewTextRenderer::HasEditorCtrl()
543 {
544 return true;
545 }
546
547 wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
548 wxRect labelRect, const wxVariant &value )
549 {
550 return new wxTextCtrl( parent, wxID_ANY, value,
551 wxPoint(labelRect.x,labelRect.y),
552 wxSize(labelRect.width,labelRect.height) );
553 }
554
555 bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
556 {
557 wxTextCtrl *text = (wxTextCtrl*) editor;
558 value = text->GetValue();
559 return true;
560 }
561
562 bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state )
563 {
564 wxDataViewCtrl *view = GetOwner()->GetOwner();
565 wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ?
566 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) :
567 view->GetForegroundColour();
568
569 dc->SetTextForeground(col);
570 dc->DrawText( m_text, cell.x, cell.y );
571
572 return true;
573 }
574
575 wxSize wxDataViewTextRenderer::GetSize() const
576 {
577 const wxDataViewCtrl *view = GetView();
578 if (!m_text.empty())
579 {
580 int x,y;
581 view->GetTextExtent( m_text, &x, &y );
582 return wxSize( x, y );
583 }
584 return wxSize(80,20);
585 }
586
587 // ---------------------------------------------------------
588 // wxDataViewBitmapRenderer
589 // ---------------------------------------------------------
590
591 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
592
593 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
594 wxDataViewCellMode mode, int align ) :
595 wxDataViewCustomRenderer( varianttype, mode, align )
596 {
597 }
598
599 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
600 {
601 if (value.GetType() == wxT("wxBitmap"))
602 m_bitmap << value;
603 if (value.GetType() == wxT("wxIcon"))
604 m_icon << value;
605
606 return true;
607 }
608
609 bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
610 {
611 return false;
612 }
613
614 bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
615 {
616 if (m_bitmap.Ok())
617 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
618 else if (m_icon.Ok())
619 dc->DrawIcon( m_icon, cell.x, cell.y );
620
621 return true;
622 }
623
624 wxSize wxDataViewBitmapRenderer::GetSize() const
625 {
626 if (m_bitmap.Ok())
627 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
628 else if (m_icon.Ok())
629 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
630
631 return wxSize(16,16);
632 }
633
634 // ---------------------------------------------------------
635 // wxDataViewToggleRenderer
636 // ---------------------------------------------------------
637
638 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
639
640 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
641 wxDataViewCellMode mode, int align ) :
642 wxDataViewCustomRenderer( varianttype, mode, align )
643 {
644 m_toggle = false;
645 }
646
647 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
648 {
649 m_toggle = value.GetBool();
650
651 return true;
652 }
653
654 bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
655 {
656 return false;
657 }
658
659 bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
660 {
661 // User wxRenderer here
662
663 wxRect rect;
664 rect.x = cell.x + cell.width/2 - 10;
665 rect.width = 20;
666 rect.y = cell.y + cell.height/2 - 10;
667 rect.height = 20;
668
669 int flags = 0;
670 if (m_toggle)
671 flags |= wxCONTROL_CHECKED;
672 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
673 flags |= wxCONTROL_DISABLED;
674
675 wxRendererNative::Get().DrawCheckBox(
676 GetOwner()->GetOwner(),
677 *dc,
678 rect,
679 flags );
680
681 return true;
682 }
683
684 bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
685 wxDataViewModel *model,
686 const wxDataViewItem & item, unsigned int col)
687 {
688 bool value = !m_toggle;
689 wxVariant variant = value;
690 model->SetValue( variant, item, col);
691 model->ValueChanged( item, col );
692 return true;
693 }
694
695 wxSize wxDataViewToggleRenderer::GetSize() const
696 {
697 return wxSize(20,20);
698 }
699
700 // ---------------------------------------------------------
701 // wxDataViewProgressRenderer
702 // ---------------------------------------------------------
703
704 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
705
706 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
707 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
708 wxDataViewCustomRenderer( varianttype, mode, align )
709 {
710 m_label = label;
711 m_value = 0;
712 }
713
714 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
715 {
716 }
717
718 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
719 {
720 m_value = (long) value;
721
722 if (m_value < 0) m_value = 0;
723 if (m_value > 100) m_value = 100;
724
725 return true;
726 }
727
728 bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
729 {
730 value = (long) m_value;
731 return true;
732 }
733
734 bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
735 {
736 double pct = (double)m_value / 100.0;
737 wxRect bar = cell;
738 bar.width = (int)(cell.width * pct);
739 dc->SetPen( *wxTRANSPARENT_PEN );
740 dc->SetBrush( *wxBLUE_BRUSH );
741 dc->DrawRectangle( bar );
742
743 dc->SetBrush( *wxTRANSPARENT_BRUSH );
744 dc->SetPen( *wxBLACK_PEN );
745 dc->DrawRectangle( cell );
746
747 return true;
748 }
749
750 wxSize wxDataViewProgressRenderer::GetSize() const
751 {
752 return wxSize(40,12);
753 }
754
755 // ---------------------------------------------------------
756 // wxDataViewDateRenderer
757 // ---------------------------------------------------------
758
759 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
760
761 #if wxUSE_DATE_RENDERER_POPUP
762
763 class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
764 {
765 public:
766 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
767 wxDataViewModel *model, const wxDataViewItem & item, unsigned int col) :
768 wxPopupTransientWindow( parent, wxBORDER_SIMPLE ),
769 m_item( item )
770 {
771 m_model = model;
772 m_col = col;
773 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
774 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
775 sizer->Add( m_cal, 1, wxGROW );
776 SetSizer( sizer );
777 sizer->Fit( this );
778 }
779
780 void OnCalendar( wxCalendarEvent &event );
781
782 wxCalendarCtrl *m_cal;
783 wxDataViewModel *m_model;
784 unsigned int m_col;
785 const wxDataViewItem & m_item;
786
787 protected:
788 virtual void OnDismiss()
789 {
790 }
791
792 private:
793 DECLARE_EVENT_TABLE()
794 };
795
796 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
797 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
798 END_EVENT_TABLE()
799
800 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
801 {
802 wxDateTime date = event.GetDate();
803 wxVariant value = date;
804 m_model->SetValue( value, m_item, m_col );
805 m_model->ValueChanged( m_item, m_col );
806 DismissAndNotify();
807 }
808
809 #endif // wxUSE_DATE_RENDERER_POPUP
810
811 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
812
813 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
814 wxDataViewCellMode mode, int align ) :
815 wxDataViewCustomRenderer( varianttype, mode, align )
816 {
817 }
818
819 bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
820 {
821 m_date = value.GetDateTime();
822
823 return true;
824 }
825
826 bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
827 {
828 value = m_date;
829 return true;
830 }
831
832 bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
833 {
834 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
835 wxString tmp = m_date.FormatDate();
836 dc->DrawText( tmp, cell.x, cell.y );
837
838 return true;
839 }
840
841 wxSize wxDataViewDateRenderer::GetSize() const
842 {
843 const wxDataViewCtrl* view = GetView();
844 wxString tmp = m_date.FormatDate();
845 wxCoord x,y,d;
846 view->GetTextExtent( tmp, &x, &y, &d );
847 return wxSize(x,y+d);
848 }
849
850 bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewModel *model,
851 const wxDataViewItem & item, unsigned int col )
852 {
853 wxVariant variant;
854 model->GetValue( variant, item, col );
855 wxDateTime value = variant.GetDateTime();
856
857 #if wxUSE_DATE_RENDERER_POPUP
858 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
859 GetOwner()->GetOwner()->GetParent(), &value, model, item, col);
860 wxPoint pos = wxGetMousePosition();
861 popup->Move( pos );
862 popup->Layout();
863 popup->Popup( popup->m_cal );
864 #else // !wxUSE_DATE_RENDERER_POPUP
865 wxMessageBox(value.Format());
866 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
867 return true;
868 }
869
870 // ---------------------------------------------------------
871 // wxDataViewColumn
872 // ---------------------------------------------------------
873
874 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
875
876 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
877 unsigned int model_column,
878 int width, wxAlignment align, int flags ) :
879 wxDataViewColumnBase( title, cell, model_column, width, align, flags )
880 {
881 SetAlignment(align);
882 SetTitle(title);
883 SetFlags(flags);
884
885 Init(width < 0 ? wxDVC_DEFAULT_WIDTH : width);
886 }
887
888 wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
889 unsigned int model_column,
890 int width, wxAlignment align, int flags ) :
891 wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags )
892 {
893 SetAlignment(align);
894 SetFlags(flags);
895
896 Init(width < 0 ? wxDVC_TOGGLE_DEFAULT_WIDTH : width);
897 }
898
899 wxDataViewColumn::~wxDataViewColumn()
900 {
901 }
902
903 void wxDataViewColumn::Init( int width )
904 {
905 m_width = width;
906 m_minWidth = wxDVC_DEFAULT_MINWIDTH;
907 m_ascending = true;
908 }
909
910 void wxDataViewColumn::SetResizeable( bool resizeable )
911 {
912 if (resizeable)
913 m_flags |= wxDATAVIEW_COL_RESIZABLE;
914 else
915 m_flags &= ~wxDATAVIEW_COL_RESIZABLE;
916 }
917
918 void wxDataViewColumn::SetHidden( bool hidden )
919 {
920 if (hidden)
921 m_flags |= wxDATAVIEW_COL_HIDDEN;
922 else
923 m_flags &= ~wxDATAVIEW_COL_HIDDEN;
924
925 // tell our owner to e.g. update its scrollbars:
926 if (GetOwner())
927 GetOwner()->OnColumnChange();
928 }
929
930 void wxDataViewColumn::SetSortable( bool sortable )
931 {
932 if (sortable)
933 m_flags |= wxDATAVIEW_COL_SORTABLE;
934 else
935 m_flags &= ~wxDATAVIEW_COL_SORTABLE;
936
937 // Update header button
938 if (GetOwner())
939 GetOwner()->OnColumnChange();
940 }
941
942 void wxDataViewColumn::SetSortOrder( bool ascending )
943 {
944 m_ascending = ascending;
945
946 // Update header button
947 if (GetOwner())
948 GetOwner()->OnColumnChange();
949 }
950
951 bool wxDataViewColumn::IsSortOrderAscending() const
952 {
953 return m_ascending;
954 }
955
956 void wxDataViewColumn::SetInternalWidth( int width )
957 {
958 m_width = width;
959
960 // the scrollbars of the wxDataViewCtrl needs to be recalculated!
961 if (m_owner && m_owner->m_clientArea)
962 m_owner->m_clientArea->RecalculateDisplay();
963 }
964
965 void wxDataViewColumn::SetWidth( int width )
966 {
967 m_owner->m_headerArea->UpdateDisplay();
968
969 SetInternalWidth(width);
970 }
971
972
973 //-----------------------------------------------------------------------------
974 // wxDataViewHeaderWindowBase
975 //-----------------------------------------------------------------------------
976
977 void wxDataViewHeaderWindowBase::SendEvent(wxEventType type, unsigned int n)
978 {
979 wxWindow *parent = GetParent();
980 wxDataViewEvent le(type, parent->GetId());
981
982 le.SetEventObject(parent);
983 le.SetColumn(n);
984 le.SetDataViewColumn(GetColumn(n));
985 le.SetModel(GetOwner()->GetModel());
986
987 // for events created by wxDataViewHeaderWindow the
988 // row / value fields are not valid
989
990 parent->GetEventHandler()->ProcessEvent(le);
991 }
992
993 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
994
995 // implemented in msw/listctrl.cpp:
996 int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
997
998 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow)
999
1000 bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl *parent, wxWindowID id,
1001 const wxPoint &pos, const wxSize &size,
1002 const wxString &name )
1003 {
1004 m_owner = parent;
1005
1006 if ( !CreateControl(parent, id, pos, size, 0, wxDefaultValidator, name) )
1007 return false;
1008
1009 int x = pos.x == wxDefaultCoord ? 0 : pos.x,
1010 y = pos.y == wxDefaultCoord ? 0 : pos.y,
1011 w = size.x == wxDefaultCoord ? 1 : size.x,
1012 h = size.y == wxDefaultCoord ? 22 : size.y;
1013
1014 // create the native WC_HEADER window:
1015 WXHWND hwndParent = (HWND)parent->GetHandle();
1016 WXDWORD msStyle = WS_CHILD | HDS_BUTTONS | HDS_HORZ | HDS_HOTTRACK | HDS_FULLDRAG;
1017 m_hWnd = CreateWindowEx(0,
1018 WC_HEADER,
1019 (LPCTSTR) NULL,
1020 msStyle,
1021 x, y, w, h,
1022 (HWND)hwndParent,
1023 (HMENU)-1,
1024 wxGetInstance(),
1025 (LPVOID) NULL);
1026 if (m_hWnd == NULL)
1027 {
1028 wxLogLastError(_T("CreateWindowEx"));
1029 return false;
1030 }
1031
1032 // we need to subclass the m_hWnd to force wxWindow::HandleNotify
1033 // to call wxDataViewHeaderWindow::MSWOnNotify
1034 SubclassWin(m_hWnd);
1035
1036 // the following is required to get the default win's font for
1037 // header windows and must be done befor sending the HDM_LAYOUT msg
1038 SetFont(GetFont());
1039
1040 RECT rcParent;
1041 HDLAYOUT hdl;
1042 WINDOWPOS wp;
1043
1044 // Retrieve the bounding rectangle of the parent window's
1045 // client area, and then request size and position values
1046 // from the header control.
1047 ::GetClientRect((HWND)hwndParent, &rcParent);
1048
1049 hdl.prc = &rcParent;
1050 hdl.pwpos = &wp;
1051 if (!SendMessage((HWND)m_hWnd, HDM_LAYOUT, 0, (LPARAM) &hdl))
1052 {
1053 wxLogLastError(_T("SendMessage"));
1054 return false;
1055 }
1056
1057 // Set the size, position, and visibility of the header control.
1058 SetWindowPos((HWND)m_hWnd,
1059 wp.hwndInsertAfter,
1060 wp.x, wp.y,
1061 wp.cx, wp.cy,
1062 wp.flags | SWP_SHOWWINDOW);
1063
1064 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1065 // a wxBoxSizer and in order to avoid super-big header windows,
1066 // we need to set our height as fixed
1067 SetMinSize(wxSize(-1, wp.cy));
1068 SetMaxSize(wxSize(-1, wp.cy));
1069
1070 return true;
1071 }
1072
1073 wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
1074 {
1075 UnsubclassWin();
1076 }
1077
1078 void wxDataViewHeaderWindowMSW::UpdateDisplay()
1079 {
1080 // remove old columns
1081 for (int j=0, max=Header_GetItemCount((HWND)m_hWnd); j < max; j++)
1082 Header_DeleteItem((HWND)m_hWnd, 0);
1083
1084 // add the updated array of columns to the header control
1085 unsigned int cols = GetOwner()->GetColumnCount();
1086 unsigned int added = 0;
1087 for (unsigned int i = 0; i < cols; i++)
1088 {
1089 wxDataViewColumn *col = GetColumn( i );
1090 if (col->IsHidden())
1091 continue; // don't add it!
1092
1093 HDITEM hdi;
1094 hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
1095 hdi.pszText = (wxChar *) col->GetTitle().wx_str();
1096 hdi.cxy = col->GetWidth();
1097 hdi.cchTextMax = sizeof(hdi.pszText)/sizeof(hdi.pszText[0]);
1098 hdi.fmt = HDF_LEFT | HDF_STRING;
1099
1100 // lParam is reserved for application's use:
1101 // we store there the column index to use it later in MSWOnNotify
1102 // (since columns may have been hidden)
1103 hdi.lParam = (LPARAM)i;
1104
1105 // the native wxMSW implementation of the header window
1106 // draws the column separator COLUMN_WIDTH_OFFSET pixels
1107 // on the right: to correct this effect we make the column
1108 // exactly COLUMN_WIDTH_OFFSET wider (for the first column):
1109 if (i == 0)
1110 hdi.cxy += COLUMN_WIDTH_OFFSET;
1111
1112 switch (col->GetAlignment())
1113 {
1114 case wxALIGN_LEFT:
1115 hdi.fmt |= HDF_LEFT;
1116 break;
1117 case wxALIGN_CENTER:
1118 case wxALIGN_CENTER_HORIZONTAL:
1119 hdi.fmt |= HDF_CENTER;
1120 break;
1121 case wxALIGN_RIGHT:
1122 hdi.fmt |= HDF_RIGHT;
1123 break;
1124
1125 default:
1126 // such alignment is not allowed for the column header!
1127 wxFAIL;
1128 }
1129
1130 SendMessage((HWND)m_hWnd, HDM_INSERTITEM,
1131 (WPARAM)added, (LPARAM)&hdi);
1132 added++;
1133 }
1134 }
1135
1136 unsigned int wxDataViewHeaderWindowMSW::GetColumnIdxFromHeader(NMHEADER *nmHDR)
1137 {
1138 unsigned int idx;
1139
1140 // NOTE: we don't just return nmHDR->iItem because when there are
1141 // hidden columns, nmHDR->iItem may be different from
1142 // nmHDR->pitem->lParam
1143
1144 if (nmHDR->pitem && nmHDR->pitem->mask & HDI_LPARAM)
1145 {
1146 idx = (unsigned int)nmHDR->pitem->lParam;
1147 return idx;
1148 }
1149
1150 HDITEM item;
1151 item.mask = HDI_LPARAM;
1152 Header_GetItem((HWND)m_hWnd, nmHDR->iItem, &item);
1153
1154 return (unsigned int)item.lParam;
1155 }
1156
1157 bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1158 {
1159 NMHDR *nmhdr = (NMHDR *)lParam;
1160
1161 // is it a message from the header?
1162 if ( nmhdr->hwndFrom != (HWND)m_hWnd )
1163 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1164
1165 NMHEADER *nmHDR = (NMHEADER *)nmhdr;
1166 switch ( nmhdr->code )
1167 {
1168 case HDN_BEGINTRACK:
1169 // user has started to resize a column:
1170 // do we need to veto it?
1171 if (!GetColumn(nmHDR->iItem)->IsResizeable())
1172 {
1173 // veto it!
1174 *result = TRUE;
1175 }
1176 break;
1177
1178 case HDN_BEGINDRAG:
1179 // user has started to reorder a column
1180 break;
1181
1182 case HDN_ITEMCHANGING:
1183 if (nmHDR->pitem != NULL &&
1184 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1185 {
1186 int minWidth = GetColumnFromHeader(nmHDR)->GetMinWidth();
1187 if (nmHDR->pitem->cxy < minWidth)
1188 {
1189 // do not allow the user to resize this column under
1190 // its minimal width:
1191 *result = TRUE;
1192 }
1193 }
1194 break;
1195
1196 case HDN_ITEMCHANGED: // user is resizing a column
1197 case HDN_ENDTRACK: // user has finished resizing a column
1198 case HDN_ENDDRAG: // user has finished reordering a column
1199
1200 // update the width of the modified column:
1201 if (nmHDR->pitem != NULL &&
1202 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1203 {
1204 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1205 unsigned int w = nmHDR->pitem->cxy;
1206 wxDataViewColumn *col = GetColumn(idx);
1207
1208 // see UpdateDisplay() for more info about COLUMN_WIDTH_OFFSET
1209 if (idx == 0 && w > COLUMN_WIDTH_OFFSET)
1210 w -= COLUMN_WIDTH_OFFSET;
1211
1212 if (w >= (unsigned)col->GetMinWidth())
1213 col->SetInternalWidth(w);
1214 }
1215 break;
1216
1217 case HDN_ITEMCLICK:
1218 {
1219 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1220 wxEventType evt = nmHDR->iButton == 0 ?
1221 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1222 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1223 SendEvent(evt, idx);
1224 }
1225 break;
1226
1227 case NM_RCLICK:
1228 {
1229 // NOTE: for some reason (i.e. for a bug in Windows)
1230 // the HDN_ITEMCLICK notification is not sent on
1231 // right clicks, so we need to handle NM_RCLICK
1232
1233 POINT ptClick;
1234 int column = wxMSWGetColumnClicked(nmhdr, &ptClick);
1235 if (column != wxNOT_FOUND)
1236 {
1237 HDITEM item;
1238 item.mask = HDI_LPARAM;
1239 Header_GetItem((HWND)m_hWnd, column, &item);
1240
1241 // 'idx' may be different from 'column' if there are
1242 // hidden columns...
1243 unsigned int idx = (unsigned int)item.lParam;
1244 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
1245 idx);
1246 }
1247 }
1248 break;
1249
1250 case HDN_GETDISPINFOW:
1251 // see wxListCtrl::MSWOnNotify for more info!
1252 break;
1253
1254 case HDN_ITEMDBLCLICK:
1255 {
1256 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1257 int w = GetOwner()->GetBestColumnWidth(idx);
1258
1259 // update the native control:
1260 HDITEM hd;
1261 ZeroMemory(&hd, sizeof(hd));
1262 hd.mask = HDI_WIDTH;
1263 hd.cxy = w;
1264 Header_SetItem(GetHwnd(),
1265 nmHDR->iItem, // NOTE: we don't want 'idx' here!
1266 &hd);
1267
1268 // update the wxDataViewColumn class:
1269 GetColumn(idx)->SetInternalWidth(w);
1270 }
1271 break;
1272
1273 default:
1274 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1275 }
1276
1277 return true;
1278 }
1279
1280 void wxDataViewHeaderWindowMSW::ScrollWindow(int WXUNUSED(dx), int WXUNUSED(dy),
1281 const wxRect *WXUNUSED(rect))
1282 {
1283 wxSize ourSz = GetClientSize();
1284 wxSize ownerSz = m_owner->GetClientSize();
1285
1286 // where should the (logical) origin of this window be placed?
1287 int x1 = 0, y1 = 0;
1288 m_owner->CalcUnscrolledPosition(0, 0, &x1, &y1);
1289
1290 // put this window on top of our parent and
1291 SetWindowPos((HWND)m_hWnd, HWND_TOP, -x1, 0,
1292 ownerSz.GetWidth() + x1, ourSz.GetHeight(),
1293 SWP_SHOWWINDOW);
1294 }
1295
1296 void wxDataViewHeaderWindowMSW::DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
1297 int WXUNUSED(w), int WXUNUSED(h),
1298 int WXUNUSED(f))
1299 {
1300 // the wxDataViewCtrl's internal wxBoxSizer will call this function when
1301 // the wxDataViewCtrl window gets resized: the following dummy call
1302 // to ScrollWindow() is required in order to get this header window
1303 // correctly repainted when it's (horizontally) scrolled:
1304
1305 ScrollWindow(0, 0);
1306 }
1307
1308 #else // !defined(__WXMSW__)
1309
1310 IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow)
1311 BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow)
1312 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint)
1313 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse)
1314 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus)
1315 END_EVENT_TABLE()
1316
1317 bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl *parent, wxWindowID id,
1318 const wxPoint &pos, const wxSize &size,
1319 const wxString &name )
1320 {
1321 m_owner = parent;
1322
1323 if (!wxDataViewHeaderWindowBase::Create(parent, id, pos, size, name))
1324 return false;
1325
1326 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
1327 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1328 SetOwnForegroundColour( attr.colFg );
1329 SetOwnBackgroundColour( attr.colBg );
1330 if (!m_hasFont)
1331 SetOwnFont( attr.font );
1332
1333 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1334 // a wxBoxSizer and in order to avoid super-big header windows,
1335 // we need to set our height as fixed
1336 SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1337 SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1338
1339 return true;
1340 }
1341
1342 void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1343 {
1344 int w, h;
1345 GetClientSize( &w, &h );
1346
1347 wxAutoBufferedPaintDC dc( this );
1348
1349 dc.SetBackground(GetBackgroundColour());
1350 dc.Clear();
1351
1352 int xpix;
1353 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1354
1355 int x;
1356 m_owner->GetViewStart( &x, NULL );
1357
1358 // account for the horz scrollbar offset
1359 dc.SetDeviceOrigin( -x * xpix, 0 );
1360
1361 dc.SetFont( GetFont() );
1362
1363 unsigned int cols = GetOwner()->GetColumnCount();
1364 unsigned int i;
1365 int xpos = 0;
1366 for (i = 0; i < cols; i++)
1367 {
1368 wxDataViewColumn *col = GetColumn( i );
1369 if (col->IsHidden())
1370 continue; // skip it!
1371
1372 int cw = col->GetWidth();
1373 int ch = h;
1374
1375 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE;
1376 if (col->IsSortable())
1377 {
1378 if (col->IsSortOrderAscending())
1379 sortArrow = wxHDR_SORT_ICON_UP;
1380 else
1381 sortArrow = wxHDR_SORT_ICON_DOWN;
1382 }
1383
1384 wxRendererNative::Get().DrawHeaderButton
1385 (
1386 this,
1387 dc,
1388 wxRect(xpos, 0, cw, ch-1),
1389 m_parent->IsEnabled() ? 0
1390 : (int)wxCONTROL_DISABLED,
1391 sortArrow
1392 );
1393
1394 // align as required the column title:
1395 int x = xpos;
1396 wxSize titleSz = dc.GetTextExtent(col->GetTitle());
1397 switch (col->GetAlignment())
1398 {
1399 case wxALIGN_LEFT:
1400 x += HEADER_HORIZ_BORDER;
1401 break;
1402 case wxALIGN_CENTER:
1403 case wxALIGN_CENTER_HORIZONTAL:
1404 x += (cw - titleSz.GetWidth() - 2 * HEADER_HORIZ_BORDER)/2;
1405 break;
1406 case wxALIGN_RIGHT:
1407 x += cw - titleSz.GetWidth() - HEADER_HORIZ_BORDER;
1408 break;
1409 }
1410
1411 // always center the title vertically:
1412 int y = wxMax((ch - titleSz.GetHeight()) / 2, HEADER_VERT_BORDER);
1413
1414 dc.SetClippingRegion( xpos+HEADER_HORIZ_BORDER,
1415 HEADER_VERT_BORDER,
1416 wxMax(cw - 2 * HEADER_HORIZ_BORDER, 1), // width
1417 wxMax(ch - 2 * HEADER_VERT_BORDER, 1)); // height
1418 dc.DrawText( col->GetTitle(), x, y );
1419 dc.DestroyClippingRegion();
1420
1421 xpos += cw;
1422 }
1423 }
1424
1425 void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
1426 {
1427 GetParent()->SetFocus();
1428 event.Skip();
1429 }
1430
1431 void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent &event )
1432 {
1433 // we want to work with logical coords
1434 int x;
1435 m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
1436 int y = event.GetY();
1437
1438 if (m_isDragging)
1439 {
1440 // we don't draw the line beyond our window,
1441 // but we allow dragging it there
1442 int w = 0;
1443 GetClientSize( &w, NULL );
1444 m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
1445 w -= 6;
1446
1447 // erase the line if it was drawn
1448 if (m_currentX < w)
1449 DrawCurrent();
1450
1451 if (event.ButtonUp())
1452 {
1453 m_isDragging = false;
1454 if (HasCapture())
1455 ReleaseMouse();
1456
1457 m_dirty = true;
1458
1459 GetColumn(m_column)->SetWidth(m_currentX - m_minX);
1460
1461 Refresh();
1462 GetOwner()->Refresh();
1463 }
1464 else
1465 {
1466 m_currentX = wxMax(m_minX + 7, x);
1467
1468 // draw in the new location
1469 if (m_currentX < w) DrawCurrent();
1470 }
1471
1472 }
1473 else // not dragging
1474 {
1475 m_minX = 0;
1476 m_column = wxNOT_FOUND;
1477
1478 bool hit_border = false;
1479
1480 // end of the current column
1481 int xpos = 0;
1482
1483 // find the column where this event occured
1484 int countCol = m_owner->GetColumnCount();
1485 for (int column = 0; column < countCol; column++)
1486 {
1487 wxDataViewColumn *p = GetColumn(column);
1488
1489 if (p->IsHidden())
1490 continue; // skip if not shown
1491
1492 xpos += p->GetWidth();
1493 m_column = column;
1494 if ((abs(x-xpos) < 3) && (y < 22))
1495 {
1496 hit_border = true;
1497 break;
1498 }
1499
1500 if (x < xpos)
1501 {
1502 // inside the column
1503 break;
1504 }
1505
1506 m_minX = xpos;
1507 }
1508
1509 if (m_column == wxNOT_FOUND)
1510 return;
1511
1512 bool resizeable = GetColumn(m_column)->IsResizeable();
1513 if (event.LeftDClick() && resizeable)
1514 {
1515 GetColumn(m_column)->SetWidth(GetOwner()->GetBestColumnWidth(m_column));
1516 Refresh();
1517 }
1518 else if (event.LeftDown() || event.RightUp())
1519 {
1520 if (hit_border && event.LeftDown() && resizeable)
1521 {
1522 m_isDragging = true;
1523 CaptureMouse();
1524 m_currentX = x;
1525 DrawCurrent();
1526 }
1527 else // click on a column
1528 {
1529 wxEventType evt = event.LeftDown() ?
1530 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1531 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1532 SendEvent(evt, m_column);
1533 }
1534 }
1535 else if (event.Moving())
1536 {
1537 if (hit_border && resizeable)
1538 m_currentCursor = m_resizeCursor;
1539 else
1540 m_currentCursor = wxSTANDARD_CURSOR;
1541
1542 SetCursor(*m_currentCursor);
1543 }
1544 }
1545 }
1546
1547 void wxGenericDataViewHeaderWindow::DrawCurrent()
1548 {
1549 int x1 = m_currentX;
1550 int y1 = 0;
1551 ClientToScreen (&x1, &y1);
1552
1553 int x2 = m_currentX-1;
1554 #ifdef __WXMSW__
1555 ++x2; // but why ????
1556 #endif
1557 int y2 = 0;
1558 m_owner->GetClientSize( NULL, &y2 );
1559 m_owner->ClientToScreen( &x2, &y2 );
1560
1561 wxScreenDC dc;
1562 dc.SetLogicalFunction(wxINVERT);
1563 dc.SetPen(m_penCurrent);
1564 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1565 AdjustDC(dc);
1566 dc.DrawLine(x1, y1, x2, y2);
1567 }
1568
1569 void wxGenericDataViewHeaderWindow::AdjustDC(wxDC& dc)
1570 {
1571 int xpix, x;
1572
1573 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1574 m_owner->GetViewStart( &x, NULL );
1575
1576 // shift the DC origin to match the position of the main window horizontal
1577 // scrollbar: this allows us to always use logical coords
1578 dc.SetDeviceOrigin( -x * xpix, 0 );
1579 }
1580
1581 #endif // defined(__WXMSW__)
1582
1583 //-----------------------------------------------------------------------------
1584 // wxDataViewRenameTimer
1585 //-----------------------------------------------------------------------------
1586
1587 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1588 {
1589 m_owner = owner;
1590 }
1591
1592 void wxDataViewRenameTimer::Notify()
1593 {
1594 m_owner->OnRenameTimer();
1595 }
1596
1597 //-----------------------------------------------------------------------------
1598 // wxDataViewMainWindow
1599 //-----------------------------------------------------------------------------
1600
1601 //The tree building helper, declared firstly
1602 void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node);
1603
1604 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
1605 {
1606 if (row1 > row2) return 1;
1607 if (row1 == row2) return 0;
1608 return -1;
1609 }
1610
1611
1612 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1613
1614 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1615 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1616 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1617 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
1618 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1619 EVT_CHAR (wxDataViewMainWindow::OnChar)
1620 END_EVENT_TABLE()
1621
1622 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1623 const wxPoint &pos, const wxSize &size, const wxString &name ) :
1624 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
1625 m_selection( wxDataViewSelectionCmp )
1626
1627 {
1628 SetOwner( parent );
1629
1630 m_lastOnSame = false;
1631 m_renameTimer = new wxDataViewRenameTimer( this );
1632
1633 // TODO: user better initial values/nothing selected
1634 m_currentCol = NULL;
1635 m_currentRow = 0;
1636
1637 // TODO: we need to calculate this smartly
1638 m_lineHeight =
1639 #ifdef __WXMSW__
1640 17;
1641 #else
1642 20;
1643 #endif
1644 wxASSERT(m_lineHeight > 2*PADDING_TOPBOTTOM);
1645
1646 m_dragCount = 0;
1647 m_dragStart = wxPoint(0,0);
1648 m_lineLastClicked = (unsigned int) -1;
1649 m_lineBeforeLastClicked = (unsigned int) -1;
1650 m_lineSelectSingleOnUp = (unsigned int) -1;
1651
1652 m_hasFocus = false;
1653
1654 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1655 SetBackgroundColour( *wxWHITE );
1656
1657 m_penRule = wxPen(GetRuleColour(), 1, wxSOLID);
1658
1659 //Here I compose a pen can draw black lines, maybe there are something system colour to use
1660 m_penExpander = wxPen( wxColour(0,0,0), 1, wxSOLID );
1661 //Some new added code to deal with the tree structure
1662 m_root = new wxDataViewTreeNode( NULL );
1663 m_root->SetHasChildren(true);
1664
1665 //Make m_count = -1 will cause the class recaculate the real displaying number of rows.
1666 m_count = -1 ;
1667 UpdateDisplay();
1668 }
1669
1670 wxDataViewMainWindow::~wxDataViewMainWindow()
1671 {
1672 DestroyTree();
1673 delete m_renameTimer;
1674 }
1675
1676 void wxDataViewMainWindow::OnRenameTimer()
1677 {
1678 // We have to call this here because changes may just have
1679 // been made and no screen update taken place.
1680 if ( m_dirty )
1681 wxSafeYield();
1682
1683 int xpos = 0;
1684 unsigned int cols = GetOwner()->GetColumnCount();
1685 unsigned int i;
1686 for (i = 0; i < cols; i++)
1687 {
1688 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1689 if (c->IsHidden())
1690 continue; // skip it!
1691
1692 if (c == m_currentCol)
1693 break;
1694 xpos += c->GetWidth();
1695 }
1696 wxRect labelRect( xpos, m_currentRow * m_lineHeight,
1697 m_currentCol->GetWidth(), m_lineHeight );
1698
1699 GetOwner()->CalcScrolledPosition( labelRect.x, labelRect.y,
1700 &labelRect.x, &labelRect.y);
1701
1702 wxDataViewItem item = GetItemByRow( m_currentRow );
1703 m_currentCol->GetRenderer()->StartEditing( item, labelRect );
1704 }
1705
1706 //------------------------------------------------------------------
1707 // Helper class for do operation on the tree node
1708 //------------------------------------------------------------------
1709 class DoJob
1710 {
1711 public:
1712 DoJob(){};
1713 virtual ~DoJob(){};
1714
1715 //The return value control how the tree-walker tranverse the tree
1716 // 0: Job done, stop tranverse and return
1717 // 1: Ignore the current node's subtree and continue
1718 // 2: Job not done, continue
1719 enum { OK = 0 , IGR = 1, CONT = 2 };
1720 virtual int operator() ( wxDataViewTreeNode * node ) = 0 ;
1721 };
1722
1723 class ItemAddJob: public DoJob
1724 {
1725 public:
1726 ItemAddJob( const wxDataViewItem & parent, const wxDataViewItem & item, int * count )
1727 { this->parent = parent ; this->item = item ; m_count = count; }
1728 virtual ~ItemAddJob(){};
1729
1730 virtual int operator() ( wxDataViewTreeNode * node )
1731 {
1732 if( node->GetItem() == parent )
1733 {
1734 node->SetHasChildren( true );
1735 wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node );
1736 newnode->SetItem(item);
1737 node->AppendChild( newnode);
1738 *m_count = -1;
1739 return OK;
1740 }
1741 return CONT;
1742 }
1743
1744 private:
1745 int * m_count;
1746 wxDataViewItem parent, item;
1747 };
1748
1749 bool Walker( wxDataViewTreeNode * node, DoJob & func )
1750 {
1751 if( node==NULL || !node->HasChildren())
1752 return false;
1753
1754 wxDataViewTreeNodes nodes = node->GetChildren();
1755 int len = node->GetChildrenNumber();
1756 int i = 0 ;
1757 for( ; i < len ; i ++ )
1758 {
1759 wxDataViewTreeNode * n = nodes[i];
1760 switch( func( n ) )
1761 {
1762 case DoJob::OK :
1763 return true ;
1764 case DoJob::IGR:
1765 continue;
1766 case DoJob::CONT:
1767 default:
1768 ;
1769 }
1770
1771 if( Walker( n , func ) )
1772 return true;
1773 }
1774 return false;
1775 }
1776
1777 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
1778 {
1779 wxDataViewTreeNode * node;
1780 node = FindNode(parent);
1781
1782 if( node == NULL )
1783 return false;
1784
1785 node->SetHasChildren( true );
1786 wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node );
1787 newnode->SetItem(item);
1788 node->AppendChild( newnode);
1789 m_count = -1;
1790 UpdateDisplay();
1791 return true;
1792 }
1793
1794 class ItemDeleteJob: public DoJob
1795 {
1796 public:
1797 ItemDeleteJob( const wxDataViewItem & item, int * count ) { m_item = item; m_count = count; }
1798 virtual ~ItemDeleteJob(){}
1799 virtual int operator() ( wxDataViewTreeNode * node )
1800 {
1801 if( node->GetItem() == m_item )
1802 {
1803 node->GetParent()->GetChildren().Remove( node );
1804 delete node;
1805 *m_count = -1;
1806 return DoJob::OK;
1807 }
1808 return DoJob::CONT;
1809 }
1810
1811 private:
1812 int * m_count;
1813 wxDataViewItem m_item;
1814 };
1815
1816 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem & item)
1817 {
1818 wxDataViewTreeNode * node;
1819 node = FindNode(item);
1820
1821 if( node == NULL )
1822 {
1823 return false;
1824 }
1825
1826 wxDataViewTreeNode * parent = node->GetParent();
1827 parent->GetChildren().Remove( node );
1828 if( parent->GetChildrenNumber() == 0)
1829 parent->SetHasChildren( false );
1830
1831 m_count = -1;
1832 UpdateDisplay();
1833 return true;
1834 }
1835
1836 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
1837 {
1838 unsigned int row = GetRowByItem(item);
1839 RefreshRow( row );
1840 return true;
1841 }
1842
1843 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int WXUNUSED(col) )
1844 {
1845 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
1846 /*#define MAX_VIRTUAL_WIDTH 100000
1847
1848 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
1849 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1850 Refresh( true, &rect );
1851
1852 return true;
1853 */
1854 unsigned int row = GetRowByItem(item);
1855 RefreshRow( row );
1856 return true;
1857 }
1858
1859 bool wxDataViewMainWindow::Cleared()
1860 {
1861 DestroyTree();
1862 UpdateDisplay();
1863 return true;
1864 }
1865
1866 void wxDataViewMainWindow::UpdateDisplay()
1867 {
1868 m_dirty = true;
1869 }
1870
1871 void wxDataViewMainWindow::OnInternalIdle()
1872 {
1873 wxWindow::OnInternalIdle();
1874
1875 if (m_dirty)
1876 {
1877 RecalculateDisplay();
1878 m_dirty = false;
1879 }
1880 }
1881
1882 void wxDataViewMainWindow::RecalculateDisplay()
1883 {
1884 wxDataViewModel *model = GetOwner()->GetModel();
1885 if (!model)
1886 {
1887 Refresh();
1888 return;
1889 }
1890
1891 int width = GetEndOfLastCol();
1892 int height = GetRowCount() * m_lineHeight;
1893
1894 SetVirtualSize( width, height );
1895 GetOwner()->SetScrollRate( 10, m_lineHeight );
1896
1897 Refresh();
1898 }
1899
1900 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1901 {
1902 wxWindow::ScrollWindow( dx, dy, rect );
1903
1904 if (GetOwner()->m_headerArea)
1905 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1906 }
1907
1908 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1909 {
1910 wxDataViewModel *model = GetOwner()->GetModel();
1911 wxAutoBufferedPaintDC dc( this );
1912
1913 // prepare the DC
1914 dc.SetBackground(GetBackgroundColour());
1915 dc.Clear();
1916 GetOwner()->PrepareDC( dc );
1917 dc.SetFont( GetFont() );
1918
1919 wxRect update = GetUpdateRegion().GetBox();
1920 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1921
1922 // compute which items needs to be redrawn
1923 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
1924 unsigned int item_count =
1925 wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1926 (int)(GetRowCount( )- item_start) );
1927 unsigned int item_last = item_start + item_count;
1928
1929 // compute which columns needs to be redrawn
1930 unsigned int cols = GetOwner()->GetColumnCount();
1931 unsigned int col_start = 0;
1932 unsigned int x_start = 0;
1933 for (x_start = 0; col_start < cols; col_start++)
1934 {
1935 wxDataViewColumn *col = GetOwner()->GetColumn(col_start);
1936 if (col->IsHidden())
1937 continue; // skip it!
1938
1939 unsigned int w = col->GetWidth();
1940 if (x_start+w >= (unsigned int)update.x)
1941 break;
1942
1943 x_start += w;
1944 }
1945
1946 unsigned int col_last = col_start;
1947 unsigned int x_last = x_start;
1948 for (; col_last < cols; col_last++)
1949 {
1950 wxDataViewColumn *col = GetOwner()->GetColumn(col_last);
1951 if (col->IsHidden())
1952 continue; // skip it!
1953
1954 if (x_last > (unsigned int)update.GetRight())
1955 break;
1956
1957 x_last += col->GetWidth();
1958 }
1959
1960 // Draw horizontal rules if required
1961 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1962 {
1963 dc.SetPen(m_penRule);
1964 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1965
1966 for (unsigned int i = item_start; i <= item_last+1; i++)
1967 {
1968 int y = i * m_lineHeight;
1969 dc.DrawLine(x_start, y, x_last, y);
1970 }
1971 }
1972
1973 // Draw vertical rules if required
1974 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1975 {
1976 dc.SetPen(m_penRule);
1977 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1978
1979 int x = x_start;
1980 for (unsigned int i = col_start; i < col_last; i++)
1981 {
1982 wxDataViewColumn *col = GetOwner()->GetColumn(i);
1983 if (col->IsHidden())
1984 continue; // skip it
1985
1986 dc.DrawLine(x, item_start * m_lineHeight,
1987 x, item_last * m_lineHeight);
1988
1989 x += col->GetWidth();
1990 }
1991
1992 // Draw last vertical rule
1993 dc.DrawLine(x, item_start * m_lineHeight,
1994 x, item_last * m_lineHeight);
1995 }
1996
1997 // redraw the background for the items which are selected/current
1998 for (unsigned int item = item_start; item < item_last; item++)
1999 {
2000 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
2001 if (selected || item == m_currentRow)
2002 {
2003 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
2004 if (item == m_currentRow)
2005 flags |= wxCONTROL_CURRENT;
2006 if (m_hasFocus)
2007 flags |= wxCONTROL_FOCUSED;
2008
2009 wxRect rect( x_start, item*m_lineHeight, x_last, m_lineHeight );
2010 wxRendererNative::Get().DrawItemSelectionRect
2011 (
2012 this,
2013 dc,
2014 rect,
2015 flags
2016 );
2017 }
2018 }
2019
2020 // redraw all cells for all rows which must be repainted and for all columns
2021 wxRect cell_rect;
2022 cell_rect.x = x_start;
2023 cell_rect.height = m_lineHeight; // -1 is for the horizontal rules
2024 for (unsigned int i = col_start; i < col_last; i++)
2025 {
2026 wxDataViewColumn *col = GetOwner()->GetColumn( i );
2027 wxDataViewRenderer *cell = col->GetRenderer();
2028 cell_rect.width = col->GetWidth();
2029
2030 if (col->IsHidden())
2031 continue; // skipt it!
2032
2033 for (unsigned int item = item_start; item < item_last; item++)
2034 {
2035 // get the cell value and set it into the renderer
2036 wxVariant value;
2037 wxDataViewTreeNode * node = GetTreeNodeByRow(item);
2038 if( node == NULL )
2039 continue;
2040
2041 wxDataViewItem dataitem = node->GetItem();
2042 model->GetValue( value, dataitem, col->GetModelColumn());
2043 cell->SetValue( value );
2044
2045 // update the y offset
2046 cell_rect.y = item * m_lineHeight;
2047
2048 //Draw the expander here. Please notice that I use const number for all pixel data. When the final API are determined
2049 //I will change this to the data member of the class wxDataViewCtrl
2050 int indent = node->GetIndentLevel();
2051 if( col->GetModelColumn() == GetOwner()->GetExpanderColumn() )
2052 {
2053 //Calculate the indent first
2054 indent = cell_rect.x + GetOwner()->GetIndent() * indent;
2055
2056 int expander_width = m_lineHeight - 2*EXPANDER_MARGIN;
2057 // change the cell_rect.x to the appropriate pos
2058 int expander_x = indent + EXPANDER_MARGIN , expander_y = cell_rect.y + EXPANDER_MARGIN ;
2059 indent = indent + m_lineHeight ; //try to use the m_lineHeight as the expander space
2060 dc.SetPen( m_penExpander );
2061 dc.SetBrush( wxNullBrush );
2062 if( node->HasChildren() )
2063 {
2064 //dc.DrawRoundedRectangle( expander_x,expander_y,expander_width,expander_width, 1.0);
2065 //dc.DrawLine( expander_x + 2 , expander_y + expander_width/2, expander_x + expander_width - 2, expander_y + expander_width/2 );
2066 wxRect rect( expander_x , expander_y, expander_width, expander_width);
2067 if( node->IsOpen() )
2068 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, wxCONTROL_EXPANDED );
2069 else
2070 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect );
2071 }
2072 else
2073 {
2074 // I am wandering whether we should draw dot lines between tree nodes
2075 }
2076
2077 //force the expander column to left-center align
2078 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
2079 }
2080
2081
2082 // cannot be bigger than allocated space
2083 wxSize size = cell->GetSize();
2084 // Because of the tree structure indent, here we should minus the width of the cell for drawing
2085 size.x = wxMin( size.x + 2*PADDING_RIGHTLEFT, cell_rect.width - indent );
2086 size.y = wxMin( size.y + 2*PADDING_TOPBOTTOM, cell_rect.height );
2087
2088 wxRect item_rect(cell_rect.GetTopLeft(), size);
2089 int align = cell->GetAlignment();
2090
2091 // horizontal alignment:
2092 item_rect.x = cell_rect.x;
2093 if (align & wxALIGN_CENTER_HORIZONTAL)
2094 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
2095 else if (align & wxALIGN_RIGHT)
2096 item_rect.x = cell_rect.x + cell_rect.width - size.x;
2097 //else: wxALIGN_LEFT is the default
2098
2099 // vertical alignment:
2100 item_rect.y = cell_rect.y;
2101 if (align & wxALIGN_CENTER_VERTICAL)
2102 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
2103 else if (align & wxALIGN_BOTTOM)
2104 item_rect.y = cell_rect.y + cell_rect.height - size.y;
2105 //else: wxALIGN_TOP is the default
2106
2107 // add padding
2108 item_rect.x += PADDING_RIGHTLEFT;
2109 item_rect.y += PADDING_TOPBOTTOM;
2110 item_rect.width = size.x - 2 * PADDING_RIGHTLEFT;
2111 item_rect.height = size.y - 2 * PADDING_TOPBOTTOM;
2112
2113 //Here we add the tree indent
2114 item_rect.x += indent;
2115
2116 int state = 0;
2117 if (m_selection.Index(item) != wxNOT_FOUND)
2118 state |= wxDATAVIEW_CELL_SELECTED;
2119
2120 // TODO: it would be much more efficient to create a clipping
2121 // region for the entire column being rendered (in the OnPaint
2122 // of wxDataViewMainWindow) instead of a single clip region for
2123 // each cell. However it would mean that each renderer should
2124 // respect the given wxRect's top & bottom coords, eventually
2125 // violating only the left & right coords - however the user can
2126 // make its own renderer and thus we cannot be sure of that.
2127 dc.SetClippingRegion( item_rect );
2128 cell->Render( item_rect, &dc, state );
2129 dc.DestroyClippingRegion();
2130 }
2131
2132 cell_rect.x += cell_rect.width;
2133 }
2134 }
2135
2136 int wxDataViewMainWindow::GetCountPerPage() const
2137 {
2138 wxSize size = GetClientSize();
2139 return size.y / m_lineHeight;
2140 }
2141
2142 int wxDataViewMainWindow::GetEndOfLastCol() const
2143 {
2144 int width = 0;
2145 unsigned int i;
2146 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
2147 {
2148 const wxDataViewColumn *c =
2149 wx_const_cast(wxDataViewCtrl*, GetOwner())->GetColumn( i );
2150
2151 if (!c->IsHidden())
2152 width += c->GetWidth();
2153 }
2154 return width;
2155 }
2156
2157 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
2158 {
2159 int x = 0;
2160 int y = 0;
2161 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2162
2163 return y / m_lineHeight;
2164 }
2165
2166 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
2167 {
2168 wxSize client_size = GetClientSize();
2169 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
2170 &client_size.x, &client_size.y );
2171
2172 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
2173 }
2174
2175 unsigned int wxDataViewMainWindow::GetRowCount()
2176 {
2177 if ( m_count == -1 )
2178 {
2179 m_count = RecalculateCount();
2180 int width, height;
2181 GetVirtualSize( &width, &height );
2182 height = m_count * m_lineHeight;
2183
2184 SetVirtualSize( width, height );
2185 }
2186 return m_count;
2187 }
2188
2189 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
2190 {
2191 m_currentRow = row;
2192
2193 // send event
2194 }
2195
2196 void wxDataViewMainWindow::SelectAllRows( bool on )
2197 {
2198 if (IsEmpty())
2199 return;
2200
2201 if (on)
2202 {
2203 m_selection.Clear();
2204 for (unsigned int i = 0; i < GetRowCount(); i++)
2205 m_selection.Add( i );
2206 Refresh();
2207 }
2208 else
2209 {
2210 unsigned int first_visible = GetFirstVisibleRow();
2211 unsigned int last_visible = GetLastVisibleRow();
2212 unsigned int i;
2213 for (i = 0; i < m_selection.GetCount(); i++)
2214 {
2215 unsigned int row = m_selection[i];
2216 if ((row >= first_visible) && (row <= last_visible))
2217 RefreshRow( row );
2218 }
2219 m_selection.Clear();
2220 }
2221 }
2222
2223 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
2224 {
2225 if (m_selection.Index( row ) == wxNOT_FOUND)
2226 {
2227 if (on)
2228 {
2229 m_selection.Add( row );
2230 RefreshRow( row );
2231 }
2232 }
2233 else
2234 {
2235 if (!on)
2236 {
2237 m_selection.Remove( row );
2238 RefreshRow( row );
2239 }
2240 }
2241 }
2242
2243 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
2244 {
2245 if (from > to)
2246 {
2247 unsigned int tmp = from;
2248 from = to;
2249 to = tmp;
2250 }
2251
2252 unsigned int i;
2253 for (i = from; i <= to; i++)
2254 {
2255 if (m_selection.Index( i ) == wxNOT_FOUND)
2256 {
2257 if (on)
2258 m_selection.Add( i );
2259 }
2260 else
2261 {
2262 if (!on)
2263 m_selection.Remove( i );
2264 }
2265 }
2266 RefreshRows( from, to );
2267 }
2268
2269 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2270 {
2271 for (size_t i=0; i < aSelections.GetCount(); i++)
2272 {
2273 int n = aSelections[i];
2274
2275 m_selection.Add( n );
2276 RefreshRow( n );
2277 }
2278 }
2279
2280 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
2281 {
2282 if (m_selection.Index( row ) == wxNOT_FOUND)
2283 m_selection.Add( row );
2284 else
2285 m_selection.Remove( row );
2286 RefreshRow( row );
2287 }
2288
2289 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
2290 {
2291 return (m_selection.Index( row ) != wxNOT_FOUND);
2292 }
2293
2294 void wxDataViewMainWindow::RefreshRow( unsigned int row )
2295 {
2296 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
2297 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2298
2299 wxSize client_size = GetClientSize();
2300 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2301 wxRect intersect_rect = client_rect.Intersect( rect );
2302 if (intersect_rect.width > 0)
2303 Refresh( true, &intersect_rect );
2304 }
2305
2306 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
2307 {
2308 if (from > to)
2309 {
2310 unsigned int tmp = to;
2311 to = from;
2312 from = tmp;
2313 }
2314
2315 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
2316 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2317
2318 wxSize client_size = GetClientSize();
2319 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2320 wxRect intersect_rect = client_rect.Intersect( rect );
2321 if (intersect_rect.width > 0)
2322 Refresh( true, &intersect_rect );
2323 }
2324
2325 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
2326 {
2327 unsigned int count = GetRowCount();
2328 if (firstRow > count)
2329 return;
2330
2331 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
2332 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2333
2334 wxSize client_size = GetClientSize();
2335 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2336 wxRect intersect_rect = client_rect.Intersect( rect );
2337 if (intersect_rect.width > 0)
2338 Refresh( true, &intersect_rect );
2339 }
2340
2341 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
2342 {
2343 wxCHECK_RET( newCurrent < GetRowCount(),
2344 _T("invalid item index in OnArrowChar()") );
2345
2346 // if there is no selection, we cannot move it anywhere
2347 if (!HasCurrentRow())
2348 return;
2349
2350 unsigned int oldCurrent = m_currentRow;
2351
2352 // in single selection we just ignore Shift as we can't select several
2353 // items anyhow
2354 if ( event.ShiftDown() && !IsSingleSel() )
2355 {
2356 RefreshRow( oldCurrent );
2357
2358 ChangeCurrentRow( newCurrent );
2359
2360 // select all the items between the old and the new one
2361 if ( oldCurrent > newCurrent )
2362 {
2363 newCurrent = oldCurrent;
2364 oldCurrent = m_currentRow;
2365 }
2366
2367 SelectRows( oldCurrent, newCurrent, true );
2368 }
2369 else // !shift
2370 {
2371 RefreshRow( oldCurrent );
2372
2373 // all previously selected items are unselected unless ctrl is held
2374 if ( !event.ControlDown() )
2375 SelectAllRows(false);
2376
2377 ChangeCurrentRow( newCurrent );
2378
2379 if ( !event.ControlDown() )
2380 SelectRow( m_currentRow, true );
2381 else
2382 RefreshRow( m_currentRow );
2383 }
2384
2385 //EnsureVisible( m_currentRow );
2386 }
2387
2388 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2389 {
2390 wxRect rect;
2391 rect.x = 0;
2392 rect.y = m_lineHeight * row;
2393 rect.width = GetEndOfLastCol();
2394 rect.height = m_lineHeight;
2395
2396 return rect;
2397 }
2398
2399 class RowToItemJob: public DoJob
2400 {
2401 public:
2402 RowToItemJob( unsigned int row , int current ) { this->row = row; this->current = current ;}
2403 virtual ~RowToItemJob(){};
2404
2405 virtual int operator() ( wxDataViewTreeNode * node )
2406 {
2407 if( current == static_cast<int>(row))
2408 {
2409 ret = node->GetItem() ;
2410 return DoJob::OK;
2411 }
2412 current ++;
2413 if ( node->IsOpen())
2414 return DoJob::CONT;
2415 else
2416 return DoJob::IGR;
2417 }
2418
2419 wxDataViewItem GetResult(){ return ret; }
2420 private:
2421 unsigned int row;
2422 int current ;
2423 wxDataViewItem ret;
2424 };
2425
2426 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row)
2427 {
2428 RowToItemJob job( row, 0 );
2429 Walker( m_root , job );
2430 return job.GetResult();
2431 }
2432
2433 class RowToTreeNodeJob: public DoJob
2434 {
2435 public:
2436 RowToTreeNodeJob( unsigned int row , int current ) { this->row = row; this->current = current ; ret = NULL ; }
2437 virtual ~RowToTreeNodeJob(){};
2438
2439 virtual int operator() ( wxDataViewTreeNode * node )
2440 {
2441 if( current == static_cast<int>(row))
2442 {
2443 ret = node ;
2444 return DoJob::OK;
2445 }
2446 current ++;
2447 if ( node->IsOpen())
2448 return DoJob::CONT;
2449 else
2450 return DoJob::IGR;
2451 }
2452
2453 wxDataViewTreeNode * GetResult(){ return ret; }
2454 private:
2455 unsigned int row;
2456 int current ;
2457 wxDataViewTreeNode * ret;
2458 };
2459
2460
2461 wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row)
2462 {
2463 RowToTreeNodeJob job( row , 0 );
2464 Walker( m_root , job );
2465 return job.GetResult();
2466 }
2467
2468 class CountJob : public DoJob
2469 {
2470 public:
2471 CountJob(){ count = 0 ; }
2472 virtual ~CountJob(){};
2473
2474 virtual int operator () ( wxDataViewTreeNode * node )
2475 {
2476 count ++;
2477 if ( node->IsOpen())
2478 return DoJob::CONT;
2479 else
2480 return DoJob::IGR;
2481 }
2482
2483 unsigned int GetResult()
2484 {
2485 return count ;
2486 }
2487 private:
2488 unsigned int count;
2489 };
2490
2491 void wxDataViewMainWindow::OnExpanding( unsigned int row )
2492 {
2493 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2494 if( node != NULL )
2495 {
2496 if( node->HasChildren())
2497 if( !node->IsOpen())
2498 {
2499 node->ToggleOpen();
2500 //Here I build the children of current node
2501 if( node->GetChildrenNumber() == 0 )
2502 BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
2503 m_count = -1;
2504 UpdateDisplay();
2505 }
2506 else
2507 {
2508 SelectRow( row, false );
2509 SelectRow( row + 1, true );
2510 ChangeCurrentRow( row + 1 );
2511 }
2512 }
2513 }
2514
2515 void wxDataViewMainWindow::OnCollapsing(unsigned int row)
2516 {
2517 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2518 if( node != NULL )
2519 {
2520 if( node->HasChildren() && node->IsOpen() )
2521 {
2522 node->ToggleOpen();
2523 m_count = -1;
2524 UpdateDisplay();
2525 //RefreshRows(row,GetLastVisibleRow());
2526 }
2527 else
2528 {
2529 node = node->GetParent();
2530 if( node != NULL )
2531 {
2532 int parent = GetRowByItem( node->GetItem()) ;
2533 SelectRow( row, false);
2534 SelectRow(parent , true );
2535 ChangeCurrentRow( parent );
2536 }
2537 }
2538 }
2539 }
2540
2541 wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
2542 {
2543 wxDataViewModel * model = GetOwner()->GetModel();
2544 if( model == NULL )
2545 return NULL;
2546
2547 //Compose the a parent-chain of the finding item
2548 ItemList list;
2549 list.DeleteContents( true );
2550 wxDataViewItem it( item );
2551 while( it.IsOk() )
2552 {
2553 wxDataViewItem * pItem = new wxDataViewItem( it );
2554 list.Insert( pItem );
2555 it = model->GetParent( it );
2556 }
2557
2558 //Find the item along the parent-chain.
2559 //This algorithm is designed to speed up the node-finding method
2560 bool found = true;
2561 wxDataViewTreeNode * node = m_root;
2562 for( ItemList::Node * n = list.GetFirst(); n; n = n->GetNext() )
2563 {
2564 if( node->HasChildren() )
2565 {
2566 if( node->GetChildrenNumber() == 0 )
2567 BuildTreeHelper(model, node->GetItem(), node);
2568
2569 int len = node->GetChildrenNumber();
2570 wxDataViewTreeNodes nodes = node->GetChildren();
2571 int j = 0;
2572 for( ; j < len; j ++)
2573 {
2574 if( nodes[j]->GetItem() == *(n->GetData()))
2575 {
2576 node = nodes[j];
2577 break;
2578 }
2579 }
2580 // Whenever we can't find the node in any level, return NULL to indicate the item can't be found
2581 if( j == len )
2582 {
2583 found = false;
2584 return NULL;
2585 }
2586 }
2587 else
2588 return NULL;
2589 }
2590 return node;
2591 }
2592
2593 int wxDataViewMainWindow::RecalculateCount()
2594 {
2595 CountJob job;
2596 Walker( m_root, job );
2597 return job.GetResult();
2598 }
2599
2600 class ItemToRowJob : public DoJob
2601 {
2602 public:
2603 ItemToRowJob(const wxDataViewItem & item){ this->item = item ; ret = 0 ; }
2604 virtual ~ItemToRowJob(){};
2605
2606 virtual int operator() ( wxDataViewTreeNode * node)
2607 {
2608 ret ++;
2609 if( node->GetItem() == item )
2610 return DoJob::OK;
2611
2612 if( node->IsOpen())
2613 return DoJob::CONT;
2614 else
2615 return DoJob::IGR;
2616 }
2617
2618 //the row number is begin from zero
2619 int GetResult(){ return ret -1 ; }
2620 private:
2621 wxDataViewItem item;
2622 int ret;
2623 };
2624
2625 unsigned int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item)
2626 {
2627 ItemToRowJob job( item );
2628 Walker(m_root , job );
2629 return job.GetResult();
2630 }
2631
2632 void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node)
2633 {
2634 if( !model->IsContainer( item ) )
2635 return ;
2636
2637 wxDataViewItem i = model->GetFirstChild( item );
2638 while( i.IsOk() )
2639 {
2640 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
2641 n->SetItem(i);
2642 n->SetHasChildren( model->IsContainer( i )) ;
2643 node->AppendChild(n);
2644 //BuildTreeHelper( model, i, n) ;
2645 i = model->GetNextSibling( i );
2646 }
2647 }
2648
2649 void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
2650 {
2651 //First we define a invalid item to fetch the top-level elements
2652 wxDataViewItem item;
2653 BuildTreeHelper( model, item, m_root);
2654 m_count = -1 ;
2655 }
2656
2657 void DestroyTreeHelper( wxDataViewTreeNode * node )
2658 {
2659 if( node->HasChildren() )
2660 {
2661 int len = node->GetChildrenNumber();
2662 int i = 0 ;
2663 wxDataViewTreeNodes nodes = node->GetChildren();
2664 for( ; i < len; i ++ )
2665 {
2666 DestroyTreeHelper(nodes[i]);
2667 }
2668 }
2669 delete node;
2670 }
2671
2672 void wxDataViewMainWindow::DestroyTree()
2673 {
2674 DestroyTreeHelper(m_root);
2675 m_count = 0 ;
2676 }
2677
2678 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
2679 {
2680 if (event.GetKeyCode() == WXK_TAB)
2681 {
2682 wxNavigationKeyEvent nevent;
2683 nevent.SetWindowChange( event.ControlDown() );
2684 nevent.SetDirection( !event.ShiftDown() );
2685 nevent.SetEventObject( GetParent()->GetParent() );
2686 nevent.SetCurrentFocus( m_parent );
2687 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
2688 return;
2689 }
2690
2691 // no item -> nothing to do
2692 if (!HasCurrentRow())
2693 {
2694 event.Skip();
2695 return;
2696 }
2697
2698 // don't use m_linesPerPage directly as it might not be computed yet
2699 const int pageSize = GetCountPerPage();
2700 wxCHECK_RET( pageSize, _T("should have non zero page size") );
2701
2702 switch ( event.GetKeyCode() )
2703 {
2704 case WXK_UP:
2705 if ( m_currentRow > 0 )
2706 OnArrowChar( m_currentRow - 1, event );
2707 break;
2708
2709 case WXK_DOWN:
2710 if ( m_currentRow < GetRowCount() - 1 )
2711 OnArrowChar( m_currentRow + 1, event );
2712 break;
2713 //Add the process for tree expanding/collapsing
2714 case WXK_LEFT:
2715 OnCollapsing(m_currentRow);
2716 break;
2717 case WXK_RIGHT:
2718 OnExpanding( m_currentRow);
2719 break;
2720 case WXK_END:
2721 if (!IsEmpty())
2722 OnArrowChar( GetRowCount() - 1, event );
2723 break;
2724
2725 case WXK_HOME:
2726 if (!IsEmpty())
2727 OnArrowChar( 0, event );
2728 break;
2729
2730 case WXK_PAGEUP:
2731 {
2732 int steps = pageSize - 1;
2733 int index = m_currentRow - steps;
2734 if (index < 0)
2735 index = 0;
2736
2737 OnArrowChar( index, event );
2738 }
2739 break;
2740
2741 case WXK_PAGEDOWN:
2742 {
2743 int steps = pageSize - 1;
2744 unsigned int index = m_currentRow + steps;
2745 unsigned int count = GetRowCount();
2746 if ( index >= count )
2747 index = count - 1;
2748
2749 OnArrowChar( index, event );
2750 }
2751 break;
2752
2753 default:
2754 event.Skip();
2755 }
2756 }
2757
2758 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
2759 {
2760 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2761 {
2762 // let the base handle mouse wheel events.
2763 event.Skip();
2764 return;
2765 }
2766
2767 int x = event.GetX();
2768 int y = event.GetY();
2769 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2770
2771 wxDataViewColumn *col = NULL;
2772
2773 int xpos = 0;
2774 unsigned int cols = GetOwner()->GetColumnCount();
2775 unsigned int i;
2776 for (i = 0; i < cols; i++)
2777 {
2778 wxDataViewColumn *c = GetOwner()->GetColumn( i );
2779 if (c->IsHidden())
2780 continue; // skip it!
2781
2782 if (x < xpos + c->GetWidth())
2783 {
2784 col = c;
2785 break;
2786 }
2787 xpos += c->GetWidth();
2788 }
2789 if (!col)
2790 return;
2791 wxDataViewRenderer *cell = col->GetRenderer();
2792
2793 unsigned int current = y / m_lineHeight;
2794
2795 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
2796 {
2797 // Unselect all if below the last row ?
2798 return;
2799 }
2800
2801 wxDataViewModel *model = GetOwner()->GetModel();
2802
2803 if (event.Dragging())
2804 {
2805 if (m_dragCount == 0)
2806 {
2807 // we have to report the raw, physical coords as we want to be
2808 // able to call HitTest(event.m_pointDrag) from the user code to
2809 // get the item being dragged
2810 m_dragStart = event.GetPosition();
2811 }
2812
2813 m_dragCount++;
2814
2815 if (m_dragCount != 3)
2816 return;
2817
2818 if (event.LeftIsDown())
2819 {
2820 // Notify cell about drag
2821 }
2822 return;
2823 }
2824 else
2825 {
2826 m_dragCount = 0;
2827 }
2828
2829 bool forceClick = false;
2830
2831 if (event.ButtonDClick())
2832 {
2833 m_renameTimer->Stop();
2834 m_lastOnSame = false;
2835 }
2836
2837 if (event.LeftDClick())
2838 {
2839 if ( current == m_lineLastClicked )
2840 {
2841 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
2842 {
2843 wxDataViewItem item = GetItemByRow(current);
2844 wxVariant value;
2845 model->GetValue( value, item, col->GetModelColumn() );
2846 cell->SetValue( value );
2847 wxRect cell_rect( xpos, current * m_lineHeight,
2848 col->GetWidth(), m_lineHeight );
2849 cell->Activate( cell_rect, model, item, col->GetModelColumn() );
2850 }
2851 return;
2852 }
2853 else
2854 {
2855 // The first click was on another item, so don't interpret this as
2856 // a double click, but as a simple click instead
2857 forceClick = true;
2858 }
2859 }
2860
2861 if (event.LeftUp())
2862 {
2863 if (m_lineSelectSingleOnUp != (unsigned int)-1)
2864 {
2865 // select single line
2866 SelectAllRows( false );
2867 SelectRow( m_lineSelectSingleOnUp, true );
2868 }
2869
2870 //Process the event of user clicking the expander
2871 bool expander = false;
2872 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
2873 if( node!=NULL && node->HasChildren() )
2874 {
2875 int indent = node->GetIndentLevel();
2876 indent = GetOwner()->GetIndent()*indent;
2877 wxRect rect( xpos + indent + EXPANDER_MARGIN, current * m_lineHeight + EXPANDER_MARGIN, m_lineHeight-2*EXPANDER_MARGIN,m_lineHeight-2*EXPANDER_MARGIN);
2878 if( rect.Contains( x, y) )
2879 {
2880 expander = true;
2881 if( node->IsOpen() )
2882 OnCollapsing(current);
2883 else
2884 OnExpanding( current );
2885 }
2886 }
2887
2888 //If the user click the expander, we do not do editing even if the column with expander are editable
2889 if (m_lastOnSame && !expander )
2890 {
2891 if ((col == m_currentCol) && (current == m_currentRow) &&
2892 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
2893 {
2894 m_renameTimer->Start( 100, true );
2895 }
2896 }
2897
2898 m_lastOnSame = false;
2899 m_lineSelectSingleOnUp = (unsigned int)-1;
2900 }
2901 else
2902 {
2903 // This is necessary, because after a DnD operation in
2904 // from and to ourself, the up event is swallowed by the
2905 // DnD code. So on next non-up event (which means here and
2906 // now) m_lineSelectSingleOnUp should be reset.
2907 m_lineSelectSingleOnUp = (unsigned int)-1;
2908 }
2909
2910 if (event.RightDown())
2911 {
2912 m_lineBeforeLastClicked = m_lineLastClicked;
2913 m_lineLastClicked = current;
2914
2915 // If the item is already selected, do not update the selection.
2916 // Multi-selections should not be cleared if a selected item is clicked.
2917 if (!IsRowSelected(current))
2918 {
2919 SelectAllRows(false);
2920 ChangeCurrentRow(current);
2921 SelectRow(m_currentRow,true);
2922 }
2923
2924 // notify cell about right click
2925 // cell->...
2926
2927 // Allow generation of context menu event
2928 event.Skip();
2929 }
2930 else if (event.MiddleDown())
2931 {
2932 // notify cell about middle click
2933 // cell->...
2934 }
2935 if (event.LeftDown() || forceClick)
2936 {
2937 #ifdef __WXMSW__
2938 SetFocus();
2939 #endif
2940
2941 m_lineBeforeLastClicked = m_lineLastClicked;
2942 m_lineLastClicked = current;
2943
2944 unsigned int oldCurrentRow = m_currentRow;
2945 bool oldWasSelected = IsRowSelected(m_currentRow);
2946
2947 bool cmdModifierDown = event.CmdDown();
2948 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
2949 {
2950 if ( IsSingleSel() || !IsRowSelected(current) )
2951 {
2952 SelectAllRows( false );
2953
2954 ChangeCurrentRow(current);
2955
2956 SelectRow(m_currentRow,true);
2957 }
2958 else // multi sel & current is highlighted & no mod keys
2959 {
2960 m_lineSelectSingleOnUp = current;
2961 ChangeCurrentRow(current); // change focus
2962 }
2963 }
2964 else // multi sel & either ctrl or shift is down
2965 {
2966 if (cmdModifierDown)
2967 {
2968 ChangeCurrentRow(current);
2969
2970 ReverseRowSelection(m_currentRow);
2971 }
2972 else if (event.ShiftDown())
2973 {
2974 ChangeCurrentRow(current);
2975
2976 unsigned int lineFrom = oldCurrentRow,
2977 lineTo = current;
2978
2979 if ( lineTo < lineFrom )
2980 {
2981 lineTo = lineFrom;
2982 lineFrom = m_currentRow;
2983 }
2984
2985 SelectRows(lineFrom, lineTo, true);
2986 }
2987 else // !ctrl, !shift
2988 {
2989 // test in the enclosing if should make it impossible
2990 wxFAIL_MSG( _T("how did we get here?") );
2991 }
2992 }
2993
2994 if (m_currentRow != oldCurrentRow)
2995 RefreshRow( oldCurrentRow );
2996
2997 wxDataViewColumn *oldCurrentCol = m_currentCol;
2998
2999 // Update selection here...
3000 m_currentCol = col;
3001
3002 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
3003 (current == oldCurrentRow)) && oldWasSelected;
3004 }
3005 }
3006
3007 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
3008 {
3009 m_hasFocus = true;
3010
3011 if (HasCurrentRow())
3012 Refresh();
3013
3014 event.Skip();
3015 }
3016
3017 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
3018 {
3019 m_hasFocus = false;
3020
3021 if (HasCurrentRow())
3022 Refresh();
3023
3024 event.Skip();
3025 }
3026
3027 wxDataViewItem wxDataViewMainWindow::GetSelection()
3028 {
3029 if( m_selection.GetCount() != 1 )
3030 return wxDataViewItem();
3031 return GetItemByRow( m_selection.Item( 0 ) );
3032 }
3033
3034 //-----------------------------------------------------------------------------
3035 // wxDataViewCtrl
3036 //-----------------------------------------------------------------------------
3037
3038 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
3039
3040 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
3041 EVT_SIZE(wxDataViewCtrl::OnSize)
3042 END_EVENT_TABLE()
3043
3044 wxDataViewCtrl::~wxDataViewCtrl()
3045 {
3046 if (m_notifier)
3047 GetModel()->RemoveNotifier( m_notifier );
3048 }
3049
3050 void wxDataViewCtrl::Init()
3051 {
3052 m_notifier = NULL;
3053 }
3054
3055 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
3056 const wxPoint& pos, const wxSize& size,
3057 long style, const wxValidator& validator )
3058 {
3059 if (!wxControl::Create( parent, id, pos, size,
3060 style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
3061 return false;
3062
3063 Init();
3064
3065 #ifdef __WXMAC__
3066 MacSetClipChildren( true ) ;
3067 #endif
3068
3069 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
3070
3071 if (HasFlag(wxDV_NO_HEADER))
3072 m_headerArea = NULL;
3073 else
3074 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY );
3075
3076 SetTargetWindow( m_clientArea );
3077
3078 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
3079 if (m_headerArea)
3080 sizer->Add( m_headerArea, 0, wxGROW );
3081 sizer->Add( m_clientArea, 1, wxGROW );
3082 SetSizer( sizer );
3083
3084 return true;
3085 }
3086
3087 #ifdef __WXMSW__
3088 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
3089 WXWPARAM wParam,
3090 WXLPARAM lParam)
3091 {
3092 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
3093
3094 #ifndef __WXWINCE__
3095 // we need to process arrows ourselves for scrolling
3096 if ( nMsg == WM_GETDLGCODE )
3097 {
3098 rc |= DLGC_WANTARROWS;
3099 }
3100 #endif
3101
3102 return rc;
3103 }
3104 #endif
3105
3106 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
3107 {
3108 // We need to override OnSize so that our scrolled
3109 // window a) does call Layout() to use sizers for
3110 // positioning the controls but b) does not query
3111 // the sizer for their size and use that for setting
3112 // the scrollable area as set that ourselves by
3113 // calling SetScrollbar() further down.
3114
3115 Layout();
3116
3117 AdjustScrollbars();
3118 }
3119
3120 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
3121 {
3122 if (!wxDataViewCtrlBase::AssociateModel( model ))
3123 return false;
3124
3125 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
3126
3127 model->AddNotifier( m_notifier );
3128
3129 m_clientArea->BuildTree(model);
3130
3131 m_clientArea->UpdateDisplay();
3132
3133 return true;
3134 }
3135
3136 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
3137 {
3138 if (!wxDataViewCtrlBase::AppendColumn(col))
3139 return false;
3140
3141 OnColumnChange();
3142 return true;
3143 }
3144
3145 void wxDataViewCtrl::OnColumnChange()
3146 {
3147 if (m_headerArea)
3148 m_headerArea->UpdateDisplay();
3149
3150 m_clientArea->UpdateDisplay();
3151 }
3152
3153 void wxDataViewCtrl::DoSetExpanderColumn()
3154 {
3155 m_clientArea->UpdateDisplay();
3156 }
3157
3158 void wxDataViewCtrl::DoSetIndent()
3159 {
3160 m_clientArea->UpdateDisplay();
3161 }
3162
3163 wxDataViewItem wxDataViewCtrl::GetSelection()
3164 {
3165 return m_clientArea->GetSelection();
3166 }
3167
3168 /********************************************************************
3169 void wxDataViewCtrl::SetSelection( int row )
3170 {
3171 m_clientArea->SelectRow(row, true);
3172 }
3173
3174 void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to )
3175 {
3176 m_clientArea->SelectRows(from, to, true);
3177 }
3178
3179 void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections )
3180 {
3181 m_clientArea->Select(aSelections);
3182 }
3183
3184 void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) )
3185 {
3186 // FIXME - TODO
3187 }
3188
3189 bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row) ) const
3190 {
3191 // FIXME - TODO
3192
3193 return false;
3194 }
3195
3196 int wxDataViewCtrl::GetSelection() const
3197 {
3198 // FIXME - TODO
3199
3200 return -1;
3201 }
3202
3203 int wxDataViewCtrl::GetSelections(wxArrayInt& WXUNUSED(aSelections) ) const
3204 {
3205 // FIXME - TODO
3206
3207 return 0;
3208 }
3209 *********************************************************************/
3210 #endif
3211 // !wxUSE_GENERICDATAVIEWCTRL
3212
3213 #endif
3214 // wxUSE_DATAVIEWCTRL