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