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