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