Most of FM's wxDataViewCtrl patch.
[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 #endif
37
38 #include "wx/stockitem.h"
39 #include "wx/calctrl.h"
40 #include "wx/popupwin.h"
41 #include "wx/renderer.h"
42 #include "wx/dcbuffer.h"
43 #include "wx/icon.h"
44
45 //-----------------------------------------------------------------------------
46 // classes
47 //-----------------------------------------------------------------------------
48
49 class wxDataViewCtrl;
50
51 //-----------------------------------------------------------------------------
52 // wxDataViewHeaderWindow
53 //-----------------------------------------------------------------------------
54
55 #define USE_NATIVE_HEADER_WINDOW 1
56
57 class wxDataViewHeaderWindowBase : public wxControl
58 {
59 public:
60 wxDataViewHeaderWindowBase()
61 { m_owner = NULL; }
62
63 bool Create(wxDataViewCtrl *parent, wxWindowID id,
64 const wxPoint &pos, const wxSize &size,
65 const wxString &name)
66 {
67 return wxWindow::Create(parent, id, pos, size, wxNO_BORDER, name);
68 }
69
70 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
71 wxDataViewCtrl *GetOwner() { return m_owner; }
72
73 // called on column addition/removal
74 virtual void UpdateDisplay() { /* by default, do nothing */ }
75
76 // updates the n-th column's width
77 virtual void SetColumnWidth(unsigned int n, int width);
78
79 // returns the n-th column
80 wxDataViewColumn *GetColumn(unsigned int n)
81 {
82 wxASSERT(m_owner);
83 wxDataViewColumn *ret = m_owner->GetColumn(n);
84 wxASSERT(ret);
85
86 return ret;
87 }
88
89 protected:
90 wxDataViewCtrl *m_owner;
91
92 // sends an event generated from the n-th wxDataViewColumn
93 void SendEvent(wxEventType type, unsigned int n);
94 };
95
96 // on wxMSW the header window (only that part however) can be made native!
97 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
98
99 #define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow
100
101 class wxDataViewHeaderWindowMSW : public wxDataViewHeaderWindowBase
102 {
103 public:
104
105 wxDataViewHeaderWindowMSW( wxDataViewCtrl *parent,
106 wxWindowID id,
107 const wxPoint &pos = wxDefaultPosition,
108 const wxSize &size = wxDefaultSize,
109 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
110 {
111 Create(parent, id, pos, size, name);
112 }
113
114 bool Create(wxDataViewCtrl *parent, wxWindowID id,
115 const wxPoint &pos, const wxSize &size,
116 const wxString &name);
117
118 ~wxDataViewHeaderWindowMSW();
119
120 // called on column addition/removal
121 virtual void UpdateDisplay();
122
123 // called when the main window gets scrolled
124 virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL);
125
126 protected:
127 virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result);
128
129 private:
130 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW)
131 };
132
133 #else // !defined(__WXMSW__)
134
135 #define HEADER_WINDOW_HEIGHT 25
136 #define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow
137
138 class wxGenericDataViewHeaderWindow : public wxDataViewHeaderWindowBase
139 {
140 public:
141 wxGenericDataViewHeaderWindow( wxDataViewCtrl *parent,
142 wxWindowID id,
143 const wxPoint &pos = wxDefaultPosition,
144 const wxSize &size = wxDefaultSize,
145 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
146 {
147 Init();
148 Create(parent, id, pos, size, name);
149 }
150
151 bool Create(wxDataViewCtrl *parent, wxWindowID id,
152 const wxPoint &pos, const wxSize &size,
153 const wxString &name);
154
155 ~wxGenericDataViewHeaderWindow()
156 {
157 delete m_resizeCursor;
158 }
159
160 // event handlers:
161
162 void OnPaint( wxPaintEvent &event );
163 void OnMouse( wxMouseEvent &event );
164 void OnSetFocus( wxFocusEvent &event );
165
166
167 protected:
168
169 // vars used for column resizing:
170
171 wxCursor *m_resizeCursor;
172 const wxCursor *m_currentCursor;
173 bool m_isDragging;
174
175 bool m_dirty; // needs refresh?
176 int m_column; // index of the column being resized
177 int m_currentX; // divider line position in logical (unscrolled) coords
178 int m_minX; // minimal position beyond which the divider line
179 // can't be dragged in logical coords
180
181 // internal utilities:
182
183 void Init()
184 {
185 m_currentCursor = (wxCursor *) NULL;
186 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
187
188 m_isDragging = false;
189 m_dirty = false;
190
191 m_column = wxNOT_FOUND;
192 m_currentX = 0;
193 m_minX = 0;
194 }
195
196 void DrawCurrent();
197 void AdjustDC(wxDC& dc);
198
199 private:
200 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow)
201 DECLARE_EVENT_TABLE()
202 };
203
204 #endif // defined(__WXMSW__)
205
206 //-----------------------------------------------------------------------------
207 // wxDataViewRenameTimer
208 //-----------------------------------------------------------------------------
209
210 class wxDataViewRenameTimer: public wxTimer
211 {
212 private:
213 wxDataViewMainWindow *m_owner;
214
215 public:
216 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
217 void Notify();
218 };
219
220 //-----------------------------------------------------------------------------
221 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
222 //-----------------------------------------------------------------------------
223
224 class wxDataViewTextCtrlWrapper : public wxEvtHandler
225 {
226 public:
227 // NB: text must be a valid object but not Create()d yet
228 wxDataViewTextCtrlWrapper( wxDataViewMainWindow *owner,
229 wxTextCtrl *text,
230 wxDataViewListModel *model,
231 unsigned int col, unsigned int row,
232 wxRect cellLabel );
233
234 wxTextCtrl *GetText() const { return m_text; }
235
236 void AcceptChangesAndFinish();
237
238 protected:
239 void OnChar( wxKeyEvent &event );
240 void OnKeyUp( wxKeyEvent &event );
241 void OnKillFocus( wxFocusEvent &event );
242
243 bool AcceptChanges();
244 void Finish();
245
246 private:
247 wxDataViewMainWindow *m_owner;
248 wxTextCtrl *m_text;
249 wxString m_startValue;
250 wxDataViewListModel *m_model;
251 unsigned int m_col;
252 unsigned int m_row;
253 bool m_finished;
254 bool m_aboutToFinish;
255
256 DECLARE_EVENT_TABLE()
257 };
258
259 //-----------------------------------------------------------------------------
260 // wxDataViewMainWindow
261 //-----------------------------------------------------------------------------
262
263 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
264 WXDLLIMPEXP_ADV);
265
266 class wxDataViewMainWindow: public wxWindow
267 {
268 public:
269 wxDataViewMainWindow( wxDataViewCtrl *parent,
270 wxWindowID id,
271 const wxPoint &pos = wxDefaultPosition,
272 const wxSize &size = wxDefaultSize,
273 const wxString &name = wxT("wxdataviewctrlmainwindow") );
274 virtual ~wxDataViewMainWindow();
275
276 // notifications from wxDataViewListModel
277 bool RowAppended();
278 bool RowPrepended();
279 bool RowInserted( unsigned int before );
280 bool RowDeleted( unsigned int row );
281 bool RowChanged( unsigned int row );
282 bool ValueChanged( unsigned int col, unsigned int row );
283 bool RowsReordered( unsigned int *new_order );
284 bool Cleared();
285
286 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
287 wxDataViewCtrl *GetOwner() { return m_owner; }
288
289 void OnPaint( wxPaintEvent &event );
290 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
291 void OnChar( wxKeyEvent &event );
292 void OnMouse( wxMouseEvent &event );
293 void OnSetFocus( wxFocusEvent &event );
294 void OnKillFocus( wxFocusEvent &event );
295
296 void UpdateDisplay();
297 void RecalculateDisplay();
298 void OnInternalIdle();
299
300 void OnRenameTimer();
301 void FinishEditing( wxTextCtrl *text );
302
303 void ScrollWindow( int dx, int dy, const wxRect *rect );
304
305 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
306 void ChangeCurrentRow( unsigned int row );
307
308 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); };
309 bool IsEmpty() { return GetRowCount() == 0; }
310
311 int GetCountPerPage();
312 int GetEndOfLastCol();
313 unsigned int GetFirstVisibleRow();
314 unsigned int GetLastVisibleRow();
315 unsigned int GetRowCount();
316
317 void Select( const wxArrayInt& aSelections );
318 void SelectAllRows( bool on );
319 void SelectRow( unsigned int row, bool on );
320 void SelectRows( unsigned int from, unsigned int to, bool on );
321 void ReverseRowSelection( unsigned int row );
322 bool IsRowSelected( unsigned int row );
323
324 void RefreshRow( unsigned int row );
325 void RefreshRows( unsigned int from, unsigned int to );
326 void RefreshRowsAfter( unsigned int firstRow );
327
328 private:
329 wxDataViewCtrl *m_owner;
330 int m_lineHeight;
331 bool m_dirty;
332
333 wxDataViewColumn *m_currentCol;
334 unsigned int m_currentRow;
335 wxDataViewSelection m_selection;
336
337 wxDataViewRenameTimer *m_renameTimer;
338 wxDataViewTextCtrlWrapper *m_textctrlWrapper;
339 bool m_lastOnSame;
340
341 bool m_hasFocus;
342
343 int m_dragCount;
344 wxPoint m_dragStart;
345
346 // for double click logic
347 unsigned int m_lineLastClicked,
348 m_lineBeforeLastClicked,
349 m_lineSelectSingleOnUp;
350
351 private:
352 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
353 DECLARE_EVENT_TABLE()
354 };
355
356 // ---------------------------------------------------------
357 // wxGenericDataViewListModelNotifier
358 // ---------------------------------------------------------
359
360 class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
361 {
362 public:
363 wxGenericDataViewListModelNotifier( wxDataViewMainWindow *mainWindow )
364 { m_mainWindow = mainWindow; }
365
366 virtual bool RowAppended()
367 { return m_mainWindow->RowAppended(); }
368 virtual bool RowPrepended()
369 { return m_mainWindow->RowPrepended(); }
370 virtual bool RowInserted( unsigned int before )
371 { return m_mainWindow->RowInserted( before ); }
372 virtual bool RowDeleted( unsigned int row )
373 { return m_mainWindow->RowDeleted( row ); }
374 virtual bool RowChanged( unsigned int row )
375 { return m_mainWindow->RowChanged( row ); }
376 virtual bool ValueChanged( unsigned int col, unsigned int row )
377 { return m_mainWindow->ValueChanged( col, row ); }
378 virtual bool RowsReordered( unsigned int *new_order )
379 { return m_mainWindow->RowsReordered( new_order ); }
380 virtual bool Cleared()
381 { return m_mainWindow->Cleared(); }
382
383 wxDataViewMainWindow *m_mainWindow;
384 };
385
386 // ---------------------------------------------------------
387 // wxDataViewRenderer
388 // ---------------------------------------------------------
389
390 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
391
392 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
393 wxDataViewCellMode mode ) :
394 wxDataViewRendererBase( varianttype, mode )
395 {
396 m_dc = NULL;
397 }
398
399 wxDataViewRenderer::~wxDataViewRenderer()
400 {
401 if (m_dc)
402 delete m_dc;
403 }
404
405 wxDC *wxDataViewRenderer::GetDC()
406 {
407 if (m_dc == NULL)
408 {
409 if (GetOwner() == NULL)
410 return NULL;
411 if (GetOwner()->GetOwner() == NULL)
412 return NULL;
413 m_dc = new wxClientDC( GetOwner()->GetOwner() );
414 }
415
416 return m_dc;
417 }
418
419 // ---------------------------------------------------------
420 // wxDataViewCustomRenderer
421 // ---------------------------------------------------------
422
423 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
424
425 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
426 wxDataViewCellMode mode ) :
427 wxDataViewRenderer( varianttype, mode )
428 {
429 }
430
431 // ---------------------------------------------------------
432 // wxDataViewTextRenderer
433 // ---------------------------------------------------------
434
435 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
436
437 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
438 wxDataViewCellMode mode ) :
439 wxDataViewCustomRenderer( varianttype, mode )
440 {
441 }
442
443 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
444 {
445 m_text = value.GetString();
446
447 return true;
448 }
449
450 bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) )
451 {
452 return false;
453 }
454
455 bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state )
456 {
457 wxDataViewCtrl *view = GetOwner()->GetOwner();
458 wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ?
459 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) :
460 view->GetForegroundColour();
461
462 dc->SetTextForeground(col);
463
464 // TODO: it would be much more efficient to create a clipping
465 // region for the entire column being rendered (in the OnPaint
466 // of wxDataViewMainWindow) instead of a single clip region for
467 // each cell. However it would mean that each renderer should
468 // respect the given wxRect's top & bottom coords, eventually
469 // violating only the left & right coords - however the user can
470 // make its own renderer and thus we cannot be sure of that.
471 dc->SetClippingRegion(cell);
472 dc->DrawText( m_text, cell.x, cell.y );
473 dc->DestroyClippingRegion();
474
475 return true;
476 }
477
478 wxSize wxDataViewTextRenderer::GetSize()
479 {
480 wxDataViewCtrl *view = GetOwner()->GetOwner();
481 if (!m_text.empty())
482 {
483 int x,y;
484 view->GetTextExtent( m_text, &x, &y );
485 return wxSize( x, y );
486 }
487 return wxSize(80,20);
488 }
489
490 // ---------------------------------------------------------
491 // wxDataViewBitmapRenderer
492 // ---------------------------------------------------------
493
494 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
495
496 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
497 wxDataViewCellMode mode ) :
498 wxDataViewCustomRenderer( varianttype, mode )
499 {
500 }
501
502 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
503 {
504 if (value.GetType() == wxT("wxBitmap"))
505 m_bitmap << value;
506 if (value.GetType() == wxT("wxIcon"))
507 m_icon << value;
508
509 return true;
510 }
511
512 bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) )
513 {
514 return false;
515 }
516
517 bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
518 {
519 if (m_bitmap.Ok())
520 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
521 else if (m_icon.Ok())
522 dc->DrawIcon( m_icon, cell.x, cell.y );
523
524 return true;
525 }
526
527 wxSize wxDataViewBitmapRenderer::GetSize()
528 {
529 if (m_bitmap.Ok())
530 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
531 else if (m_icon.Ok())
532 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
533
534 return wxSize(16,16);
535 }
536
537 // ---------------------------------------------------------
538 // wxDataViewToggleRenderer
539 // ---------------------------------------------------------
540
541 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
542
543 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
544 wxDataViewCellMode mode ) :
545 wxDataViewCustomRenderer( varianttype, mode )
546 {
547 m_toggle = false;
548 }
549
550 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
551 {
552 m_toggle = value.GetBool();
553
554 return true;
555 }
556
557 bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) )
558 {
559 return false;
560 }
561
562 bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
563 {
564 // User wxRenderer here
565
566 wxRect rect;
567 rect.x = cell.x + cell.width/2 - 10;
568 rect.width = 20;
569 rect.y = cell.y + cell.height/2 - 10;
570 rect.height = 20;
571
572 int flags = 0;
573 if (m_toggle)
574 flags |= wxCONTROL_CHECKED;
575 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
576 flags |= wxCONTROL_DISABLED;
577
578 wxRendererNative::Get().DrawCheckBox(
579 GetOwner()->GetOwner(),
580 *dc,
581 rect,
582 flags );
583
584 return true;
585 }
586
587 bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
588 wxDataViewListModel *model,
589 unsigned int col, unsigned int row )
590 {
591 bool value = !m_toggle;
592 wxVariant variant = value;
593 model->SetValue( variant, col, row );
594 model->ValueChanged( col, row );
595 return true;
596 }
597
598 wxSize wxDataViewToggleRenderer::GetSize()
599 {
600 return wxSize(20,20);
601 }
602
603 // ---------------------------------------------------------
604 // wxDataViewProgressRenderer
605 // ---------------------------------------------------------
606
607 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
608
609 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
610 const wxString &varianttype, wxDataViewCellMode mode ) :
611 wxDataViewCustomRenderer( varianttype, mode )
612 {
613 m_label = label;
614 m_value = 0;
615 }
616
617 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
618 {
619 }
620
621 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
622 {
623 m_value = (long) value;
624
625 if (m_value < 0) m_value = 0;
626 if (m_value > 100) m_value = 100;
627
628 return true;
629 }
630
631 bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
632 {
633 double pct = (double)m_value / 100.0;
634 wxRect bar = cell;
635 bar.width = (int)(cell.width * pct);
636 dc->SetPen( *wxTRANSPARENT_PEN );
637 dc->SetBrush( *wxBLUE_BRUSH );
638 dc->DrawRectangle( bar );
639
640 dc->SetBrush( *wxTRANSPARENT_BRUSH );
641 dc->SetPen( *wxBLACK_PEN );
642 dc->DrawRectangle( cell );
643
644 return true;
645 }
646
647 wxSize wxDataViewProgressRenderer::GetSize()
648 {
649 return wxSize(40,12);
650 }
651
652 // ---------------------------------------------------------
653 // wxDataViewDateRenderer
654 // ---------------------------------------------------------
655
656 #define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
657
658 #if wxUSE_DATE_RENDERER_POPUP
659
660 class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
661 {
662 public:
663 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
664 wxDataViewListModel *model, unsigned int col, unsigned int row ) :
665 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
666 {
667 m_model = model;
668 m_col = col;
669 m_row = row;
670 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
671 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
672 sizer->Add( m_cal, 1, wxGROW );
673 SetSizer( sizer );
674 sizer->Fit( this );
675 }
676
677 void OnCalendar( wxCalendarEvent &event );
678
679 wxCalendarCtrl *m_cal;
680 wxDataViewListModel *m_model;
681 unsigned int m_col;
682 unsigned int m_row;
683
684 protected:
685 virtual void OnDismiss()
686 {
687 }
688
689 private:
690 DECLARE_EVENT_TABLE()
691 };
692
693 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
694 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
695 END_EVENT_TABLE()
696
697 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
698 {
699 wxDateTime date = event.GetDate();
700 wxVariant value = date;
701 m_model->SetValue( value, m_col, m_row );
702 m_model->ValueChanged( m_col, m_row );
703 DismissAndNotify();
704 }
705
706 #endif // wxUSE_DATE_RENDERER_POPUP
707
708 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
709
710 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
711 wxDataViewCellMode mode ) :
712 wxDataViewCustomRenderer( varianttype, mode )
713 {
714 }
715
716 bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
717 {
718 m_date = value.GetDateTime();
719
720 return true;
721 }
722
723 bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
724 {
725 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
726 wxString tmp = m_date.FormatDate();
727 dc->DrawText( tmp, cell.x, cell.y );
728
729 return true;
730 }
731
732 wxSize wxDataViewDateRenderer::GetSize()
733 {
734 wxDataViewCtrl* view = GetOwner()->GetOwner();
735 wxString tmp = m_date.FormatDate();
736 wxCoord x,y,d;
737 view->GetTextExtent( tmp, &x, &y, &d );
738 return wxSize(x,y+d);
739 }
740
741 bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model,
742 unsigned int col, unsigned int row )
743 {
744 wxVariant variant;
745 model->GetValue( variant, col, row );
746 wxDateTime value = variant.GetDateTime();
747
748 #if wxUSE_DATE_RENDERER_POPUP
749 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
750 GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
751 wxPoint pos = wxGetMousePosition();
752 popup->Move( pos );
753 popup->Layout();
754 popup->Popup( popup->m_cal );
755 #else // !wxUSE_DATE_RENDERER_POPUP
756 wxMessageBox(value.Format());
757 #endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
758 return true;
759 }
760
761 // ---------------------------------------------------------
762 // wxDataViewColumn
763 // ---------------------------------------------------------
764
765 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
766
767 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
768 unsigned int model_column,
769 int width, wxAlignment align, int flags ) :
770 wxDataViewColumnBase( title, cell, model_column, width, align, flags )
771 {
772 m_align = align;
773 Init(width < 0 ? 80 : width);
774 }
775
776 wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
777 unsigned int model_column,
778 int width, wxAlignment align, int flags ) :
779 wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags )
780 {
781 m_align = align;
782 Init(width < 0 ? 30 : width);
783 }
784
785 void wxDataViewColumn::Init(int width)
786 {
787 m_width = width;
788 m_fixedWidth = -1;
789 }
790
791 void wxDataViewColumn::SetSortable( bool WXUNUSED(sortable) )
792 {
793 // TODO
794 }
795
796 void wxDataViewColumn::SetSortOrder( bool WXUNUSED(ascending) )
797 {
798 // TODO
799 }
800
801 bool wxDataViewColumn::IsSortOrderAscending() const
802 {
803 // TODO
804 return true;
805 }
806
807
808 wxDataViewColumn::~wxDataViewColumn()
809 {
810 }
811
812 void wxDataViewColumn::SetTitle( const wxString &title )
813 {
814 wxDataViewColumnBase::SetTitle( title );
815
816 }
817
818 void wxDataViewColumn::SetBitmap( const wxBitmap &bitmap )
819 {
820 wxDataViewColumnBase::SetBitmap( bitmap );
821
822 }
823
824 int wxDataViewColumn::GetWidth() const
825 {
826 return m_width;
827 }
828
829 //-----------------------------------------------------------------------------
830 // wxDataViewHeaderWindowBase
831 //-----------------------------------------------------------------------------
832
833 void wxDataViewHeaderWindowBase::SetColumnWidth(unsigned int n, int width)
834 {
835 GetColumn(n)->m_width = width;
836 m_owner->m_clientArea->RecalculateDisplay();
837 }
838
839 void wxDataViewHeaderWindowBase::SendEvent(wxEventType type, unsigned int n)
840 {
841 wxWindow *parent = GetParent();
842 wxDataViewEvent le(type, parent->GetId());
843
844 le.SetEventObject(parent);
845 le.SetColumn(n);
846 le.SetDataViewColumn(GetColumn(n));
847 le.SetModel(GetOwner()->GetModel());
848
849 // for events created by wxDataViewHeaderWindow the
850 // row / value fields are not valid
851
852 parent->GetEventHandler()->ProcessEvent(le);
853 }
854
855 #if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
856
857 // implemented in msw/window.cpp:
858 void wxAssociateWinWithHandle(HWND hWnd, wxWindowMSW *win);
859 void wxRemoveHandleAssociation(wxWindowMSW *win);
860
861 // implemented in msw/listctrl.cpp:
862 unsigned int wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
863
864 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow);
865
866 bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl *parent, wxWindowID id,
867 const wxPoint &pos, const wxSize &size,
868 const wxString &name )
869 {
870 m_owner = parent;
871
872 if ( !CreateControl(parent, id, pos, size, 0, wxDefaultValidator, name) )
873 return false;
874
875 int x = pos.x == wxDefaultCoord ? 0 : pos.x,
876 y = pos.y == wxDefaultCoord ? 0 : pos.y,
877 w = size.x == wxDefaultCoord ? 1 : size.x,
878 h = size.y == wxDefaultCoord ? 22 : size.y;
879
880 // create the native WC_HEADER window:
881 WXHWND hwndParent = (HWND)parent->GetHandle();
882 WXDWORD msStyle = WS_CHILD | HDS_BUTTONS | HDS_HORZ | HDS_HOTTRACK | HDS_FULLDRAG;
883 m_hWnd = CreateWindowEx(0,
884 WC_HEADER,
885 (LPCTSTR) NULL,
886 msStyle,
887 x, y, w, h,
888 (HWND)hwndParent,
889 (HMENU)-1,
890 wxGetInstance(),
891 (LPVOID) NULL);
892 if (m_hWnd == NULL)
893 {
894 wxLogLastError(_T("CreateWindowEx"));
895 return false;
896 }
897
898 // we need to do the association to force wxWindow::HandleNotify
899 // to call wxDataViewHeaderWindow::MSWOnNotify
900 wxAssociateWinWithHandle((HWND)m_hWnd, this);
901
902
903 RECT rcParent;
904 HDLAYOUT hdl;
905 WINDOWPOS wp;
906
907 // Retrieve the bounding rectangle of the parent window's
908 // client area, and then request size and position values
909 // from the header control.
910 ::GetClientRect((HWND)hwndParent, &rcParent);
911
912 hdl.prc = &rcParent;
913 hdl.pwpos = &wp;
914 if (!SendMessage((HWND)m_hWnd, HDM_LAYOUT, 0, (LPARAM) &hdl))
915 {
916 wxLogLastError(_T("SendMessage"));
917 return false;
918 }
919
920 // Set the size, position, and visibility of the header control.
921 SetWindowPos((HWND)m_hWnd,
922 wp.hwndInsertAfter,
923 wp.x, wp.y,
924 wp.cx, wp.cy,
925 wp.flags | SWP_SHOWWINDOW);
926
927 // set our size hints: wxDataViewCtrl will put this wxWindow inside
928 // a wxBoxSizer and in order to avoid super-big header windows,
929 // we need to set our height as fixed
930 SetMinSize(wxSize(-1, wp.cy));
931 SetMaxSize(wxSize(-1, wp.cy));
932
933 // the following is required to get the default win's font for header windows
934 SetFont(GetFont());
935
936 return true;
937 }
938
939 wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
940 {
941 wxRemoveHandleAssociation(this);
942 }
943
944 void wxDataViewHeaderWindowMSW::UpdateDisplay()
945 {
946 // remove old columns
947 for (int i=0, max=Header_GetItemCount((HWND)m_hWnd); i < max; i++)
948 Header_DeleteItem((HWND)m_hWnd, 0);
949
950 // add the updated array of columns to the header control
951 unsigned int cols = GetOwner()->GetNumberOfColumns();
952 for (unsigned int i = 0; i < cols; i++)
953 {
954 wxDataViewColumn *col = GetColumn( i );
955 if (col->IsHidden())
956 continue; // don't add it!
957
958 HDITEM hdi;
959 hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
960 hdi.pszText = (WCHAR*) col->GetTitle().c_str();
961 hdi.cxy = col->GetWidth();
962 hdi.cchTextMax = sizeof(hdi.pszText)/sizeof(hdi.pszText[0]);
963 hdi.fmt = HDF_LEFT | HDF_STRING;
964
965 SendMessage((HWND)m_hWnd, HDM_INSERTITEM, (WPARAM)i, (LPARAM)&hdi);
966 }
967 }
968
969 bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
970 {
971 NMHDR *nmhdr = (NMHDR *)lParam;
972
973 // is it a message from the header?
974 if ( nmhdr->hwndFrom != (HWND)m_hWnd )
975 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
976
977 NMHEADER *nmHDR = (NMHEADER *)nmhdr;
978 switch ( nmhdr->code )
979 {
980 case HDN_BEGINTRACK:
981 // user has started to resize a column:
982 // do we need to veto it?
983 if (!GetColumn(nmHDR->iItem)->IsResizeable())
984 {
985 // veto it!
986 *result = TRUE;
987 }
988 break;
989
990 case HDN_BEGINDRAG:
991 // user has started to reorder a column
992 break;
993
994 case HDN_ITEMCHANGED: // user is resizing a column
995 case HDN_ENDTRACK: // user has finished resizing a column
996 case HDN_ENDDRAG: // user has finished reordering a column
997
998 // update the width of the modified column:
999 if ((nmHDR->pitem->mask & HDI_WIDTH) != 0 &&
1000 nmHDR->pitem != NULL)
1001 SetColumnWidth(nmHDR->iItem, nmHDR->pitem->cxy);
1002 break;
1003
1004 case HDN_ITEMCLICK:
1005 {
1006 wxEventType evt = nmHDR->iButton == 0 ?
1007 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1008 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1009 SendEvent(evt, nmHDR->iItem);
1010 }
1011 break;
1012
1013 case NM_RCLICK:
1014 {
1015 // NOTE: for some reason (i.e. for a bug in Windows)
1016 // the HDN_ITEMCLICK notification is not sent on
1017 // right clicks, so we need to handle NM_RCLICK
1018
1019 POINT ptClick;
1020 unsigned int column =
1021 wxMSWGetColumnClicked(nmhdr, &ptClick);
1022
1023 if (column != wxNOT_FOUND)
1024 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
1025 column);
1026 }
1027 break;
1028
1029 case HDN_GETDISPINFOW:
1030 // see wxListCtrl::MSWOnNotify for more info!
1031 break;
1032
1033 case HDN_ITEMDBLCLICK:
1034 {
1035 unsigned int idx = nmHDR->iItem;
1036 int w = GetOwner()->GetBestColumnWidth(idx);
1037
1038 // update the native control:
1039 HDITEM hd;
1040 ZeroMemory(&hd, sizeof(hd));
1041 hd.mask = HDI_WIDTH;
1042 hd.cxy = w;
1043 Header_SetItem(GetHwnd(), idx, &hd);
1044
1045 // update the wxDataViewColumn class:
1046 SetColumnWidth(idx, w);
1047 }
1048 break;
1049
1050 default:
1051 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1052 }
1053
1054 return true;
1055 }
1056
1057 void wxDataViewHeaderWindowMSW::ScrollWindow(int WXUNUSED(dx), int WXUNUSED(dy),
1058 const wxRect *WXUNUSED(rect))
1059 {
1060 wxSize ourSz = GetClientSize();
1061 wxSize ownerSz = m_owner->GetClientSize();
1062
1063 // where should the (logical) origin of this window be placed?
1064 int x1 = 0, y1 = 0;
1065 m_owner->CalcUnscrolledPosition(0, 0, &x1, &y1);
1066
1067 // put this window on top of our parent and
1068 SetWindowPos((HWND)m_hWnd, HWND_TOP, -x1, y1,
1069 ownerSz.GetWidth() + x1, ourSz.GetHeight(),
1070 SWP_SHOWWINDOW);
1071 }
1072
1073 #else // !defined(__WXMSW__)
1074
1075 IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow)
1076 BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow)
1077 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint)
1078 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse)
1079 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus)
1080 END_EVENT_TABLE()
1081
1082 bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl *parent, wxWindowID id,
1083 const wxPoint &pos, const wxSize &size,
1084 const wxString &name )
1085 {
1086 m_owner = parent;
1087
1088 if (!wxDataViewHeaderWindowBase::Create(parent, id, pos, size, name))
1089 return false;
1090
1091 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
1092 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1093 SetOwnForegroundColour( attr.colFg );
1094 SetOwnBackgroundColour( attr.colBg );
1095 if (!m_hasFont)
1096 SetOwnFont( attr.font );
1097
1098 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1099 // a wxBoxSizer and in order to avoid super-big header windows,
1100 // we need to set our height as fixed
1101 SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1102 SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1103
1104 return true;
1105 }
1106
1107 void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1108 {
1109 int w, h;
1110 GetClientSize( &w, &h );
1111
1112 wxAutoBufferedPaintDC dc( this );
1113
1114 dc.SetBackground(GetBackgroundColour());
1115 dc.Clear();
1116
1117 int xpix;
1118 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1119
1120 int x;
1121 m_owner->GetViewStart( &x, NULL );
1122
1123 // account for the horz scrollbar offset
1124 dc.SetDeviceOrigin( -x * xpix, 0 );
1125
1126 dc.SetFont( GetFont() );
1127
1128 unsigned int cols = GetOwner()->GetNumberOfColumns();
1129 unsigned int i;
1130 int xpos = 0;
1131 for (i = 0; i < cols; i++)
1132 {
1133 wxDataViewColumn *col = GetColumn( i );
1134 if (col->IsHidden())
1135 break; // don't draw it!
1136
1137 int cw = col->GetWidth();
1138 int ch = h;
1139
1140 wxRendererNative::Get().DrawHeaderButton
1141 (
1142 this,
1143 dc,
1144 wxRect(xpos, 0, cw, ch-1),
1145 m_parent->IsEnabled() ? 0
1146 : (int)wxCONTROL_DISABLED
1147 );
1148
1149 dc.DrawText( col->GetTitle(), xpos+3, 3 );
1150
1151 xpos += cw;
1152 }
1153 }
1154
1155 void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
1156 {
1157 GetParent()->SetFocus();
1158 event.Skip();
1159 }
1160
1161 void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent &event )
1162 {
1163 // we want to work with logical coords
1164 int x;
1165 m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
1166 int y = event.GetY();
1167
1168 if (m_isDragging)
1169 {
1170 // we don't draw the line beyond our window,
1171 // but we allow dragging it there
1172 int w = 0;
1173 GetClientSize( &w, NULL );
1174 m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
1175 w -= 6;
1176
1177 // erase the line if it was drawn
1178 if (m_currentX < w)
1179 DrawCurrent();
1180
1181 if (event.ButtonUp())
1182 {
1183 m_isDragging = false;
1184 if (HasCapture())
1185 ReleaseMouse();
1186
1187 m_dirty = true;
1188
1189 SetColumnWidth(m_column, m_currentX - m_minX);
1190
1191 Refresh();
1192 GetOwner()->Refresh();
1193 }
1194 else
1195 {
1196 m_currentX = wxMax(m_minX + 7, x);
1197
1198 // draw in the new location
1199 if (m_currentX < w) DrawCurrent();
1200 }
1201
1202 }
1203 else // not dragging
1204 {
1205 m_minX = 0;
1206 m_column = wxNOT_FOUND;
1207
1208 bool hit_border = false;
1209
1210 // end of the current column
1211 int xpos = 0;
1212
1213 // find the column where this event occured
1214 int countCol = m_owner->GetNumberOfColumns();
1215 for (int column = 0; column < countCol; column++)
1216 {
1217 wxDataViewColumn *p = GetColumn(column);
1218
1219 if (p->IsHidden())
1220 continue; // skip if not shown
1221
1222 xpos += p->GetWidth();
1223 m_column = column;
1224 if ((abs(x-xpos) < 3) && (y < 22))
1225 {
1226 hit_border = true;
1227 break;
1228 }
1229
1230 if (x < xpos)
1231 {
1232 // inside the column
1233 break;
1234 }
1235
1236 m_minX = xpos;
1237 }
1238
1239 if (m_column == wxNOT_FOUND)
1240 return;
1241
1242 bool resizeable = GetColumn(m_column)->IsResizeable();
1243 if (event.LeftDClick() && resizeable)
1244 {
1245 SetColumnWidth(m_column, GetOwner()->GetBestColumnWidth(m_column));
1246 Refresh();
1247 }
1248 else if (event.LeftDown() || event.RightUp())
1249 {
1250 if (hit_border && event.LeftDown() && resizeable)
1251 {
1252 m_isDragging = true;
1253 CaptureMouse();
1254 m_currentX = x;
1255 DrawCurrent();
1256 }
1257 else // click on a column
1258 {
1259 wxEventType evt = event.LeftDown() ?
1260 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1261 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1262 SendEvent(evt, m_column);
1263 }
1264 }
1265 else if (event.Moving())
1266 {
1267 if (hit_border && resizeable)
1268 m_currentCursor = m_resizeCursor;
1269 else
1270 m_currentCursor = wxSTANDARD_CURSOR;
1271
1272 SetCursor(*m_currentCursor);
1273 }
1274 }
1275 }
1276
1277 void wxGenericDataViewHeaderWindow::DrawCurrent()
1278 {
1279 int x1 = m_currentX;
1280 int y1 = 0;
1281 ClientToScreen (&x1, &y1);
1282
1283 int x2 = m_currentX-1;
1284 #ifdef __WXMSW__
1285 ++x2; // but why ????
1286 #endif
1287 int y2 = 0;
1288 m_owner->GetClientSize( NULL, &y2 );
1289 m_owner->ClientToScreen( &x2, &y2 );
1290
1291 wxScreenDC dc;
1292 dc.SetLogicalFunction (wxINVERT);
1293 //dc.SetPen (wxPen (*wxBLACK, 2, wxSOLID));
1294 dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT),1, wxSOLID));
1295 dc.SetBrush (*wxTRANSPARENT_BRUSH);
1296
1297 AdjustDC(dc);
1298 dc.DrawLine (x1, y1, x2, y2);
1299 dc.SetLogicalFunction (wxCOPY);
1300 dc.SetPen (wxNullPen);
1301 dc.SetBrush (wxNullBrush);
1302 }
1303
1304 void wxGenericDataViewHeaderWindow::AdjustDC(wxDC& dc)
1305 {
1306 int xpix, x;
1307
1308 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1309 m_owner->GetViewStart( &x, NULL );
1310
1311 // shift the DC origin to match the position of the main window horizontal
1312 // scrollbar: this allows us to always use logical coords
1313 dc.SetDeviceOrigin( -x * xpix, 0 );
1314 }
1315
1316 #endif // defined(__WXMSW__)
1317
1318 //-----------------------------------------------------------------------------
1319 // wxDataViewRenameTimer
1320 //-----------------------------------------------------------------------------
1321
1322 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1323 {
1324 m_owner = owner;
1325 }
1326
1327 void wxDataViewRenameTimer::Notify()
1328 {
1329 m_owner->OnRenameTimer();
1330 }
1331
1332 //-----------------------------------------------------------------------------
1333 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
1334 //-----------------------------------------------------------------------------
1335
1336 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler)
1337 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar)
1338 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp)
1339 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus)
1340 END_EVENT_TABLE()
1341
1342 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
1343 wxDataViewMainWindow *owner,
1344 wxTextCtrl *text,
1345 wxDataViewListModel *model,
1346 unsigned int col, unsigned int row,
1347 wxRect rectLabel )
1348 {
1349 m_owner = owner;
1350 m_model = model;
1351 m_row = row;
1352 m_col = col;
1353 m_text = text;
1354
1355 m_finished = false;
1356 m_aboutToFinish = false;
1357
1358 wxVariant value;
1359 model->GetValue( value, col, row );
1360 m_startValue = value.GetString();
1361
1362 m_owner->GetOwner()->CalcScrolledPosition(
1363 rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
1364
1365 m_text->Create( owner, wxID_ANY, m_startValue,
1366 wxPoint(rectLabel.x-2,rectLabel.y-2),
1367 wxSize(rectLabel.width+7,rectLabel.height+4) );
1368 m_text->SetFocus();
1369
1370 m_text->PushEventHandler(this);
1371 }
1372
1373 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
1374 {
1375 m_aboutToFinish = true;
1376
1377 // Notify the owner about the changes
1378 AcceptChanges();
1379
1380 // Even if vetoed, close the control (consistent with MSW)
1381 Finish();
1382 }
1383
1384 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent &event )
1385 {
1386 switch ( event.m_keyCode )
1387 {
1388 case WXK_RETURN:
1389 AcceptChangesAndFinish();
1390 break;
1391
1392 case WXK_ESCAPE:
1393 // m_owner->OnRenameCancelled( m_itemEdited );
1394 Finish();
1395 break;
1396
1397 default:
1398 event.Skip();
1399 }
1400 }
1401
1402 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
1403 {
1404 if (m_finished)
1405 {
1406 event.Skip();
1407 return;
1408 }
1409
1410 // auto-grow the textctrl
1411 wxSize parentSize = m_owner->GetSize();
1412 wxPoint myPos = m_text->GetPosition();
1413 wxSize mySize = m_text->GetSize();
1414 int sx, sy;
1415 m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
1416 if (myPos.x + sx > parentSize.x)
1417 sx = parentSize.x - myPos.x;
1418 if (mySize.x > sx)
1419 sx = mySize.x;
1420 m_text->SetSize(sx, wxDefaultCoord);
1421
1422 event.Skip();
1423 }
1424
1425 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
1426 {
1427 if ( !m_finished && !m_aboutToFinish )
1428 {
1429 AcceptChanges();
1430 //if ( !AcceptChanges() )
1431 // m_owner->OnRenameCancelled( m_itemEdited );
1432
1433 Finish();
1434 }
1435
1436 // We must let the native text control handle focus
1437 event.Skip();
1438 }
1439
1440 bool wxDataViewTextCtrlWrapper::AcceptChanges()
1441 {
1442 const wxString value = m_text->GetValue();
1443
1444 if ( value == m_startValue )
1445 // nothing changed, always accept
1446 return true;
1447
1448 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
1449 // vetoed by the user
1450 // return false;
1451
1452 // accepted, do rename the item
1453 wxVariant variant;
1454 variant = value;
1455 m_model->SetValue( variant, m_col, m_row );
1456 m_model->ValueChanged( m_col, m_row );
1457
1458 return true;
1459 }
1460
1461 void wxDataViewTextCtrlWrapper::Finish()
1462 {
1463 if ( !m_finished )
1464 {
1465 m_finished = true;
1466
1467 m_text->RemoveEventHandler(this);
1468 m_owner->FinishEditing(m_text);
1469
1470 // delete later
1471 wxPendingDelete.Append( this );
1472 }
1473 }
1474
1475 //-----------------------------------------------------------------------------
1476 // wxDataViewMainWindow
1477 //-----------------------------------------------------------------------------
1478
1479 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
1480 {
1481 if (row1 > row2) return 1;
1482 if (row1 == row2) return 0;
1483 return -1;
1484 }
1485
1486
1487 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
1488
1489 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1490 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1491 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1492 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
1493 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1494 EVT_CHAR (wxDataViewMainWindow::OnChar)
1495 END_EVENT_TABLE()
1496
1497 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1498 const wxPoint &pos, const wxSize &size, const wxString &name ) :
1499 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
1500 m_selection( wxDataViewSelectionCmp )
1501
1502 {
1503 SetOwner( parent );
1504
1505 m_lastOnSame = false;
1506 m_renameTimer = new wxDataViewRenameTimer( this );
1507 m_textctrlWrapper = NULL;
1508
1509 // TODO: user better initial values/nothing selected
1510 m_currentCol = NULL;
1511 m_currentRow = 0;
1512
1513 // TODO: we need to calculate this smartly
1514 m_lineHeight =
1515 #ifdef __WXMSW__
1516 17;
1517 #else
1518 20;
1519 #endif
1520
1521 m_dragCount = 0;
1522 m_dragStart = wxPoint(0,0);
1523 m_lineLastClicked = (unsigned int) -1;
1524 m_lineBeforeLastClicked = (unsigned int) -1;
1525 m_lineSelectSingleOnUp = (unsigned int) -1;
1526
1527 m_hasFocus = false;
1528
1529 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1530 SetBackgroundColour( *wxWHITE );
1531
1532 UpdateDisplay();
1533 }
1534
1535 wxDataViewMainWindow::~wxDataViewMainWindow()
1536 {
1537 delete m_renameTimer;
1538 }
1539
1540 void wxDataViewMainWindow::OnRenameTimer()
1541 {
1542 // We have to call this here because changes may just have
1543 // been made and no screen update taken place.
1544 if ( m_dirty )
1545 wxSafeYield();
1546
1547
1548 int xpos = 0;
1549 unsigned int cols = GetOwner()->GetNumberOfColumns();
1550 unsigned int i;
1551 for (i = 0; i < cols; i++)
1552 {
1553 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1554 if (c == m_currentCol)
1555 break;
1556 xpos += c->GetWidth();
1557 }
1558 wxRect labelRect( xpos, m_currentRow * m_lineHeight,
1559 m_currentCol->GetWidth(), m_lineHeight );
1560
1561 wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
1562
1563 wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
1564 m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
1565 m_currentCol->GetModelColumn(), m_currentRow, labelRect );
1566 }
1567
1568 void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text )
1569 {
1570 delete text;
1571 m_textctrlWrapper = NULL;
1572 SetFocus();
1573 // SetFocusIgnoringChildren();
1574 }
1575
1576 bool wxDataViewMainWindow::RowAppended()
1577 {
1578 return false;
1579 }
1580
1581 bool wxDataViewMainWindow::RowPrepended()
1582 {
1583 return false;
1584 }
1585
1586 bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before) )
1587 {
1588 return false;
1589 }
1590
1591 bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row) )
1592 {
1593 return false;
1594 }
1595
1596 bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row) )
1597 {
1598 return false;
1599 }
1600
1601 bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col), unsigned int row )
1602 {
1603 wxRect rect( 0, row*m_lineHeight, 10000, m_lineHeight );
1604 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1605 Refresh( true, &rect );
1606
1607 return true;
1608 }
1609
1610 bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order) )
1611 {
1612 Refresh();
1613
1614 return true;
1615 }
1616
1617 bool wxDataViewMainWindow::Cleared()
1618 {
1619 return false;
1620 }
1621
1622 void wxDataViewMainWindow::UpdateDisplay()
1623 {
1624 m_dirty = true;
1625 }
1626
1627 void wxDataViewMainWindow::OnInternalIdle()
1628 {
1629 wxWindow::OnInternalIdle();
1630
1631 if (m_dirty)
1632 {
1633 RecalculateDisplay();
1634 m_dirty = false;
1635 }
1636 }
1637
1638 void wxDataViewMainWindow::RecalculateDisplay()
1639 {
1640 wxDataViewListModel *model = GetOwner()->GetModel();
1641 if (!model)
1642 {
1643 Refresh();
1644 return;
1645 }
1646
1647 int width = 0;
1648 unsigned int cols = GetOwner()->GetNumberOfColumns();
1649 unsigned int i;
1650 for (i = 0; i < cols; i++)
1651 {
1652 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1653 width += col->GetWidth();
1654 }
1655
1656 int height = model->GetNumberOfRows() * m_lineHeight;
1657
1658 SetVirtualSize( width, height );
1659 GetOwner()->SetScrollRate( 10, m_lineHeight );
1660
1661 Refresh();
1662 }
1663
1664 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1665 {
1666 wxWindow::ScrollWindow( dx, dy, rect );
1667 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1668 }
1669
1670 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1671 {
1672 wxAutoBufferedPaintDC dc( this );
1673
1674 dc.SetBackground(GetBackgroundColour());
1675 dc.Clear();
1676
1677 GetOwner()->PrepareDC( dc );
1678
1679 dc.SetFont( GetFont() );
1680
1681 wxRect update = GetUpdateRegion().GetBox();
1682 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1683
1684 wxDataViewListModel *model = GetOwner()->GetModel();
1685
1686 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
1687 unsigned int item_count =
1688 wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1689 (int)(model->GetNumberOfRows()-item_start) );
1690
1691 unsigned int item;
1692 for (item = item_start; item < item_start+item_count; item++)
1693 {
1694 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1695 if (selected || item == m_currentRow)
1696 {
1697 int flags = selected ? wxCONTROL_SELECTED : 0;
1698 if (item == m_currentRow)
1699 flags |= wxCONTROL_CURRENT;
1700 if (m_hasFocus)
1701 flags |= wxCONTROL_FOCUSED;
1702 wxRect rect( 0, item*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
1703 wxRendererNative::Get().DrawItemSelectionRect
1704 (
1705 this,
1706 dc,
1707 rect,
1708 flags
1709 );
1710 }
1711 }
1712
1713 wxRect cell_rect;
1714 cell_rect.x = 0;
1715 cell_rect.height = m_lineHeight;
1716 unsigned int cols = GetOwner()->GetNumberOfColumns();
1717 unsigned int i;
1718 for (i = 0; i < cols; i++)
1719 {
1720 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1721 wxDataViewRenderer *cell = col->GetRenderer();
1722 cell_rect.width = col->GetWidth();
1723
1724 if (col->IsHidden())
1725 continue; // skipt it!
1726
1727 for (item = item_start; item < item_start+item_count; item++)
1728 {
1729 // get the cell value and set it into the renderer
1730 wxVariant value;
1731 model->GetValue( value, col->GetModelColumn(), item );
1732 cell->SetValue( value );
1733
1734 // update the y offset
1735 cell_rect.y = item*m_lineHeight;
1736
1737 // cannot be bigger than allocated space
1738 wxSize size = cell->GetSize();
1739 size.x = wxMin( size.x, cell_rect.width );
1740 size.y = wxMin( size.y, cell_rect.height );
1741
1742 wxRect item_rect(cell_rect.GetTopLeft(), size);
1743
1744 // horizontal alignment:
1745 if (col->GetAlignment() & wxALIGN_CENTER_HORIZONTAL)
1746 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1747 else if (col->GetAlignment() & wxALIGN_RIGHT)
1748 item_rect.x = cell_rect.x + cell_rect.width - size.x;
1749 //else: wxALIGN_LEFT is the default
1750
1751 // vertical alignment:
1752 if (col->GetAlignment() & wxALIGN_CENTER_VERTICAL)
1753 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1754 else if (col->GetAlignment() & wxALIGN_BOTTOM)
1755 item_rect.y = cell_rect.y + cell_rect.height - size.y;
1756 //else: wxALIGN_TOP is the default
1757
1758 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1759
1760 item_rect.width = size.x;
1761 item_rect.height= size.y;
1762
1763 int state = 0;
1764 //if (item == m_currentRow) -- seems wrong to me...
1765 if (m_selection.Index(item) != wxNOT_FOUND)
1766 state |= wxDATAVIEW_CELL_SELECTED;
1767
1768 cell->Render( item_rect, &dc, state );
1769 }
1770
1771 cell_rect.x += cell_rect.width;
1772 }
1773 }
1774
1775 int wxDataViewMainWindow::GetCountPerPage()
1776 {
1777 wxSize size = GetClientSize();
1778 return size.y / m_lineHeight;
1779 }
1780
1781 int wxDataViewMainWindow::GetEndOfLastCol()
1782 {
1783 int width = 0;
1784 unsigned int i;
1785 for (i = 0; i < GetOwner()->GetNumberOfColumns(); i++)
1786 {
1787 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1788 width += c->GetWidth();
1789 }
1790 return width;
1791 }
1792
1793 unsigned int wxDataViewMainWindow::GetFirstVisibleRow()
1794 {
1795 int x = 0;
1796 int y = 0;
1797 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1798
1799 return y / m_lineHeight;
1800 }
1801
1802 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
1803 {
1804 wxSize client_size = GetClientSize();
1805 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
1806 &client_size.x, &client_size.y );
1807
1808 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
1809 }
1810
1811 unsigned int wxDataViewMainWindow::GetRowCount()
1812 {
1813 return GetOwner()->GetModel()->GetNumberOfRows();
1814 }
1815
1816 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
1817 {
1818 m_currentRow = row;
1819
1820 // send event
1821 }
1822
1823 void wxDataViewMainWindow::SelectAllRows( bool on )
1824 {
1825 if (IsEmpty())
1826 return;
1827
1828 if (on)
1829 {
1830 m_selection.Clear();
1831 for (unsigned int i = 0; i < GetRowCount(); i++)
1832 m_selection.Add( i );
1833 Refresh();
1834 }
1835 else
1836 {
1837 unsigned int first_visible = GetFirstVisibleRow();
1838 unsigned int last_visible = GetLastVisibleRow();
1839 unsigned int i;
1840 for (i = 0; i < m_selection.GetCount(); i++)
1841 {
1842 unsigned int row = m_selection[i];
1843 if ((row >= first_visible) && (row <= last_visible))
1844 RefreshRow( row );
1845 }
1846 m_selection.Clear();
1847 }
1848 }
1849
1850 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
1851 {
1852 if (m_selection.Index( row ) == wxNOT_FOUND)
1853 {
1854 if (on)
1855 {
1856 m_selection.Add( row );
1857 RefreshRow( row );
1858 }
1859 }
1860 else
1861 {
1862 if (!on)
1863 {
1864 m_selection.Remove( row );
1865 RefreshRow( row );
1866 }
1867 }
1868 }
1869
1870 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
1871 {
1872 if (from > to)
1873 {
1874 unsigned int tmp = from;
1875 from = to;
1876 to = tmp;
1877 }
1878
1879 unsigned int i;
1880 for (i = from; i <= to; i++)
1881 {
1882 if (m_selection.Index( i ) == wxNOT_FOUND)
1883 {
1884 if (on)
1885 m_selection.Add( i );
1886 }
1887 else
1888 {
1889 if (!on)
1890 m_selection.Remove( i );
1891 }
1892 }
1893 RefreshRows( from, to );
1894 }
1895
1896 void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
1897 {
1898 for (size_t i=0; i < aSelections.GetCount(); i++)
1899 {
1900 int n = aSelections[i];
1901
1902 m_selection.Add( n );
1903 RefreshRow( n );
1904 }
1905 }
1906
1907 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
1908 {
1909 if (m_selection.Index( row ) == wxNOT_FOUND)
1910 m_selection.Add( row );
1911 else
1912 m_selection.Remove( row );
1913 RefreshRow( row );
1914 }
1915
1916 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
1917 {
1918 return (m_selection.Index( row ) != wxNOT_FOUND);
1919 }
1920
1921 void wxDataViewMainWindow::RefreshRow( unsigned int row )
1922 {
1923 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
1924 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1925
1926 wxSize client_size = GetClientSize();
1927 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1928 wxRect intersect_rect = client_rect.Intersect( rect );
1929 if (intersect_rect.width > 0)
1930 Refresh( true, &intersect_rect );
1931 }
1932
1933 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
1934 {
1935 if (from > to)
1936 {
1937 unsigned int tmp = to;
1938 to = from;
1939 from = tmp;
1940 }
1941
1942 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
1943 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1944
1945 wxSize client_size = GetClientSize();
1946 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1947 wxRect intersect_rect = client_rect.Intersect( rect );
1948 if (intersect_rect.width > 0)
1949 Refresh( true, &intersect_rect );
1950 }
1951
1952 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
1953 {
1954 unsigned int count = GetRowCount();
1955 if (firstRow > count)
1956 return;
1957
1958 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
1959 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1960
1961 wxSize client_size = GetClientSize();
1962 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1963 wxRect intersect_rect = client_rect.Intersect( rect );
1964 if (intersect_rect.width > 0)
1965 Refresh( true, &intersect_rect );
1966 }
1967
1968 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
1969 {
1970 wxCHECK_RET( newCurrent < GetRowCount(),
1971 _T("invalid item index in OnArrowChar()") );
1972
1973 // if there is no selection, we cannot move it anywhere
1974 if (!HasCurrentRow())
1975 return;
1976
1977 unsigned int oldCurrent = m_currentRow;
1978
1979 // in single selection we just ignore Shift as we can't select several
1980 // items anyhow
1981 if ( event.ShiftDown() && !IsSingleSel() )
1982 {
1983 RefreshRow( oldCurrent );
1984
1985 ChangeCurrentRow( newCurrent );
1986
1987 // select all the items between the old and the new one
1988 if ( oldCurrent > newCurrent )
1989 {
1990 newCurrent = oldCurrent;
1991 oldCurrent = m_currentRow;
1992 }
1993
1994 SelectRows( oldCurrent, newCurrent, true );
1995 }
1996 else // !shift
1997 {
1998 RefreshRow( oldCurrent );
1999
2000 // all previously selected items are unselected unless ctrl is held
2001 if ( !event.ControlDown() )
2002 SelectAllRows(false);
2003
2004 ChangeCurrentRow( newCurrent );
2005
2006 if ( !event.ControlDown() )
2007 SelectRow( m_currentRow, true );
2008 else
2009 RefreshRow( m_currentRow );
2010 }
2011
2012 // MoveToFocus();
2013 }
2014
2015 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
2016 {
2017 if (event.GetKeyCode() == WXK_TAB)
2018 {
2019 wxNavigationKeyEvent nevent;
2020 nevent.SetWindowChange( event.ControlDown() );
2021 nevent.SetDirection( !event.ShiftDown() );
2022 nevent.SetEventObject( GetParent()->GetParent() );
2023 nevent.SetCurrentFocus( m_parent );
2024 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
2025 return;
2026 }
2027
2028 // no item -> nothing to do
2029 if (!HasCurrentRow())
2030 {
2031 event.Skip();
2032 return;
2033 }
2034
2035 // don't use m_linesPerPage directly as it might not be computed yet
2036 const int pageSize = GetCountPerPage();
2037 wxCHECK_RET( pageSize, _T("should have non zero page size") );
2038
2039 switch ( event.GetKeyCode() )
2040 {
2041 case WXK_UP:
2042 if ( m_currentRow > 0 )
2043 OnArrowChar( m_currentRow - 1, event );
2044 break;
2045
2046 case WXK_DOWN:
2047 if ( m_currentRow < GetRowCount() - 1 )
2048 OnArrowChar( m_currentRow + 1, event );
2049 break;
2050
2051 case WXK_END:
2052 if (!IsEmpty())
2053 OnArrowChar( GetRowCount() - 1, event );
2054 break;
2055
2056 case WXK_HOME:
2057 if (!IsEmpty())
2058 OnArrowChar( 0, event );
2059 break;
2060
2061 case WXK_PAGEUP:
2062 {
2063 int steps = pageSize - 1;
2064 int index = m_currentRow - steps;
2065 if (index < 0)
2066 index = 0;
2067
2068 OnArrowChar( index, event );
2069 }
2070 break;
2071
2072 case WXK_PAGEDOWN:
2073 {
2074 int steps = pageSize - 1;
2075 unsigned int index = m_currentRow + steps;
2076 unsigned int count = GetRowCount();
2077 if ( index >= count )
2078 index = count - 1;
2079
2080 OnArrowChar( index, event );
2081 }
2082 break;
2083
2084 default:
2085 event.Skip();
2086 }
2087 }
2088
2089 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
2090 {
2091 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2092 {
2093 // let the base handle mouse wheel events.
2094 event.Skip();
2095 return;
2096 }
2097
2098 int x = event.GetX();
2099 int y = event.GetY();
2100 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2101
2102 wxDataViewColumn *col = NULL;
2103
2104 int xpos = 0;
2105 unsigned int cols = GetOwner()->GetNumberOfColumns();
2106 unsigned int i;
2107 for (i = 0; i < cols; i++)
2108 {
2109 wxDataViewColumn *c = GetOwner()->GetColumn( i );
2110 if (x < xpos + c->GetWidth())
2111 {
2112 col = c;
2113 break;
2114 }
2115 xpos += c->GetWidth();
2116 }
2117 if (!col)
2118 return;
2119 wxDataViewRenderer *cell = col->GetRenderer();
2120
2121 unsigned int current = y / m_lineHeight;
2122
2123 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
2124 {
2125 // Unselect all if below the last row ?
2126 return;
2127 }
2128
2129 wxDataViewListModel *model = GetOwner()->GetModel();
2130
2131 if (event.Dragging())
2132 {
2133 if (m_dragCount == 0)
2134 {
2135 // we have to report the raw, physical coords as we want to be
2136 // able to call HitTest(event.m_pointDrag) from the user code to
2137 // get the item being dragged
2138 m_dragStart = event.GetPosition();
2139 }
2140
2141 m_dragCount++;
2142
2143 if (m_dragCount != 3)
2144 return;
2145
2146 if (event.LeftIsDown())
2147 {
2148 // Notify cell about drag
2149 }
2150 return;
2151 }
2152 else
2153 {
2154 m_dragCount = 0;
2155 }
2156
2157 bool forceClick = false;
2158
2159 if (event.ButtonDClick())
2160 {
2161 m_renameTimer->Stop();
2162 m_lastOnSame = false;
2163 }
2164
2165 if (event.LeftDClick())
2166 {
2167 if ( current == m_lineLastClicked )
2168 {
2169 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
2170 {
2171 wxVariant value;
2172 model->GetValue( value, col->GetModelColumn(), current );
2173 cell->SetValue( value );
2174 wxRect cell_rect( xpos, current * m_lineHeight,
2175 col->GetWidth(), m_lineHeight );
2176 cell->Activate( cell_rect, model, col->GetModelColumn(), current );
2177 }
2178 return;
2179 }
2180 else
2181 {
2182 // The first click was on another item, so don't interpret this as
2183 // a double click, but as a simple click instead
2184 forceClick = true;
2185 }
2186 }
2187
2188 if (event.LeftUp())
2189 {
2190 if (m_lineSelectSingleOnUp != (unsigned int)-1)
2191 {
2192 // select single line
2193 SelectAllRows( false );
2194 SelectRow( m_lineSelectSingleOnUp, true );
2195 }
2196
2197 if (m_lastOnSame)
2198 {
2199 if ((col == m_currentCol) && (current == m_currentRow) &&
2200 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
2201 {
2202 m_renameTimer->Start( 100, true );
2203 }
2204 }
2205
2206 m_lastOnSame = false;
2207 m_lineSelectSingleOnUp = (unsigned int)-1;
2208 }
2209 else
2210 {
2211 // This is necessary, because after a DnD operation in
2212 // from and to ourself, the up event is swallowed by the
2213 // DnD code. So on next non-up event (which means here and
2214 // now) m_lineSelectSingleOnUp should be reset.
2215 m_lineSelectSingleOnUp = (unsigned int)-1;
2216 }
2217
2218 if (event.RightDown())
2219 {
2220 m_lineBeforeLastClicked = m_lineLastClicked;
2221 m_lineLastClicked = current;
2222
2223 // If the item is already selected, do not update the selection.
2224 // Multi-selections should not be cleared if a selected item is clicked.
2225 if (!IsRowSelected(current))
2226 {
2227 SelectAllRows(false);
2228 ChangeCurrentRow(current);
2229 SelectRow(m_currentRow,true);
2230 }
2231
2232 // notify cell about right click
2233 // cell->...
2234
2235 // Allow generation of context menu event
2236 event.Skip();
2237 }
2238 else if (event.MiddleDown())
2239 {
2240 // notify cell about middle click
2241 // cell->...
2242 }
2243 if (event.LeftDown() || forceClick)
2244 {
2245 #ifdef __WXMSW__
2246 SetFocus();
2247 #endif
2248
2249 m_lineBeforeLastClicked = m_lineLastClicked;
2250 m_lineLastClicked = current;
2251
2252 unsigned int oldCurrentRow = m_currentRow;
2253 bool oldWasSelected = IsRowSelected(m_currentRow);
2254
2255 bool cmdModifierDown = event.CmdDown();
2256 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
2257 {
2258 if ( IsSingleSel() || !IsRowSelected(current) )
2259 {
2260 SelectAllRows( false );
2261
2262 ChangeCurrentRow(current);
2263
2264 SelectRow(m_currentRow,true);
2265 }
2266 else // multi sel & current is highlighted & no mod keys
2267 {
2268 m_lineSelectSingleOnUp = current;
2269 ChangeCurrentRow(current); // change focus
2270 }
2271 }
2272 else // multi sel & either ctrl or shift is down
2273 {
2274 if (cmdModifierDown)
2275 {
2276 ChangeCurrentRow(current);
2277
2278 ReverseRowSelection(m_currentRow);
2279 }
2280 else if (event.ShiftDown())
2281 {
2282 ChangeCurrentRow(current);
2283
2284 unsigned int lineFrom = oldCurrentRow,
2285 lineTo = current;
2286
2287 if ( lineTo < lineFrom )
2288 {
2289 lineTo = lineFrom;
2290 lineFrom = m_currentRow;
2291 }
2292
2293 SelectRows(lineFrom, lineTo, true);
2294 }
2295 else // !ctrl, !shift
2296 {
2297 // test in the enclosing if should make it impossible
2298 wxFAIL_MSG( _T("how did we get here?") );
2299 }
2300 }
2301
2302 if (m_currentRow != oldCurrentRow)
2303 RefreshRow( oldCurrentRow );
2304
2305 wxDataViewColumn *oldCurrentCol = m_currentCol;
2306
2307 // Update selection here...
2308 m_currentCol = col;
2309
2310 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
2311 (current == oldCurrentRow)) && oldWasSelected;
2312 }
2313 }
2314
2315 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
2316 {
2317 m_hasFocus = true;
2318
2319 if (HasCurrentRow())
2320 Refresh();
2321
2322 event.Skip();
2323 }
2324
2325 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
2326 {
2327 m_hasFocus = false;
2328
2329 if (HasCurrentRow())
2330 Refresh();
2331
2332 event.Skip();
2333 }
2334
2335 //-----------------------------------------------------------------------------
2336 // wxDataViewCtrl
2337 //-----------------------------------------------------------------------------
2338
2339 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
2340
2341 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
2342 EVT_SIZE(wxDataViewCtrl::OnSize)
2343 END_EVENT_TABLE()
2344
2345 wxDataViewCtrl::~wxDataViewCtrl()
2346 {
2347 if (m_notifier)
2348 GetModel()->RemoveNotifier( m_notifier );
2349 }
2350
2351 void wxDataViewCtrl::Init()
2352 {
2353 m_notifier = NULL;
2354 }
2355
2356 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
2357 const wxPoint& pos, const wxSize& size,
2358 long style, const wxValidator& validator )
2359 {
2360 if (!wxControl::Create( parent, id, pos, size,
2361 style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
2362 return false;
2363
2364 Init();
2365
2366 #ifdef __WXMAC__
2367 MacSetClipChildren( true ) ;
2368 #endif
2369
2370 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
2371 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY );
2372
2373 SetTargetWindow( m_clientArea );
2374
2375 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
2376 sizer->Add( m_headerArea, 0, wxGROW );
2377 sizer->Add( m_clientArea, 1, wxGROW );
2378 SetSizer( sizer );
2379
2380 return true;
2381 }
2382
2383 #ifdef __WXMSW__
2384 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
2385 WXWPARAM wParam,
2386 WXLPARAM lParam)
2387 {
2388 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
2389
2390 #ifndef __WXWINCE__
2391 // we need to process arrows ourselves for scrolling
2392 if ( nMsg == WM_GETDLGCODE )
2393 {
2394 rc |= DLGC_WANTARROWS;
2395 }
2396 #endif
2397
2398 return rc;
2399 }
2400 #endif
2401
2402 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
2403 {
2404 // We need to override OnSize so that our scrolled
2405 // window a) does call Layout() to use sizers for
2406 // positioning the controls but b) does not query
2407 // the sizer for their size and use that for setting
2408 // the scrollable area as set that ourselves by
2409 // calling SetScrollbar() further down.
2410
2411 Layout();
2412
2413 AdjustScrollbars();
2414 }
2415
2416 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
2417 {
2418 if (!wxDataViewCtrlBase::AssociateModel( model ))
2419 return false;
2420
2421 m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
2422
2423 model->AddNotifier( m_notifier );
2424
2425 m_clientArea->UpdateDisplay();
2426
2427 return true;
2428 }
2429
2430 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
2431 {
2432 if (!wxDataViewCtrlBase::AppendColumn(col))
2433 return false;
2434
2435 m_clientArea->UpdateDisplay();
2436 m_headerArea->UpdateDisplay();
2437
2438 return true;
2439 }
2440
2441 void wxDataViewCtrl::SetSelection( int row )
2442 {
2443 m_clientArea->SelectRow(row, true);
2444 }
2445
2446 void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to )
2447 {
2448 m_clientArea->SelectRows(from, to, true);
2449 }
2450
2451 void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections )
2452 {
2453 m_clientArea->Select(aSelections);
2454 }
2455
2456 void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) )
2457 {
2458 // FIXME - TODO
2459 }
2460
2461 bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row) ) const
2462 {
2463 // FIXME - TODO
2464
2465 return false;
2466 }
2467
2468 int wxDataViewCtrl::GetSelection() const
2469 {
2470 // FIXME - TODO
2471
2472 return -1;
2473 }
2474
2475 int wxDataViewCtrl::GetSelections(wxArrayInt& WXUNUSED(aSelections) ) const
2476 {
2477 // FIXME - TODO
2478
2479 return 0;
2480 }
2481
2482 #endif
2483 // !wxUSE_GENERICDATAVIEWCTRL
2484
2485 #endif
2486 // wxUSE_DATAVIEWCTRL