]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
Patch from Bo for generic wxDataViewCtrl
[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 m_currentCol->GetRenderer()->StartEditing( m_currentRow, labelRect );
1697 }
1698
1699 //------------------------------------------------------------------
1700 // Helper class for do operation on the tree node
1701 //------------------------------------------------------------------
1702 class DoJob
1703 {
1704 public:
1705 DoJob(){};
1706 virtual ~DoJob(){};
1707
1708 //The return value control how the tree-walker tranverse the tree
1709 // 0: Job done, stop tranverse and return
1710 // 1: Ignore the current node's subtree and continue
1711 // 2: Job not done, continue
1712 enum { OK = 0 , IGR = 1, CONT = 2 };
1713 virtual int operator() ( wxDataViewTreeNode * node ) = 0 ;
1714 };
1715
1716 class ItemAddJob: public DoJob
1717 {
1718 public:
1719 ItemAddJob( const wxDataViewItem & parent, const wxDataViewItem & item, int * count )
1720 { this->parent = parent ; this->item = item ; m_count = count; }
1721 virtual ~ItemAddJob(){};
1722
1723 virtual int operator() ( wxDataViewTreeNode * node )
1724 {
1725 if( node->GetItem() == parent )
1726 {
1727 node->SetHasChildren( true );
1728 wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node );
1729 newnode->SetItem(item);
1730 node->AppendChild( newnode);
1731 *m_count = -1;
1732 return OK;
1733 }
1734 return CONT;
1735 }
1736
1737 private:
1738 int * m_count;
1739 wxDataViewItem parent, item;
1740 };
1741
1742 bool Walker( wxDataViewTreeNode * node, DoJob & func )
1743 {
1744 if( node==NULL || !node->HasChildren())
1745 return false;
1746
1747 wxDataViewTreeNodes nodes = node->GetChildren();
1748 int len = node->GetChildrenNumber();
1749 int i = 0 ;
1750 for( ; i < len ; i ++ )
1751 {
1752 wxDataViewTreeNode * n = nodes[i];
1753 switch( func( n ) )
1754 {
1755 case DoJob::OK :
1756 return true ;
1757 case DoJob::IGR:
1758 continue;
1759 case DoJob::CONT:
1760 default:
1761 ;
1762 }
1763
1764 if( Walker( n , func ) )
1765 return true;
1766 }
1767 return false;
1768 }
1769
1770 bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
1771 {
1772 ItemAddJob job( parent, item, &m_count);
1773 Walker( m_root , job);
1774 UpdateDisplay();
1775 return true;
1776 }
1777
1778 class ItemDeleteJob: public DoJob
1779 {
1780 public:
1781 ItemDeleteJob( const wxDataViewItem & item, int * count ) { m_item = item; m_count = count; }
1782 virtual ~ItemDeleteJob(){}
1783 virtual int operator() ( wxDataViewTreeNode * node )
1784 {
1785 if( node->GetItem() == m_item )
1786 {
1787 node->GetParent()->GetChildren().Remove( node );
1788 delete node;
1789 *m_count = -1;
1790 return DoJob::OK;
1791 }
1792 return DoJob::CONT;
1793 }
1794
1795 private:
1796 int * m_count;
1797 wxDataViewItem m_item;
1798 };
1799
1800 bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem & item)
1801 {
1802 ItemDeleteJob job( item, &m_count);
1803 Walker( m_root, job);
1804 UpdateDisplay();
1805 return true;
1806 }
1807
1808 bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
1809 {
1810 unsigned int row = GetRowByItem(item);
1811 RefreshRow( row );
1812 return true;
1813 }
1814
1815 bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int WXUNUSED(col) )
1816 {
1817 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
1818 /*#define MAX_VIRTUAL_WIDTH 100000
1819
1820 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
1821 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1822 Refresh( true, &rect );
1823
1824 return true;
1825 */
1826 unsigned int row = GetRowByItem(item);
1827 RefreshRow( row );
1828 return true;
1829 }
1830
1831 bool wxDataViewMainWindow::Cleared()
1832 {
1833 DestroyTree();
1834 UpdateDisplay();
1835 return true;
1836 }
1837
1838 void wxDataViewMainWindow::UpdateDisplay()
1839 {
1840 m_dirty = true;
1841 }
1842
1843 void wxDataViewMainWindow::OnInternalIdle()
1844 {
1845 wxWindow::OnInternalIdle();
1846
1847 if (m_dirty)
1848 {
1849 RecalculateDisplay();
1850 m_dirty = false;
1851 }
1852 }
1853
1854 void wxDataViewMainWindow::RecalculateDisplay()
1855 {
1856 wxDataViewModel *model = GetOwner()->GetModel();
1857 if (!model)
1858 {
1859 Refresh();
1860 return;
1861 }
1862
1863 int width = GetEndOfLastCol();
1864 int height = GetRowCount() * m_lineHeight;
1865
1866 SetVirtualSize( width, height );
1867 GetOwner()->SetScrollRate( 10, m_lineHeight );
1868
1869 Refresh();
1870 }
1871
1872 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1873 {
1874 wxWindow::ScrollWindow( dx, dy, rect );
1875
1876 if (GetOwner()->m_headerArea)
1877 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1878 }
1879
1880 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1881 {
1882 wxDataViewModel *model = GetOwner()->GetModel();
1883 wxAutoBufferedPaintDC dc( this );
1884
1885 // prepare the DC
1886 dc.SetBackground(GetBackgroundColour());
1887 dc.Clear();
1888 GetOwner()->PrepareDC( dc );
1889 dc.SetFont( GetFont() );
1890
1891 wxRect update = GetUpdateRegion().GetBox();
1892 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1893
1894 // compute which items needs to be redrawn
1895 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
1896 unsigned int item_count =
1897 wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1898 (int)(GetRowCount( )- item_start) );
1899 unsigned int item_last = item_start + item_count;
1900
1901 // compute which columns needs to be redrawn
1902 unsigned int cols = GetOwner()->GetColumnCount();
1903 unsigned int col_start = 0;
1904 unsigned int x_start = 0;
1905 for (x_start = 0; col_start < cols; col_start++)
1906 {
1907 wxDataViewColumn *col = GetOwner()->GetColumn(col_start);
1908 if (col->IsHidden())
1909 continue; // skip it!
1910
1911 unsigned int w = col->GetWidth();
1912 if (x_start+w >= (unsigned int)update.x)
1913 break;
1914
1915 x_start += w;
1916 }
1917
1918 unsigned int col_last = col_start;
1919 unsigned int x_last = x_start;
1920 for (; col_last < cols; col_last++)
1921 {
1922 wxDataViewColumn *col = GetOwner()->GetColumn(col_last);
1923 if (col->IsHidden())
1924 continue; // skip it!
1925
1926 if (x_last > (unsigned int)update.GetRight())
1927 break;
1928
1929 x_last += col->GetWidth();
1930 }
1931
1932 // Draw horizontal rules if required
1933 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1934 {
1935 dc.SetPen(m_penRule);
1936 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1937
1938 for (unsigned int i = item_start; i <= item_last+1; i++)
1939 {
1940 int y = i * m_lineHeight;
1941 dc.DrawLine(x_start, y, x_last, y);
1942 }
1943 }
1944
1945 // Draw vertical rules if required
1946 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1947 {
1948 dc.SetPen(m_penRule);
1949 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1950
1951 int x = x_start;
1952 for (unsigned int i = col_start; i < col_last; i++)
1953 {
1954 wxDataViewColumn *col = GetOwner()->GetColumn(i);
1955 if (col->IsHidden())
1956 continue; // skip it
1957
1958 dc.DrawLine(x, item_start * m_lineHeight,
1959 x, item_last * m_lineHeight);
1960
1961 x += col->GetWidth();
1962 }
1963
1964 // Draw last vertical rule
1965 dc.DrawLine(x, item_start * m_lineHeight,
1966 x, item_last * m_lineHeight);
1967 }
1968
1969 // redraw the background for the items which are selected/current
1970 for (unsigned int item = item_start; item < item_last; item++)
1971 {
1972 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1973 if (selected || item == m_currentRow)
1974 {
1975 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
1976 if (item == m_currentRow)
1977 flags |= wxCONTROL_CURRENT;
1978 if (m_hasFocus)
1979 flags |= wxCONTROL_FOCUSED;
1980
1981 wxRect rect( x_start, item*m_lineHeight, x_last, m_lineHeight );
1982 wxRendererNative::Get().DrawItemSelectionRect
1983 (
1984 this,
1985 dc,
1986 rect,
1987 flags
1988 );
1989 }
1990 }
1991
1992 // redraw all cells for all rows which must be repainted and for all columns
1993 wxRect cell_rect;
1994 cell_rect.x = x_start;
1995 cell_rect.height = m_lineHeight; // -1 is for the horizontal rules
1996 for (unsigned int i = col_start; i < col_last; i++)
1997 {
1998 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1999 wxDataViewRenderer *cell = col->GetRenderer();
2000 cell_rect.width = col->GetWidth();
2001
2002 if (col->IsHidden())
2003 continue; // skipt it!
2004
2005 for (unsigned int item = item_start; item < item_last; item++)
2006 {
2007 // get the cell value and set it into the renderer
2008 wxVariant value;
2009 wxDataViewTreeNode * node = GetTreeNodeByRow(item);
2010 if( node == NULL )
2011 continue;
2012
2013 wxDataViewItem dataitem = node->GetItem();
2014 model->GetValue( value, dataitem, col->GetModelColumn());
2015 cell->SetValue( value );
2016
2017 // update the y offset
2018 cell_rect.y = item * m_lineHeight;
2019
2020 //Draw the expander here. Please notice that I use const number for all pixel data. When the final API are determined
2021 //I will change this to the data member of the class wxDataViewCtrl
2022 int indent = node->GetIndentLevel();
2023 if( col->GetModelColumn() == GetOwner()->GetExpanderColumn() )
2024 {
2025 //Calculate the indent first
2026 indent = cell_rect.x + GetOwner()->GetIndent() * indent;
2027
2028 int expander_width = m_lineHeight - 2*EXPANDER_MARGIN;
2029 // change the cell_rect.x to the appropriate pos
2030 int expander_x = indent + EXPANDER_MARGIN , expander_y = cell_rect.y + EXPANDER_MARGIN ;
2031 indent = indent + m_lineHeight ; //try to use the m_lineHeight as the expander space
2032 dc.SetPen( m_penExpander );
2033 dc.SetBrush( wxNullBrush );
2034 if( node->HasChildren() )
2035 {
2036 //dc.DrawRoundedRectangle( expander_x,expander_y,expander_width,expander_width, 1.0);
2037 //dc.DrawLine( expander_x + 2 , expander_y + expander_width/2, expander_x + expander_width - 2, expander_y + expander_width/2 );
2038 wxRect rect( expander_x , expander_y, expander_width, expander_width);
2039 if( node->IsOpen() )
2040 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, wxCONTROL_EXPANDED );
2041 else
2042 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect );
2043 }
2044 else
2045 {
2046 // I am wandering whether we should draw dot lines between tree nodes
2047 }
2048
2049 //force the expander column to left-center align
2050 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
2051 }
2052
2053
2054 // cannot be bigger than allocated space
2055 wxSize size = cell->GetSize();
2056 // Because of the tree structure indent, here we should minus the width of the cell for drawing
2057 size.x = wxMin( size.x + 2*PADDING_RIGHTLEFT, cell_rect.width - indent );
2058 size.y = wxMin( size.y + 2*PADDING_TOPBOTTOM, cell_rect.height );
2059
2060 wxRect item_rect(cell_rect.GetTopLeft(), size);
2061 int align = cell->GetAlignment();
2062
2063 // horizontal alignment:
2064 item_rect.x = cell_rect.x;
2065 if (align & wxALIGN_CENTER_HORIZONTAL)
2066 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
2067 else if (align & wxALIGN_RIGHT)
2068 item_rect.x = cell_rect.x + cell_rect.width - size.x;
2069 //else: wxALIGN_LEFT is the default
2070
2071 // vertical alignment:
2072 item_rect.y = cell_rect.y;
2073 if (align & wxALIGN_CENTER_VERTICAL)
2074 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
2075 else if (align & wxALIGN_BOTTOM)
2076 item_rect.y = cell_rect.y + cell_rect.height - size.y;
2077 //else: wxALIGN_TOP is the default
2078
2079 // add padding
2080 item_rect.x += PADDING_RIGHTLEFT;
2081 item_rect.y += PADDING_TOPBOTTOM;
2082 item_rect.width = size.x - 2 * PADDING_RIGHTLEFT;
2083 item_rect.height = size.y - 2 * PADDING_TOPBOTTOM;
2084
2085 //Here we add the tree indent
2086 item_rect.x += indent;
2087
2088 int state = 0;
2089 if (m_selection.Index(item) != wxNOT_FOUND)
2090 state |= wxDATAVIEW_CELL_SELECTED;
2091
2092 // TODO: it would be much more efficient to create a clipping
2093 // region for the entire column being rendered (in the OnPaint
2094 // of wxDataViewMainWindow) instead of a single clip region for
2095 // each cell. However it would mean that each renderer should
2096 // respect the given wxRect's top & bottom coords, eventually
2097 // violating only the left & right coords - however the user can
2098 // make its own renderer and thus we cannot be sure of that.
2099 dc.SetClippingRegion( item_rect );
2100 cell->Render( item_rect, &dc, state );
2101 dc.DestroyClippingRegion();
2102 }
2103
2104 cell_rect.x += cell_rect.width;
2105 }
2106 }
2107
2108 int wxDataViewMainWindow::GetCountPerPage() const
2109 {
2110 wxSize size = GetClientSize();
2111 return size.y / m_lineHeight;
2112 }
2113
2114 int wxDataViewMainWindow::GetEndOfLastCol() const
2115 {
2116 int width = 0;
2117 unsigned int i;
2118 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
2119 {
2120 const wxDataViewColumn *c =
2121 wx_const_cast(wxDataViewCtrl*, GetOwner())->GetColumn( i );
2122
2123 if (!c->IsHidden())
2124 width += c->GetWidth();
2125 }
2126 return width;
2127 }
2128
2129 unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
2130 {
2131 int x = 0;
2132 int y = 0;
2133 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2134
2135 return y / m_lineHeight;
2136 }
2137
2138 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
2139 {
2140 wxSize client_size = GetClientSize();
2141 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
2142 &client_size.x, &client_size.y );
2143
2144 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
2145 }
2146
2147 unsigned int wxDataViewMainWindow::GetRowCount()
2148 {
2149 if ( m_count == -1 )
2150 {
2151 m_count = RecalculateCount();
2152 int width, height;
2153 GetVirtualSize( &width, &height );
2154 height = m_count * m_lineHeight;
2155
2156 SetVirtualSize( width, height );
2157 }
2158 return m_count;
2159 }
2160
2161 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
2162 {
2163 m_currentRow = row;
2164
2165 // send event
2166 }
2167
2168 void wxDataViewMainWindow::SelectAllRows( bool on )
2169 {
2170 if (IsEmpty())
2171 return;
2172
2173 if (on)
2174 {
2175 m_selection.Clear();
2176 for (unsigned int i = 0; i < GetRowCount(); i++)
2177 m_selection.Add( i );
2178 Refresh();
2179 }
2180 else
2181 {
2182 unsigned int first_visible = GetFirstVisibleRow();
2183 unsigned int last_visible = GetLastVisibleRow();
2184 unsigned int i;
2185 for (i = 0; i < m_selection.GetCount(); i++)
2186 {
2187 unsigned int row = m_selection[i];
2188 if ((row >= first_visible) && (row <= last_visible))
2189 RefreshRow( row );
2190 }
2191 m_selection.Clear();
2192 }
2193 }
2194
2195 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
2196 {
2197 if (m_selection.Index( row ) == wxNOT_FOUND)
2198 {
2199 if (on)
2200 {
2201 m_selection.Add( row );
2202 RefreshRow( row );
2203 }
2204 }
2205 else
2206 {
2207 if (!on)
2208 {
2209 m_selection.Remove( row );
2210 RefreshRow( row );
2211 }
2212 }
2213 }
2214
2215 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
2216 {
2217 if (from > to)
2218 {
2219 unsigned int tmp = from;
2220 from = to;
2221 to = tmp;
2222 }
2223
2224 unsigned int i;
2225 for (i = from; i <= to; i++)
2226 {
2227 if (m_selection.Index( i ) == wxNOT_FOUND)
2228 {
2229 if (on)
2230 m_selection.Add( i );
2231 }
2232 else
2233 {
2234 if (!on)
2235 m_selection.Remove( i );
2236 }
2237 }
2238 RefreshRows( from, to );
2239 }
2240
2241 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2242 {
2243 for (size_t i=0; i < aSelections.GetCount(); i++)
2244 {
2245 int n = aSelections[i];
2246
2247 m_selection.Add( n );
2248 RefreshRow( n );
2249 }
2250 }
2251
2252 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
2253 {
2254 if (m_selection.Index( row ) == wxNOT_FOUND)
2255 m_selection.Add( row );
2256 else
2257 m_selection.Remove( row );
2258 RefreshRow( row );
2259 }
2260
2261 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
2262 {
2263 return (m_selection.Index( row ) != wxNOT_FOUND);
2264 }
2265
2266 void wxDataViewMainWindow::RefreshRow( unsigned int row )
2267 {
2268 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
2269 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2270
2271 wxSize client_size = GetClientSize();
2272 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2273 wxRect intersect_rect = client_rect.Intersect( rect );
2274 if (intersect_rect.width > 0)
2275 Refresh( true, &intersect_rect );
2276 }
2277
2278 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
2279 {
2280 if (from > to)
2281 {
2282 unsigned int tmp = to;
2283 to = from;
2284 from = tmp;
2285 }
2286
2287 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
2288 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2289
2290 wxSize client_size = GetClientSize();
2291 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2292 wxRect intersect_rect = client_rect.Intersect( rect );
2293 if (intersect_rect.width > 0)
2294 Refresh( true, &intersect_rect );
2295 }
2296
2297 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
2298 {
2299 unsigned int count = GetRowCount();
2300 if (firstRow > count)
2301 return;
2302
2303 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
2304 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2305
2306 wxSize client_size = GetClientSize();
2307 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2308 wxRect intersect_rect = client_rect.Intersect( rect );
2309 if (intersect_rect.width > 0)
2310 Refresh( true, &intersect_rect );
2311 }
2312
2313 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
2314 {
2315 wxCHECK_RET( newCurrent < GetRowCount(),
2316 _T("invalid item index in OnArrowChar()") );
2317
2318 // if there is no selection, we cannot move it anywhere
2319 if (!HasCurrentRow())
2320 return;
2321
2322 unsigned int oldCurrent = m_currentRow;
2323
2324 // in single selection we just ignore Shift as we can't select several
2325 // items anyhow
2326 if ( event.ShiftDown() && !IsSingleSel() )
2327 {
2328 RefreshRow( oldCurrent );
2329
2330 ChangeCurrentRow( newCurrent );
2331
2332 // select all the items between the old and the new one
2333 if ( oldCurrent > newCurrent )
2334 {
2335 newCurrent = oldCurrent;
2336 oldCurrent = m_currentRow;
2337 }
2338
2339 SelectRows( oldCurrent, newCurrent, true );
2340 }
2341 else // !shift
2342 {
2343 RefreshRow( oldCurrent );
2344
2345 // all previously selected items are unselected unless ctrl is held
2346 if ( !event.ControlDown() )
2347 SelectAllRows(false);
2348
2349 ChangeCurrentRow( newCurrent );
2350
2351 if ( !event.ControlDown() )
2352 SelectRow( m_currentRow, true );
2353 else
2354 RefreshRow( m_currentRow );
2355 }
2356
2357 //EnsureVisible( m_currentRow );
2358 }
2359
2360 wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2361 {
2362 wxRect rect;
2363 rect.x = 0;
2364 rect.y = m_lineHeight * row;
2365 rect.width = GetEndOfLastCol();
2366 rect.height = m_lineHeight;
2367
2368 return rect;
2369 }
2370
2371 class RowToItemJob: public DoJob
2372 {
2373 public:
2374 RowToItemJob( unsigned int row , int current ) { this->row = row; this->current = current ;}
2375 virtual ~RowToItemJob(){};
2376
2377 virtual int operator() ( wxDataViewTreeNode * node )
2378 {
2379 if( current == static_cast<int>(row))
2380 {
2381 ret = node->GetItem() ;
2382 return DoJob::OK;
2383 }
2384 current ++;
2385 if ( node->IsOpen())
2386 return DoJob::CONT;
2387 else
2388 return DoJob::IGR;
2389 }
2390
2391 wxDataViewItem GetResult(){ return ret; }
2392 private:
2393 unsigned int row;
2394 int current ;
2395 wxDataViewItem ret;
2396 };
2397
2398 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row)
2399 {
2400 RowToItemJob job( row, 0 );
2401 Walker( m_root , job );
2402 return job.GetResult();
2403 }
2404
2405 class RowToTreeNodeJob: public DoJob
2406 {
2407 public:
2408 RowToTreeNodeJob( unsigned int row , int current ) { this->row = row; this->current = current ; ret = NULL ; }
2409 virtual ~RowToTreeNodeJob(){};
2410
2411 virtual int operator() ( wxDataViewTreeNode * node )
2412 {
2413 if( current == static_cast<int>(row))
2414 {
2415 ret = node ;
2416 return DoJob::OK;
2417 }
2418 current ++;
2419 if ( node->IsOpen())
2420 return DoJob::CONT;
2421 else
2422 return DoJob::IGR;
2423 }
2424
2425 wxDataViewTreeNode * GetResult(){ return ret; }
2426 private:
2427 unsigned int row;
2428 int current ;
2429 wxDataViewTreeNode * ret;
2430 };
2431
2432
2433 wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row)
2434 {
2435 RowToTreeNodeJob job( row , 0 );
2436 Walker( m_root , job );
2437 return job.GetResult();
2438 }
2439
2440 class CountJob : public DoJob
2441 {
2442 public:
2443 CountJob(){ count = 0 ; }
2444 virtual ~CountJob(){};
2445
2446 virtual int operator () ( wxDataViewTreeNode * node )
2447 {
2448 count ++;
2449 if ( node->IsOpen())
2450 return DoJob::CONT;
2451 else
2452 return DoJob::IGR;
2453 }
2454
2455 unsigned int GetResult()
2456 {
2457 return count ;
2458 }
2459 private:
2460 unsigned int count;
2461 };
2462
2463 void wxDataViewMainWindow::OnExpanding( unsigned int row )
2464 {
2465 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2466 if( node != NULL )
2467 {
2468 if( node->HasChildren())
2469 if( !node->IsOpen())
2470 {
2471 node->ToggleOpen();
2472 //Here I build the children of current node
2473 if( node->GetChildrenNumber() == 0 )
2474 BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
2475 m_count = -1;
2476 Refresh();
2477 }
2478 }
2479 }
2480
2481 void wxDataViewMainWindow::OnCollapsing(unsigned int row)
2482 {
2483 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2484 if( node != NULL )
2485 {
2486 if( node->HasChildren() && node->IsOpen() )
2487 {
2488 node->ToggleOpen();
2489 m_count = -1;
2490 Refresh();
2491 //RefreshRows(row,GetLastVisibleRow());
2492 }
2493 else
2494 {
2495 node = node->GetParent();
2496 if( node != NULL )
2497 {
2498 int parent = GetRowByItem( node->GetItem()) ;
2499 SelectRow( row, false);
2500 SelectRow(parent , true );
2501 ChangeCurrentRow( parent );
2502 }
2503 }
2504 }
2505 }
2506
2507 int wxDataViewMainWindow::RecalculateCount()
2508 {
2509 CountJob job;
2510 Walker( m_root, job );
2511 return job.GetResult();
2512 }
2513
2514 class ItemToRowJob : public DoJob
2515 {
2516 public:
2517 ItemToRowJob(const wxDataViewItem & item){ this->item = item ; ret = 0 ; }
2518 virtual ~ItemToRowJob(){};
2519
2520 virtual int operator() ( wxDataViewTreeNode * node)
2521 {
2522 ret ++;
2523 if( node->GetItem() == item )
2524 return DoJob::OK;
2525
2526 if( node->IsOpen())
2527 return DoJob::CONT;
2528 else
2529 return DoJob::IGR;
2530 }
2531
2532 //the row number is begin from zero
2533 int GetResult(){ return ret -1 ; }
2534 private:
2535 wxDataViewItem item;
2536 int ret;
2537 };
2538
2539 unsigned int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item)
2540 {
2541 ItemToRowJob job( item );
2542 Walker(m_root , job );
2543 return job.GetResult();
2544 }
2545
2546 void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node)
2547 {
2548 if( !model->HasChildren( item ) )
2549 return ;
2550
2551 wxDataViewItem i = model->GetFirstChild( item );
2552 while( i.IsOk() )
2553 {
2554 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
2555 n->SetItem(i);
2556 n->SetHasChildren( model->HasChildren( i )) ;
2557 node->AppendChild(n);
2558 //BuildTreeHelper( model, i, n) ;
2559 i = model->GetNextSibling( i );
2560 }
2561 }
2562
2563 void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
2564 {
2565 //First we define a invalid item to fetch the top-level elements
2566 wxDataViewItem item;
2567 BuildTreeHelper( model, item, m_root);
2568 m_count = -1 ;
2569 }
2570
2571 void DestroyTreeHelper( wxDataViewTreeNode * node )
2572 {
2573 if( node->HasChildren() )
2574 {
2575 int len = node->GetChildrenNumber();
2576 int i = 0 ;
2577 wxDataViewTreeNodes nodes = node->GetChildren();
2578 for( ; i < len; i ++ )
2579 {
2580 DestroyTreeHelper(nodes[i]);
2581 }
2582 }
2583 delete node;
2584 }
2585
2586 void wxDataViewMainWindow::DestroyTree()
2587 {
2588 DestroyTreeHelper(m_root);
2589 m_count = 0 ;
2590 }
2591
2592 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
2593 {
2594 if (event.GetKeyCode() == WXK_TAB)
2595 {
2596 wxNavigationKeyEvent nevent;
2597 nevent.SetWindowChange( event.ControlDown() );
2598 nevent.SetDirection( !event.ShiftDown() );
2599 nevent.SetEventObject( GetParent()->GetParent() );
2600 nevent.SetCurrentFocus( m_parent );
2601 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
2602 return;
2603 }
2604
2605 // no item -> nothing to do
2606 if (!HasCurrentRow())
2607 {
2608 event.Skip();
2609 return;
2610 }
2611
2612 // don't use m_linesPerPage directly as it might not be computed yet
2613 const int pageSize = GetCountPerPage();
2614 wxCHECK_RET( pageSize, _T("should have non zero page size") );
2615
2616 switch ( event.GetKeyCode() )
2617 {
2618 case WXK_UP:
2619 if ( m_currentRow > 0 )
2620 OnArrowChar( m_currentRow - 1, event );
2621 break;
2622
2623 case WXK_DOWN:
2624 if ( m_currentRow < GetRowCount() - 1 )
2625 OnArrowChar( m_currentRow + 1, event );
2626 break;
2627 //Add the process for tree expanding/collapsing
2628 case WXK_LEFT:
2629 OnCollapsing(m_currentRow);
2630 break;
2631 case WXK_RIGHT:
2632 OnExpanding( m_currentRow);
2633 break;
2634 case WXK_END:
2635 if (!IsEmpty())
2636 OnArrowChar( GetRowCount() - 1, event );
2637 break;
2638
2639 case WXK_HOME:
2640 if (!IsEmpty())
2641 OnArrowChar( 0, event );
2642 break;
2643
2644 case WXK_PAGEUP:
2645 {
2646 int steps = pageSize - 1;
2647 int index = m_currentRow - steps;
2648 if (index < 0)
2649 index = 0;
2650
2651 OnArrowChar( index, event );
2652 }
2653 break;
2654
2655 case WXK_PAGEDOWN:
2656 {
2657 int steps = pageSize - 1;
2658 unsigned int index = m_currentRow + steps;
2659 unsigned int count = GetRowCount();
2660 if ( index >= count )
2661 index = count - 1;
2662
2663 OnArrowChar( index, event );
2664 }
2665 break;
2666
2667 default:
2668 event.Skip();
2669 }
2670 }
2671
2672 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
2673 {
2674 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2675 {
2676 // let the base handle mouse wheel events.
2677 event.Skip();
2678 return;
2679 }
2680
2681 int x = event.GetX();
2682 int y = event.GetY();
2683 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2684
2685 wxDataViewColumn *col = NULL;
2686
2687 int xpos = 0;
2688 unsigned int cols = GetOwner()->GetColumnCount();
2689 unsigned int i;
2690 for (i = 0; i < cols; i++)
2691 {
2692 wxDataViewColumn *c = GetOwner()->GetColumn( i );
2693 if (c->IsHidden())
2694 continue; // skip it!
2695
2696 if (x < xpos + c->GetWidth())
2697 {
2698 col = c;
2699 break;
2700 }
2701 xpos += c->GetWidth();
2702 }
2703 if (!col)
2704 return;
2705 wxDataViewRenderer *cell = col->GetRenderer();
2706
2707 unsigned int current = y / m_lineHeight;
2708
2709 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
2710 {
2711 // Unselect all if below the last row ?
2712 return;
2713 }
2714
2715 wxDataViewModel *model = GetOwner()->GetModel();
2716
2717 if (event.Dragging())
2718 {
2719 if (m_dragCount == 0)
2720 {
2721 // we have to report the raw, physical coords as we want to be
2722 // able to call HitTest(event.m_pointDrag) from the user code to
2723 // get the item being dragged
2724 m_dragStart = event.GetPosition();
2725 }
2726
2727 m_dragCount++;
2728
2729 if (m_dragCount != 3)
2730 return;
2731
2732 if (event.LeftIsDown())
2733 {
2734 // Notify cell about drag
2735 }
2736 return;
2737 }
2738 else
2739 {
2740 m_dragCount = 0;
2741 }
2742
2743 bool forceClick = false;
2744
2745 if (event.ButtonDClick())
2746 {
2747 m_renameTimer->Stop();
2748 m_lastOnSame = false;
2749 }
2750
2751 if (event.LeftDClick())
2752 {
2753 if ( current == m_lineLastClicked )
2754 {
2755 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
2756 {
2757 wxVariant value;
2758 model->GetValue( value, col->GetModelColumn(), current );
2759 cell->SetValue( value );
2760 wxRect cell_rect( xpos, current * m_lineHeight,
2761 col->GetWidth(), m_lineHeight );
2762 wxDataViewItem dataitem = GetItemByRow(current);
2763 cell->Activate( cell_rect, model, dataitem, col->GetModelColumn() );
2764 }
2765 return;
2766 }
2767 else
2768 {
2769 // The first click was on another item, so don't interpret this as
2770 // a double click, but as a simple click instead
2771 forceClick = true;
2772 }
2773 }
2774
2775 if (event.LeftUp())
2776 {
2777 if (m_lineSelectSingleOnUp != (unsigned int)-1)
2778 {
2779 // select single line
2780 SelectAllRows( false );
2781 SelectRow( m_lineSelectSingleOnUp, true );
2782 }
2783
2784 //Process the event of user clicking the expander
2785 bool expander = false;
2786 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
2787 if( node!=NULL && node->HasChildren() )
2788 {
2789 int indent = node->GetIndentLevel();
2790 indent = GetOwner()->GetIndent()*indent;
2791 wxRect rect( xpos + indent + EXPANDER_MARGIN, current * m_lineHeight + EXPANDER_MARGIN, m_lineHeight-2*EXPANDER_MARGIN,m_lineHeight-2*EXPANDER_MARGIN);
2792 if( rect.Contains( x, y) )
2793 {
2794 expander = true;
2795 if( node->IsOpen() )
2796 OnCollapsing(current);
2797 else
2798 OnExpanding( current );
2799 }
2800 }
2801
2802 //If the user click the expander, we do not do editing even if the column with expander are editable
2803 if (m_lastOnSame && !expander )
2804 {
2805 if ((col == m_currentCol) && (current == m_currentRow) &&
2806 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
2807 {
2808 m_renameTimer->Start( 100, true );
2809 }
2810 }
2811
2812 m_lastOnSame = false;
2813 m_lineSelectSingleOnUp = (unsigned int)-1;
2814 }
2815 else
2816 {
2817 // This is necessary, because after a DnD operation in
2818 // from and to ourself, the up event is swallowed by the
2819 // DnD code. So on next non-up event (which means here and
2820 // now) m_lineSelectSingleOnUp should be reset.
2821 m_lineSelectSingleOnUp = (unsigned int)-1;
2822 }
2823
2824 if (event.RightDown())
2825 {
2826 m_lineBeforeLastClicked = m_lineLastClicked;
2827 m_lineLastClicked = current;
2828
2829 // If the item is already selected, do not update the selection.
2830 // Multi-selections should not be cleared if a selected item is clicked.
2831 if (!IsRowSelected(current))
2832 {
2833 SelectAllRows(false);
2834 ChangeCurrentRow(current);
2835 SelectRow(m_currentRow,true);
2836 }
2837
2838 // notify cell about right click
2839 // cell->...
2840
2841 // Allow generation of context menu event
2842 event.Skip();
2843 }
2844 else if (event.MiddleDown())
2845 {
2846 // notify cell about middle click
2847 // cell->...
2848 }
2849 if (event.LeftDown() || forceClick)
2850 {
2851 #ifdef __WXMSW__
2852 SetFocus();
2853 #endif
2854
2855 m_lineBeforeLastClicked = m_lineLastClicked;
2856 m_lineLastClicked = current;
2857
2858 unsigned int oldCurrentRow = m_currentRow;
2859 bool oldWasSelected = IsRowSelected(m_currentRow);
2860
2861 bool cmdModifierDown = event.CmdDown();
2862 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
2863 {
2864 if ( IsSingleSel() || !IsRowSelected(current) )
2865 {
2866 SelectAllRows( false );
2867
2868 ChangeCurrentRow(current);
2869
2870 SelectRow(m_currentRow,true);
2871 }
2872 else // multi sel & current is highlighted & no mod keys
2873 {
2874 m_lineSelectSingleOnUp = current;
2875 ChangeCurrentRow(current); // change focus
2876 }
2877 }
2878 else // multi sel & either ctrl or shift is down
2879 {
2880 if (cmdModifierDown)
2881 {
2882 ChangeCurrentRow(current);
2883
2884 ReverseRowSelection(m_currentRow);
2885 }
2886 else if (event.ShiftDown())
2887 {
2888 ChangeCurrentRow(current);
2889
2890 unsigned int lineFrom = oldCurrentRow,
2891 lineTo = current;
2892
2893 if ( lineTo < lineFrom )
2894 {
2895 lineTo = lineFrom;
2896 lineFrom = m_currentRow;
2897 }
2898
2899 SelectRows(lineFrom, lineTo, true);
2900 }
2901 else // !ctrl, !shift
2902 {
2903 // test in the enclosing if should make it impossible
2904 wxFAIL_MSG( _T("how did we get here?") );
2905 }
2906 }
2907
2908 if (m_currentRow != oldCurrentRow)
2909 RefreshRow( oldCurrentRow );
2910
2911 wxDataViewColumn *oldCurrentCol = m_currentCol;
2912
2913 // Update selection here...
2914 m_currentCol = col;
2915
2916 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
2917 (current == oldCurrentRow)) && oldWasSelected;
2918 }
2919 }
2920
2921 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
2922 {
2923 m_hasFocus = true;
2924
2925 if (HasCurrentRow())
2926 Refresh();
2927
2928 event.Skip();
2929 }
2930
2931 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
2932 {
2933 m_hasFocus = false;
2934
2935 if (HasCurrentRow())
2936 Refresh();
2937
2938 event.Skip();
2939 }
2940
2941 wxDataViewItem wxDataViewMainWindow::GetSelection()
2942 {
2943 if( m_selection.GetCount() != 1 )
2944 return wxDataViewItem();
2945 return GetItemByRow( m_selection.Item( 0 ) );
2946 }
2947
2948 //-----------------------------------------------------------------------------
2949 // wxDataViewCtrl
2950 //-----------------------------------------------------------------------------
2951
2952 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
2953
2954 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
2955 EVT_SIZE(wxDataViewCtrl::OnSize)
2956 END_EVENT_TABLE()
2957
2958 wxDataViewCtrl::~wxDataViewCtrl()
2959 {
2960 if (m_notifier)
2961 GetModel()->RemoveNotifier( m_notifier );
2962 }
2963
2964 void wxDataViewCtrl::Init()
2965 {
2966 m_notifier = NULL;
2967 }
2968
2969 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
2970 const wxPoint& pos, const wxSize& size,
2971 long style, const wxValidator& validator )
2972 {
2973 if (!wxControl::Create( parent, id, pos, size,
2974 style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
2975 return false;
2976
2977 Init();
2978
2979 #ifdef __WXMAC__
2980 MacSetClipChildren( true ) ;
2981 #endif
2982
2983 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
2984
2985 if (HasFlag(wxDV_NO_HEADER))
2986 m_headerArea = NULL;
2987 else
2988 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY );
2989
2990 SetTargetWindow( m_clientArea );
2991
2992 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
2993 if (m_headerArea)
2994 sizer->Add( m_headerArea, 0, wxGROW );
2995 sizer->Add( m_clientArea, 1, wxGROW );
2996 SetSizer( sizer );
2997
2998 return true;
2999 }
3000
3001 #ifdef __WXMSW__
3002 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
3003 WXWPARAM wParam,
3004 WXLPARAM lParam)
3005 {
3006 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
3007
3008 #ifndef __WXWINCE__
3009 // we need to process arrows ourselves for scrolling
3010 if ( nMsg == WM_GETDLGCODE )
3011 {
3012 rc |= DLGC_WANTARROWS;
3013 }
3014 #endif
3015
3016 return rc;
3017 }
3018 #endif
3019
3020 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
3021 {
3022 // We need to override OnSize so that our scrolled
3023 // window a) does call Layout() to use sizers for
3024 // positioning the controls but b) does not query
3025 // the sizer for their size and use that for setting
3026 // the scrollable area as set that ourselves by
3027 // calling SetScrollbar() further down.
3028
3029 Layout();
3030
3031 AdjustScrollbars();
3032 }
3033
3034 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
3035 {
3036 if (!wxDataViewCtrlBase::AssociateModel( model ))
3037 return false;
3038
3039 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
3040
3041 model->AddNotifier( m_notifier );
3042
3043 m_clientArea->BuildTree(model);
3044
3045 m_clientArea->UpdateDisplay();
3046
3047 return true;
3048 }
3049
3050 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
3051 {
3052 if (!wxDataViewCtrlBase::AppendColumn(col))
3053 return false;
3054
3055 OnColumnChange();
3056 return true;
3057 }
3058
3059 void wxDataViewCtrl::OnColumnChange()
3060 {
3061 if (m_headerArea)
3062 m_headerArea->UpdateDisplay();
3063
3064 m_clientArea->UpdateDisplay();
3065 }
3066
3067 void wxDataViewCtrl::DoSetExpanderColumn()
3068 {
3069 m_clientArea->UpdateDisplay();
3070 }
3071
3072 void wxDataViewCtrl::DoSetIndent()
3073 {
3074 m_clientArea->UpdateDisplay();
3075 }
3076
3077 wxDataViewItem wxDataViewCtrl::GetSelection()
3078 {
3079 return m_clientArea->GetSelection();
3080 }
3081
3082 /********************************************************************
3083 void wxDataViewCtrl::SetSelection( int row )
3084 {
3085 m_clientArea->SelectRow(row, true);
3086 }
3087
3088 void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to )
3089 {
3090 m_clientArea->SelectRows(from, to, true);
3091 }
3092
3093 void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections )
3094 {
3095 m_clientArea->Select(aSelections);
3096 }
3097
3098 void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) )
3099 {
3100 // FIXME - TODO
3101 }
3102
3103 bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row) ) const
3104 {
3105 // FIXME - TODO
3106
3107 return false;
3108 }
3109
3110 int wxDataViewCtrl::GetSelection() const
3111 {
3112 // FIXME - TODO
3113
3114 return -1;
3115 }
3116
3117 int wxDataViewCtrl::GetSelections(wxArrayInt& WXUNUSED(aSelections) ) const
3118 {
3119 // FIXME - TODO
3120
3121 return 0;
3122 }
3123 *********************************************************************/
3124 #endif
3125 // !wxUSE_GENERICDATAVIEWCTRL
3126
3127 #endif
3128 // wxUSE_DATAVIEWCTRL