Several wxBitmapDataCell changes.
[wxWidgets.git] / src / generic / datavgen.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datavgen.cpp
3 // Purpose: wxDataViewCtrl generic implementation
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #ifdef __BORLANDC__
14 #pragma hdrstop
15 #endif
16
17 #if wxUSE_DATAVIEWCTRL
18
19 #include "wx/dataview.h"
20
21 #ifdef wxUSE_GENERICDATAVIEWCTRL
22
23 #ifndef WX_PRECOMP
24 #ifdef __WXMSW__
25 #include "wx/msw/wrapwin.h"
26 #endif
27 #include "wx/sizer.h"
28 #include "wx/log.h"
29 #include "wx/dcclient.h"
30 #include "wx/timer.h"
31 #include "wx/settings.h"
32 #endif
33
34 #include "wx/stockitem.h"
35 #include "wx/calctrl.h"
36 #include "wx/popupwin.h"
37 #include "wx/renderer.h"
38 #include "wx/icon.h"
39
40 //-----------------------------------------------------------------------------
41 // classes
42 //-----------------------------------------------------------------------------
43
44 class wxDataViewCtrl;
45
46 //-----------------------------------------------------------------------------
47 // wxDataViewHeaderWindow
48 //-----------------------------------------------------------------------------
49
50 class wxDataViewHeaderWindow: public wxWindow
51 {
52 public:
53 wxDataViewHeaderWindow( wxDataViewCtrl *parent,
54 wxWindowID id,
55 const wxPoint &pos = wxDefaultPosition,
56 const wxSize &size = wxDefaultSize,
57 const wxString &name = wxT("wxdataviewctrlheaderwindow") );
58 virtual ~wxDataViewHeaderWindow();
59
60 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
61 wxDataViewCtrl *GetOwner() { return m_owner; }
62
63 void OnPaint( wxPaintEvent &event );
64 void OnMouse( wxMouseEvent &event );
65 void OnSetFocus( wxFocusEvent &event );
66
67 private:
68 wxDataViewCtrl *m_owner;
69 wxCursor *m_resizeCursor;
70
71 private:
72 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindow)
73 DECLARE_EVENT_TABLE()
74 };
75
76 //-----------------------------------------------------------------------------
77 // wxDataViewRenameTimer
78 //-----------------------------------------------------------------------------
79
80 class wxDataViewRenameTimer: public wxTimer
81 {
82 private:
83 wxDataViewMainWindow *m_owner;
84
85 public:
86 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
87 void Notify();
88 };
89
90 //-----------------------------------------------------------------------------
91 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
92 //-----------------------------------------------------------------------------
93
94 class wxDataViewTextCtrlWrapper : public wxEvtHandler
95 {
96 public:
97 // NB: text must be a valid object but not Create()d yet
98 wxDataViewTextCtrlWrapper( wxDataViewMainWindow *owner,
99 wxTextCtrl *text,
100 wxDataViewListModel *model,
101 size_t col, size_t row,
102 wxRect cellLabel );
103
104 wxTextCtrl *GetText() const { return m_text; }
105
106 void AcceptChangesAndFinish();
107
108 protected:
109 void OnChar( wxKeyEvent &event );
110 void OnKeyUp( wxKeyEvent &event );
111 void OnKillFocus( wxFocusEvent &event );
112
113 bool AcceptChanges();
114 void Finish();
115
116 private:
117 wxDataViewMainWindow *m_owner;
118 wxTextCtrl *m_text;
119 wxString m_startValue;
120 wxDataViewListModel *m_model;
121 size_t m_col;
122 size_t m_row;
123 bool m_finished;
124 bool m_aboutToFinish;
125
126 DECLARE_EVENT_TABLE()
127 };
128
129 //-----------------------------------------------------------------------------
130 // wxDataViewMainWindow
131 //-----------------------------------------------------------------------------
132
133 WX_DEFINE_SORTED_ARRAY_SIZE_T( size_t, wxDataViewSelection );
134
135 class wxDataViewMainWindow: public wxWindow
136 {
137 public:
138 wxDataViewMainWindow( wxDataViewCtrl *parent,
139 wxWindowID id,
140 const wxPoint &pos = wxDefaultPosition,
141 const wxSize &size = wxDefaultSize,
142 const wxString &name = wxT("wxdataviewctrlmainwindow") );
143 virtual ~wxDataViewMainWindow();
144
145 // notifications from wxDataViewListModel
146 bool RowAppended();
147 bool RowPrepended();
148 bool RowInserted( size_t before );
149 bool RowDeleted( size_t row );
150 bool RowChanged( size_t row );
151 bool ValueChanged( size_t col, size_t row );
152 bool RowsReordered( size_t *new_order );
153 bool Cleared();
154
155 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
156 wxDataViewCtrl *GetOwner() { return m_owner; }
157
158 void OnPaint( wxPaintEvent &event );
159 void OnArrowChar(size_t newCurrent, const wxKeyEvent& event);
160 void OnChar( wxKeyEvent &event );
161 void OnMouse( wxMouseEvent &event );
162 void OnSetFocus( wxFocusEvent &event );
163 void OnKillFocus( wxFocusEvent &event );
164
165 void UpdateDisplay();
166 void RecalculateDisplay();
167 void OnInternalIdle();
168
169 void OnRenameTimer();
170 void FinishEditing( wxTextCtrl *text );
171
172 void ScrollWindow( int dx, int dy, const wxRect *rect );
173
174 bool HasCurrentRow() { return m_currentRow != (size_t)-1; }
175 void ChangeCurrentRow( size_t row );
176
177 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); };
178 bool IsEmpty() { return GetRowCount() == 0; }
179
180 int GetCountPerPage();
181 int GetEndOfLastCol();
182 size_t GetFirstVisibleRow();
183 size_t GetLastVisibleRow();
184 size_t GetRowCount();
185
186 void SelectAllRows( bool on );
187 void SelectRow( size_t row, bool on );
188 void SelectRows( size_t from, size_t to, bool on );
189 void ReverseRowSelection( size_t row );
190 bool IsRowSelected( size_t row );
191
192 void RefreshRow( size_t row );
193 void RefreshRows( size_t from, size_t to );
194 void RefreshRowsAfter( size_t firstRow );
195
196 private:
197 wxDataViewCtrl *m_owner;
198 int m_lineHeight;
199 bool m_dirty;
200
201 wxDataViewColumn *m_currentCol;
202 size_t m_currentRow;
203 wxDataViewSelection m_selection;
204
205 wxDataViewRenameTimer *m_renameTimer;
206 wxDataViewTextCtrlWrapper *m_textctrlWrapper;
207 bool m_lastOnSame;
208
209 bool m_hasFocus;
210
211 int m_dragCount;
212 wxPoint m_dragStart;
213
214 // for double click logic
215 size_t m_lineLastClicked,
216 m_lineBeforeLastClicked,
217 m_lineSelectSingleOnUp;
218
219 private:
220 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
221 DECLARE_EVENT_TABLE()
222 };
223
224 // ---------------------------------------------------------
225 // wxGenericDataViewListModelNotifier
226 // ---------------------------------------------------------
227
228 class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
229 {
230 public:
231 wxGenericDataViewListModelNotifier( wxDataViewMainWindow *mainWindow )
232 { m_mainWindow = mainWindow; }
233
234 virtual bool RowAppended()
235 { return m_mainWindow->RowAppended(); }
236 virtual bool RowPrepended()
237 { return m_mainWindow->RowPrepended(); }
238 virtual bool RowInserted( size_t before )
239 { return m_mainWindow->RowInserted( before ); }
240 virtual bool RowDeleted( size_t row )
241 { return m_mainWindow->RowDeleted( row ); }
242 virtual bool RowChanged( size_t row )
243 { return m_mainWindow->RowChanged( row ); }
244 virtual bool ValueChanged( size_t col, size_t row )
245 { return m_mainWindow->ValueChanged( col, row ); }
246 virtual bool RowsReordered( size_t *new_order )
247 { return m_mainWindow->RowsReordered( new_order ); }
248 virtual bool Cleared()
249 { return m_mainWindow->Cleared(); }
250
251 wxDataViewMainWindow *m_mainWindow;
252 };
253
254 // ---------------------------------------------------------
255 // wxDataViewCell
256 // ---------------------------------------------------------
257
258 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCell, wxDataViewCellBase)
259
260 wxDataViewCell::wxDataViewCell( const wxString &varianttype, wxDataViewCellMode mode ) :
261 wxDataViewCellBase( varianttype, mode )
262 {
263 m_dc = NULL;
264 }
265
266 wxDataViewCell::~wxDataViewCell()
267 {
268 if (m_dc)
269 delete m_dc;
270 }
271
272 wxDC *wxDataViewCell::GetDC()
273 {
274 if (m_dc == NULL)
275 {
276 if (GetOwner() == NULL)
277 return NULL;
278 if (GetOwner()->GetOwner() == NULL)
279 return NULL;
280 m_dc = new wxClientDC( GetOwner()->GetOwner() );
281 }
282
283 return m_dc;
284 }
285
286 // ---------------------------------------------------------
287 // wxDataViewCustomCell
288 // ---------------------------------------------------------
289
290 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomCell, wxDataViewCell)
291
292 wxDataViewCustomCell::wxDataViewCustomCell( const wxString &varianttype,
293 wxDataViewCellMode mode ) :
294 wxDataViewCell( varianttype, mode )
295 {
296 }
297
298 // ---------------------------------------------------------
299 // wxDataViewTextCell
300 // ---------------------------------------------------------
301
302 IMPLEMENT_CLASS(wxDataViewTextCell, wxDataViewCustomCell)
303
304 wxDataViewTextCell::wxDataViewTextCell( const wxString &varianttype, wxDataViewCellMode mode ) :
305 wxDataViewCustomCell( varianttype, mode )
306 {
307 }
308
309 bool wxDataViewTextCell::SetValue( const wxVariant &value )
310 {
311 m_text = value.GetString();
312
313 return true;
314 }
315
316 bool wxDataViewTextCell::GetValue( wxVariant& WXUNUSED(value) )
317 {
318 return false;
319 }
320
321 bool wxDataViewTextCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
322 {
323 dc->DrawText( m_text, cell.x, cell.y );
324
325 return true;
326 }
327
328 wxSize wxDataViewTextCell::GetSize()
329 {
330 return wxSize(80,20);
331 }
332
333 // ---------------------------------------------------------
334 // wxDataViewBitmapCell
335 // ---------------------------------------------------------
336
337 IMPLEMENT_CLASS(wxDataViewBitmapCell, wxDataViewCustomCell)
338
339 wxDataViewBitmapCell::wxDataViewBitmapCell( const wxString &varianttype, wxDataViewCellMode mode ) :
340 wxDataViewCustomCell( varianttype, mode )
341 {
342 }
343
344 bool wxDataViewBitmapCell::SetValue( const wxVariant &value )
345 {
346 if (value.GetType() == wxT("wxBitmap"))
347 m_bitmap << value;
348 if (value.GetType() == wxT("wxIcon"))
349 m_icon << value;
350
351 return true;
352 }
353
354 bool wxDataViewBitmapCell::GetValue( wxVariant& WXUNUSED(value) )
355 {
356 return false;
357 }
358
359 bool wxDataViewBitmapCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
360 {
361 if (m_bitmap.Ok())
362 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
363 else if (m_icon.Ok())
364 dc->DrawIcon( m_icon, cell.x, cell.y );
365
366 return true;
367 }
368
369 wxSize wxDataViewBitmapCell::GetSize()
370 {
371 if (m_bitmap.Ok())
372 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
373 else if (m_icon.Ok())
374 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
375
376 return wxSize(16,16);
377 }
378
379 // ---------------------------------------------------------
380 // wxDataViewToggleCell
381 // ---------------------------------------------------------
382
383 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleCell, wxDataViewCustomCell)
384
385 wxDataViewToggleCell::wxDataViewToggleCell( const wxString &varianttype,
386 wxDataViewCellMode mode ) :
387 wxDataViewCustomCell( varianttype, mode )
388 {
389 m_toggle = false;
390 }
391
392 bool wxDataViewToggleCell::SetValue( const wxVariant &value )
393 {
394 m_toggle = value.GetBool();
395
396 return true;
397 }
398
399 bool wxDataViewToggleCell::GetValue( wxVariant &WXUNUSED(value) )
400 {
401 return false;
402 }
403
404 bool wxDataViewToggleCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
405 {
406 // User wxRenderer here
407
408 wxRect rect;
409 rect.x = cell.x + cell.width/2 - 10;
410 rect.width = 20;
411 rect.y = cell.y + cell.height/2 - 10;
412 rect.height = 20;
413
414 int flags = 0;
415 if (m_toggle)
416 flags |= wxCONTROL_CHECKED;
417 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
418 flags |= wxCONTROL_DISABLED;
419
420 wxRendererNative::Get().DrawCheckBox(
421 GetOwner()->GetOwner(),
422 *dc,
423 rect,
424 flags );
425
426 return true;
427 }
428
429 bool wxDataViewToggleCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
430 {
431 bool value = !m_toggle;
432 wxVariant variant = value;
433 model->SetValue( variant, col, row );
434 model->ValueChanged( col, row );
435 return true;
436 }
437
438 wxSize wxDataViewToggleCell::GetSize()
439 {
440 return wxSize(20,20);
441 }
442
443 // ---------------------------------------------------------
444 // wxDataViewProgressCell
445 // ---------------------------------------------------------
446
447 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell, wxDataViewCustomCell)
448
449 wxDataViewProgressCell::wxDataViewProgressCell( const wxString &label,
450 const wxString &varianttype, wxDataViewCellMode mode ) :
451 wxDataViewCustomCell( varianttype, mode )
452 {
453 m_label = label;
454 m_value = 0;
455 }
456
457 wxDataViewProgressCell::~wxDataViewProgressCell()
458 {
459 }
460
461 bool wxDataViewProgressCell::SetValue( const wxVariant &value )
462 {
463 m_value = (long) value;
464
465 if (m_value < 0) m_value = 0;
466 if (m_value > 100) m_value = 100;
467
468 return true;
469 }
470
471 bool wxDataViewProgressCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
472 {
473 double pct = (double)m_value / 100.0;
474 wxRect bar = cell;
475 bar.width = (int)(cell.width * pct);
476 dc->SetPen( *wxTRANSPARENT_PEN );
477 dc->SetBrush( *wxBLUE_BRUSH );
478 dc->DrawRectangle( bar );
479
480 dc->SetBrush( *wxTRANSPARENT_BRUSH );
481 dc->SetPen( *wxBLACK_PEN );
482 dc->DrawRectangle( cell );
483
484 return true;
485 }
486
487 wxSize wxDataViewProgressCell::GetSize()
488 {
489 return wxSize(40,12);
490 }
491
492 // ---------------------------------------------------------
493 // wxDataViewDateCell
494 // ---------------------------------------------------------
495
496 class wxDataViewDateCellPopupTransient: public wxPopupTransientWindow
497 {
498 public:
499 wxDataViewDateCellPopupTransient( wxWindow* parent, wxDateTime *value,
500 wxDataViewListModel *model, size_t col, size_t row ) :
501 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
502 {
503 m_model = model;
504 m_col = col;
505 m_row = row;
506 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
507 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
508 sizer->Add( m_cal, 1, wxGROW );
509 SetSizer( sizer );
510 sizer->Fit( this );
511 }
512
513 void OnCalendar( wxCalendarEvent &event );
514
515 wxCalendarCtrl *m_cal;
516 wxDataViewListModel *m_model;
517 size_t m_col;
518 size_t m_row;
519
520 protected:
521 virtual void OnDismiss()
522 {
523 }
524
525 private:
526 DECLARE_EVENT_TABLE()
527 };
528
529 BEGIN_EVENT_TABLE(wxDataViewDateCellPopupTransient,wxPopupTransientWindow)
530 EVT_CALENDAR( wxID_ANY, wxDataViewDateCellPopupTransient::OnCalendar )
531 END_EVENT_TABLE()
532
533 void wxDataViewDateCellPopupTransient::OnCalendar( wxCalendarEvent &event )
534 {
535 wxDateTime date = event.GetDate();
536 wxVariant value = date;
537 m_model->SetValue( value, m_col, m_row );
538 m_model->ValueChanged( m_col, m_row );
539 DismissAndNotify();
540 }
541
542 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateCell, wxDataViewCustomCell)
543
544 wxDataViewDateCell::wxDataViewDateCell( const wxString &varianttype,
545 wxDataViewCellMode mode ) :
546 wxDataViewCustomCell( varianttype, mode )
547 {
548 }
549
550 bool wxDataViewDateCell::SetValue( const wxVariant &value )
551 {
552 m_date = value.GetDateTime();
553
554 return true;
555 }
556
557 bool wxDataViewDateCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
558 {
559 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
560 wxString tmp = m_date.FormatDate();
561 dc->DrawText( tmp, cell.x, cell.y );
562
563 return true;
564 }
565
566 wxSize wxDataViewDateCell::GetSize()
567 {
568 wxDataViewCtrl* view = GetOwner()->GetOwner();
569 wxString tmp = m_date.FormatDate();
570 wxCoord x,y,d;
571 view->GetTextExtent( tmp, &x, &y, &d );
572 return wxSize(x,y+d);
573 }
574
575 bool wxDataViewDateCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
576 {
577 wxVariant variant;
578 model->GetValue( variant, col, row );
579 wxDateTime value = variant.GetDateTime();
580
581 wxDataViewDateCellPopupTransient *popup = new wxDataViewDateCellPopupTransient(
582 GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
583 wxPoint pos = wxGetMousePosition();
584 popup->Move( pos );
585 popup->Layout();
586 popup->Popup( popup->m_cal );
587
588 return true;
589 }
590
591 // ---------------------------------------------------------
592 // wxDataViewColumn
593 // ---------------------------------------------------------
594
595 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
596
597 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewCell *cell, size_t model_column,
598 int fixed_width, wxDataViewColumnSizing sizing, int flags ) :
599 wxDataViewColumnBase( title, cell, model_column, flags )
600 {
601 m_sizing = sizing;
602
603 m_width = fixed_width;
604 m_fixedWidth = fixed_width;
605 }
606
607 wxDataViewColumn::~wxDataViewColumn()
608 {
609 }
610
611 void wxDataViewColumn::SetTitle( const wxString &title )
612 {
613 wxDataViewColumnBase::SetTitle( title );
614
615 }
616
617 int wxDataViewColumn::GetWidth()
618 {
619 return m_width;
620 }
621
622 void wxDataViewColumn::SetFixedWidth( int width )
623 {
624 m_fixedWidth = width;
625
626 if (m_sizing == wxDATAVIEW_COL_WIDTH_FIXED)
627 {
628 m_width = width;
629 // Set dirty
630 }
631 }
632
633 int wxDataViewColumn::GetFixedWidth()
634 {
635 return m_fixedWidth;
636 }
637
638 //-----------------------------------------------------------------------------
639 // wxDataViewHeaderWindow
640 //-----------------------------------------------------------------------------
641
642 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow)
643
644 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow)
645 EVT_PAINT (wxDataViewHeaderWindow::OnPaint)
646 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse)
647 EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus)
648 END_EVENT_TABLE()
649
650 wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id,
651 const wxPoint &pos, const wxSize &size, const wxString &name ) :
652 wxWindow( parent, id, pos, size, 0, name )
653 {
654 SetOwner( parent );
655
656 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
657
658 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
659 SetOwnForegroundColour( attr.colFg );
660 SetOwnBackgroundColour( attr.colBg );
661 if (!m_hasFont)
662 SetOwnFont( attr.font );
663 }
664
665 wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
666 {
667 delete m_resizeCursor;
668 }
669
670 void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
671 {
672 int w, h;
673 GetClientSize( &w, &h );
674
675 wxPaintDC dc( this );
676
677 int xpix;
678 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
679
680 int x;
681 m_owner->GetViewStart( &x, NULL );
682
683 // account for the horz scrollbar offset
684 dc.SetDeviceOrigin( -x * xpix, 0 );
685
686 dc.SetFont( GetFont() );
687
688 size_t cols = GetOwner()->GetNumberOfColumns();
689 size_t i;
690 int xpos = 0;
691 for (i = 0; i < cols; i++)
692 {
693 wxDataViewColumn *col = GetOwner()->GetColumn( i );
694 int width = col->GetWidth();
695
696 int cw = width;
697 int ch = h;
698
699 wxRendererNative::Get().DrawHeaderButton
700 (
701 this,
702 dc,
703 wxRect(xpos, 0, cw, ch-1),
704 m_parent->IsEnabled() ? 0
705 : (int)wxCONTROL_DISABLED
706 );
707
708 dc.DrawText( col->GetTitle(), xpos+3, 3 );
709
710 xpos += width;
711 }
712 }
713
714 void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) )
715 {
716 }
717
718 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
719 {
720 GetParent()->SetFocus();
721 event.Skip();
722 }
723
724 //-----------------------------------------------------------------------------
725 // wxDataViewRenameTimer
726 //-----------------------------------------------------------------------------
727
728 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
729 {
730 m_owner = owner;
731 }
732
733 void wxDataViewRenameTimer::Notify()
734 {
735 m_owner->OnRenameTimer();
736 }
737
738 //-----------------------------------------------------------------------------
739 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
740 //-----------------------------------------------------------------------------
741
742 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler)
743 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar)
744 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp)
745 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus)
746 END_EVENT_TABLE()
747
748 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
749 wxDataViewMainWindow *owner,
750 wxTextCtrl *text,
751 wxDataViewListModel *model,
752 size_t col, size_t row,
753 wxRect rectLabel )
754 {
755 m_owner = owner;
756 m_model = model;
757 m_row = row;
758 m_col = col;
759 m_text = text;
760
761 m_finished = false;
762 m_aboutToFinish = false;
763
764 wxVariant value;
765 model->GetValue( value, col, row );
766 m_startValue = value.GetString();
767
768 m_owner->GetOwner()->CalcScrolledPosition(
769 rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
770
771 m_text->Create( owner, wxID_ANY, m_startValue,
772 wxPoint(rectLabel.x-2,rectLabel.y-2),
773 wxSize(rectLabel.width+7,rectLabel.height+4) );
774 m_text->SetFocus();
775
776 m_text->PushEventHandler(this);
777 }
778
779 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
780 {
781 m_aboutToFinish = true;
782
783 // Notify the owner about the changes
784 AcceptChanges();
785
786 // Even if vetoed, close the control (consistent with MSW)
787 Finish();
788 }
789
790 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent &event )
791 {
792 switch ( event.m_keyCode )
793 {
794 case WXK_RETURN:
795 AcceptChangesAndFinish();
796 break;
797
798 case WXK_ESCAPE:
799 // m_owner->OnRenameCancelled( m_itemEdited );
800 Finish();
801 break;
802
803 default:
804 event.Skip();
805 }
806 }
807
808 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
809 {
810 if (m_finished)
811 {
812 event.Skip();
813 return;
814 }
815
816 // auto-grow the textctrl
817 wxSize parentSize = m_owner->GetSize();
818 wxPoint myPos = m_text->GetPosition();
819 wxSize mySize = m_text->GetSize();
820 int sx, sy;
821 m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
822 if (myPos.x + sx > parentSize.x)
823 sx = parentSize.x - myPos.x;
824 if (mySize.x > sx)
825 sx = mySize.x;
826 m_text->SetSize(sx, wxDefaultCoord);
827
828 event.Skip();
829 }
830
831 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
832 {
833 if ( !m_finished && !m_aboutToFinish )
834 {
835 AcceptChanges();
836 //if ( !AcceptChanges() )
837 // m_owner->OnRenameCancelled( m_itemEdited );
838
839 Finish();
840 }
841
842 // We must let the native text control handle focus
843 event.Skip();
844 }
845
846 bool wxDataViewTextCtrlWrapper::AcceptChanges()
847 {
848 const wxString value = m_text->GetValue();
849
850 if ( value == m_startValue )
851 // nothing changed, always accept
852 return true;
853
854 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
855 // vetoed by the user
856 // return false;
857
858 // accepted, do rename the item
859 wxVariant variant;
860 variant = value;
861 m_model->SetValue( variant, m_col, m_row );
862 m_model->ValueChanged( m_col, m_row );
863
864 return true;
865 }
866
867 void wxDataViewTextCtrlWrapper::Finish()
868 {
869 if ( !m_finished )
870 {
871 m_finished = true;
872
873 m_text->RemoveEventHandler(this);
874 m_owner->FinishEditing(m_text);
875
876 // delete later
877 wxPendingDelete.Append( this );
878 }
879 }
880
881 //-----------------------------------------------------------------------------
882 // wxDataViewMainWindow
883 //-----------------------------------------------------------------------------
884
885 int LINKAGEMODE wxDataViewSelectionCmp( size_t row1, size_t row2 )
886 {
887 if (row1 > row2) return 1;
888 if (row1 == row2) return 0;
889 return -1;
890 }
891
892
893 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
894
895 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
896 EVT_PAINT (wxDataViewMainWindow::OnPaint)
897 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
898 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
899 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
900 EVT_CHAR (wxDataViewMainWindow::OnChar)
901 END_EVENT_TABLE()
902
903 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
904 const wxPoint &pos, const wxSize &size, const wxString &name ) :
905 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
906 m_selection( wxDataViewSelectionCmp )
907
908 {
909 SetOwner( parent );
910
911 m_lastOnSame = false;
912 m_renameTimer = new wxDataViewRenameTimer( this );
913 m_textctrlWrapper = NULL;
914
915 // TODO: user better initial values/nothing selected
916 m_currentCol = NULL;
917 m_currentRow = 0;
918
919 // TODO: we need to calculate this smartly
920 m_lineHeight = 20;
921
922 m_dragCount = 0;
923 m_dragStart = wxPoint(0,0);
924 m_lineLastClicked = (size_t) -1;
925 m_lineBeforeLastClicked = (size_t) -1;
926 m_lineSelectSingleOnUp = (size_t) -1;
927
928 m_hasFocus = false;
929
930 SetBackgroundColour( *wxWHITE );
931
932 UpdateDisplay();
933 }
934
935 wxDataViewMainWindow::~wxDataViewMainWindow()
936 {
937 delete m_renameTimer;
938 }
939
940 void wxDataViewMainWindow::OnRenameTimer()
941 {
942 // We have to call this here because changes may just have
943 // been made and no screen update taken place.
944 if ( m_dirty )
945 wxSafeYield();
946
947
948 int xpos = 0;
949 size_t cols = GetOwner()->GetNumberOfColumns();
950 size_t i;
951 for (i = 0; i < cols; i++)
952 {
953 wxDataViewColumn *c = GetOwner()->GetColumn( i );
954 if (c == m_currentCol)
955 break;
956 xpos += c->GetWidth();
957 }
958 wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight );
959
960 wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
961
962 wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
963 m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
964 m_currentCol->GetModelColumn(), m_currentRow, labelRect );
965 }
966
967 void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text )
968 {
969 delete text;
970 m_textctrlWrapper = NULL;
971 SetFocus();
972 // SetFocusIgnoringChildren();
973 }
974
975 bool wxDataViewMainWindow::RowAppended()
976 {
977 return false;
978 }
979
980 bool wxDataViewMainWindow::RowPrepended()
981 {
982 return false;
983 }
984
985 bool wxDataViewMainWindow::RowInserted( size_t WXUNUSED(before) )
986 {
987 return false;
988 }
989
990 bool wxDataViewMainWindow::RowDeleted( size_t WXUNUSED(row) )
991 {
992 return false;
993 }
994
995 bool wxDataViewMainWindow::RowChanged( size_t WXUNUSED(row) )
996 {
997 return false;
998 }
999
1000 bool wxDataViewMainWindow::ValueChanged( size_t WXUNUSED(col), size_t row )
1001 {
1002 wxRect rect( 0, row*m_lineHeight, 10000, m_lineHeight );
1003 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1004 Refresh( true, &rect );
1005
1006 return true;
1007 }
1008
1009 bool wxDataViewMainWindow::RowsReordered( size_t *WXUNUSED(new_order) )
1010 {
1011 Refresh();
1012
1013 return true;
1014 }
1015
1016 bool wxDataViewMainWindow::Cleared()
1017 {
1018 return false;
1019 }
1020
1021 void wxDataViewMainWindow::UpdateDisplay()
1022 {
1023 m_dirty = true;
1024 }
1025
1026 void wxDataViewMainWindow::OnInternalIdle()
1027 {
1028 wxWindow::OnInternalIdle();
1029
1030 if (m_dirty)
1031 {
1032 RecalculateDisplay();
1033 m_dirty = false;
1034 }
1035 }
1036
1037 void wxDataViewMainWindow::RecalculateDisplay()
1038 {
1039 wxDataViewListModel *model = GetOwner()->GetModel();
1040 if (!model)
1041 {
1042 Refresh();
1043 return;
1044 }
1045
1046 int width = 0;
1047 size_t cols = GetOwner()->GetNumberOfColumns();
1048 size_t i;
1049 for (i = 0; i < cols; i++)
1050 {
1051 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1052 width += col->GetWidth();
1053 }
1054
1055 int height = model->GetNumberOfRows() * m_lineHeight;
1056
1057 SetVirtualSize( width, height );
1058 GetOwner()->SetScrollRate( 10, m_lineHeight );
1059
1060 Refresh();
1061 }
1062
1063 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1064 {
1065 wxWindow::ScrollWindow( dx, dy, rect );
1066 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1067 }
1068
1069 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1070 {
1071 wxPaintDC dc( this );
1072
1073 GetOwner()->PrepareDC( dc );
1074
1075 dc.SetFont( GetFont() );
1076
1077 wxRect update = GetUpdateRegion().GetBox();
1078 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1079
1080 wxDataViewListModel *model = GetOwner()->GetModel();
1081
1082 size_t item_start = wxMax( 0, (update.y / m_lineHeight) );
1083 size_t item_count = wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1084 (int)(model->GetNumberOfRows()-item_start) );
1085
1086
1087
1088 size_t item;
1089 for (item = item_start; item < item_start+item_count; item++)
1090 {
1091 if (m_selection.Index( item ) != wxNOT_FOUND)
1092 {
1093 int flags = wxCONTROL_SELECTED;
1094 if (item == m_currentRow)
1095 flags |= wxCONTROL_CURRENT;
1096 if (m_hasFocus)
1097 flags |= wxCONTROL_FOCUSED;
1098 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1099 wxRendererNative::Get().DrawItemSelectionRect
1100 (
1101 this,
1102 dc,
1103 rect,
1104 flags
1105 );
1106 }
1107 else
1108 {
1109 if (item == m_currentRow)
1110 {
1111 int flags = wxCONTROL_CURRENT;
1112 if (m_hasFocus)
1113 flags |= wxCONTROL_FOCUSED; // should have no effect
1114 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1115 wxRendererNative::Get().DrawItemSelectionRect
1116 (
1117 this,
1118 dc,
1119 rect,
1120 flags
1121 );
1122
1123 }
1124 }
1125 }
1126
1127 wxRect cell_rect;
1128 cell_rect.x = 0;
1129 cell_rect.height = m_lineHeight;
1130 size_t cols = GetOwner()->GetNumberOfColumns();
1131 size_t i;
1132 for (i = 0; i < cols; i++)
1133 {
1134 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1135 wxDataViewCell *cell = col->GetCell();
1136 cell_rect.width = col->GetWidth();
1137
1138 for (item = item_start; item < item_start+item_count; item++)
1139 {
1140 cell_rect.y = item*m_lineHeight;
1141 wxVariant value;
1142 model->GetValue( value, col->GetModelColumn(), item );
1143 cell->SetValue( value );
1144 wxSize size = cell->GetSize();
1145 // cannot be bigger than allocated space
1146 size.x = wxMin( size.x, cell_rect.width );
1147 size.y = wxMin( size.y, cell_rect.height );
1148 // TODO: check for left/right/centre alignment here
1149 wxRect item_rect;
1150 // for now: centre
1151 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1152 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1153
1154 item_rect.width = size.x;
1155 item_rect.height= size.y;
1156 cell->Render( item_rect, &dc, 0 );
1157 }
1158
1159 cell_rect.x += cell_rect.width;
1160 }
1161 }
1162
1163 int wxDataViewMainWindow::GetCountPerPage()
1164 {
1165 wxSize size = GetClientSize();
1166 return size.y / m_lineHeight;
1167 }
1168
1169 int wxDataViewMainWindow::GetEndOfLastCol()
1170 {
1171 int width = 0;
1172 size_t i;
1173 for (i = 0; i < GetOwner()->GetNumberOfColumns(); i++)
1174 {
1175 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1176 width += c->GetWidth();
1177 }
1178 return width;
1179 }
1180
1181 size_t wxDataViewMainWindow::GetFirstVisibleRow()
1182 {
1183 int x = 0;
1184 int y = 0;
1185 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1186
1187 return y / m_lineHeight;
1188 }
1189
1190 size_t wxDataViewMainWindow::GetLastVisibleRow()
1191 {
1192 wxSize client_size = GetClientSize();
1193 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y, &client_size.x, &client_size.y );
1194
1195 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
1196 }
1197
1198 size_t wxDataViewMainWindow::GetRowCount()
1199 {
1200 return GetOwner()->GetModel()->GetNumberOfRows();
1201 }
1202
1203 void wxDataViewMainWindow::ChangeCurrentRow( size_t row )
1204 {
1205 m_currentRow = row;
1206
1207 // send event
1208 }
1209
1210 void wxDataViewMainWindow::SelectAllRows( bool on )
1211 {
1212 if (IsEmpty())
1213 return;
1214
1215 if (on)
1216 {
1217 m_selection.Clear();
1218 for (size_t i = 0; i < GetRowCount(); i++)
1219 m_selection.Add( i );
1220 Refresh();
1221 }
1222 else
1223 {
1224 size_t first_visible = GetFirstVisibleRow();
1225 size_t last_visible = GetLastVisibleRow();
1226 size_t i;
1227 for (i = 0; i < m_selection.GetCount(); i++)
1228 {
1229 size_t row = m_selection[i];
1230 if ((row >= first_visible) && (row <= last_visible))
1231 RefreshRow( row );
1232 }
1233 m_selection.Clear();
1234 }
1235 }
1236
1237 void wxDataViewMainWindow::SelectRow( size_t row, bool on )
1238 {
1239 if (m_selection.Index( row ) == wxNOT_FOUND)
1240 {
1241 if (on)
1242 {
1243 m_selection.Add( row );
1244 RefreshRow( row );
1245 }
1246 }
1247 else
1248 {
1249 if (!on)
1250 {
1251 m_selection.Remove( row );
1252 RefreshRow( row );
1253 }
1254 }
1255 }
1256
1257 void wxDataViewMainWindow::SelectRows( size_t from, size_t to, bool on )
1258 {
1259 if (from > to)
1260 {
1261 size_t tmp = from;
1262 from = to;
1263 to = tmp;
1264 }
1265
1266 size_t i;
1267 for (i = from; i <= to; i++)
1268 {
1269 if (m_selection.Index( i ) == wxNOT_FOUND)
1270 {
1271 if (on)
1272 m_selection.Add( i );
1273 }
1274 else
1275 {
1276 if (!on)
1277 m_selection.Remove( i );
1278 }
1279 }
1280 RefreshRows( from, to );
1281 }
1282
1283 void wxDataViewMainWindow::ReverseRowSelection( size_t row )
1284 {
1285 if (m_selection.Index( row ) == wxNOT_FOUND)
1286 m_selection.Add( row );
1287 else
1288 m_selection.Remove( row );
1289 RefreshRow( row );
1290 }
1291
1292 bool wxDataViewMainWindow::IsRowSelected( size_t row )
1293 {
1294 return (m_selection.Index( row ) != wxNOT_FOUND);
1295 }
1296
1297 void wxDataViewMainWindow::RefreshRow( size_t row )
1298 {
1299 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
1300 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1301
1302 wxSize client_size = GetClientSize();
1303 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1304 wxRect intersect_rect = client_rect.Intersect( rect );
1305 if (intersect_rect.width > 0)
1306 Refresh( true, &intersect_rect );
1307 }
1308
1309 void wxDataViewMainWindow::RefreshRows( size_t from, size_t to )
1310 {
1311 if (from > to)
1312 {
1313 size_t tmp = to;
1314 to = from;
1315 from = tmp;
1316 }
1317
1318 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
1319 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1320
1321 wxSize client_size = GetClientSize();
1322 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1323 wxRect intersect_rect = client_rect.Intersect( rect );
1324 if (intersect_rect.width > 0)
1325 Refresh( true, &intersect_rect );
1326 }
1327
1328 void wxDataViewMainWindow::RefreshRowsAfter( size_t firstRow )
1329 {
1330 size_t count = GetRowCount();
1331 if (firstRow > count)
1332 return;
1333
1334 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
1335 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1336
1337 wxSize client_size = GetClientSize();
1338 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1339 wxRect intersect_rect = client_rect.Intersect( rect );
1340 if (intersect_rect.width > 0)
1341 Refresh( true, &intersect_rect );
1342 }
1343
1344 void wxDataViewMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event)
1345 {
1346 wxCHECK_RET( newCurrent < GetRowCount(),
1347 _T("invalid item index in OnArrowChar()") );
1348
1349 // if there is no selection, we cannot move it anywhere
1350 if (!HasCurrentRow())
1351 return;
1352
1353 size_t oldCurrent = m_currentRow;
1354
1355 // in single selection we just ignore Shift as we can't select several
1356 // items anyhow
1357 if ( event.ShiftDown() && !IsSingleSel() )
1358 {
1359 RefreshRow( oldCurrent );
1360
1361 ChangeCurrentRow( newCurrent );
1362
1363 // select all the items between the old and the new one
1364 if ( oldCurrent > newCurrent )
1365 {
1366 newCurrent = oldCurrent;
1367 oldCurrent = m_currentRow;
1368 }
1369
1370 SelectRows( oldCurrent, newCurrent, true );
1371 }
1372 else // !shift
1373 {
1374 RefreshRow( oldCurrent );
1375
1376 // all previously selected items are unselected unless ctrl is held
1377 if ( !event.ControlDown() )
1378 SelectAllRows(false);
1379
1380 ChangeCurrentRow( newCurrent );
1381
1382 if ( !event.ControlDown() )
1383 SelectRow( m_currentRow, true );
1384 else
1385 RefreshRow( m_currentRow );
1386 }
1387
1388 // MoveToFocus();
1389 }
1390
1391 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
1392 {
1393 if (event.GetKeyCode() == WXK_TAB)
1394 {
1395 wxNavigationKeyEvent nevent;
1396 nevent.SetWindowChange( event.ControlDown() );
1397 nevent.SetDirection( !event.ShiftDown() );
1398 nevent.SetEventObject( GetParent()->GetParent() );
1399 nevent.SetCurrentFocus( m_parent );
1400 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
1401 return;
1402 }
1403
1404 // no item -> nothing to do
1405 if (!HasCurrentRow())
1406 {
1407 event.Skip();
1408 return;
1409 }
1410
1411 // don't use m_linesPerPage directly as it might not be computed yet
1412 const int pageSize = GetCountPerPage();
1413 wxCHECK_RET( pageSize, _T("should have non zero page size") );
1414
1415 switch ( event.GetKeyCode() )
1416 {
1417 case WXK_UP:
1418 if ( m_currentRow > 0 )
1419 OnArrowChar( m_currentRow - 1, event );
1420 break;
1421
1422 case WXK_DOWN:
1423 if ( m_currentRow < GetRowCount() - 1 )
1424 OnArrowChar( m_currentRow + 1, event );
1425 break;
1426
1427 case WXK_END:
1428 if (!IsEmpty())
1429 OnArrowChar( GetRowCount() - 1, event );
1430 break;
1431
1432 case WXK_HOME:
1433 if (!IsEmpty())
1434 OnArrowChar( 0, event );
1435 break;
1436
1437 case WXK_PAGEUP:
1438 {
1439 int steps = pageSize - 1;
1440 int index = m_currentRow - steps;
1441 if (index < 0)
1442 index = 0;
1443
1444 OnArrowChar( index, event );
1445 }
1446 break;
1447
1448 case WXK_PAGEDOWN:
1449 {
1450 int steps = pageSize - 1;
1451 size_t index = m_currentRow + steps;
1452 size_t count = GetRowCount();
1453 if ( index >= count )
1454 index = count - 1;
1455
1456 OnArrowChar( index, event );
1457 }
1458 break;
1459
1460 default:
1461 event.Skip();
1462 }
1463 }
1464
1465 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
1466 {
1467 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
1468 {
1469 // let the base handle mouse wheel events.
1470 event.Skip();
1471 return;
1472 }
1473
1474 int x = event.GetX();
1475 int y = event.GetY();
1476 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1477
1478 wxDataViewColumn *col = NULL;
1479
1480 int xpos = 0;
1481 size_t cols = GetOwner()->GetNumberOfColumns();
1482 size_t i;
1483 for (i = 0; i < cols; i++)
1484 {
1485 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1486 if (x < xpos + c->GetWidth())
1487 {
1488 col = c;
1489 break;
1490 }
1491 xpos += c->GetWidth();
1492 }
1493 if (!col)
1494 return;
1495 wxDataViewCell *cell = col->GetCell();
1496
1497 size_t current = y / m_lineHeight;
1498
1499 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
1500 {
1501 // Unselect all if below the last row ?
1502 return;
1503 }
1504
1505 wxDataViewListModel *model = GetOwner()->GetModel();
1506
1507 if (event.Dragging())
1508 {
1509 if (m_dragCount == 0)
1510 {
1511 // we have to report the raw, physical coords as we want to be
1512 // able to call HitTest(event.m_pointDrag) from the user code to
1513 // get the item being dragged
1514 m_dragStart = event.GetPosition();
1515 }
1516
1517 m_dragCount++;
1518
1519 if (m_dragCount != 3)
1520 return;
1521
1522 if (event.LeftIsDown())
1523 {
1524 // Notify cell about drag
1525 }
1526 return;
1527 }
1528 else
1529 {
1530 m_dragCount = 0;
1531 }
1532
1533 bool forceClick = false;
1534
1535 if (event.ButtonDClick())
1536 {
1537 m_renameTimer->Stop();
1538 m_lastOnSame = false;
1539 }
1540
1541 if (event.LeftDClick())
1542 {
1543 if ( current == m_lineLastClicked )
1544 {
1545 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
1546 {
1547 wxVariant value;
1548 model->GetValue( value, col->GetModelColumn(), current );
1549 cell->SetValue( value );
1550 wxRect cell_rect( xpos, current * m_lineHeight, col->GetWidth(), m_lineHeight );
1551 cell->Activate( cell_rect, model, col->GetModelColumn(), current );
1552 }
1553 return;
1554 }
1555 else
1556 {
1557 // The first click was on another item, so don't interpret this as
1558 // a double click, but as a simple click instead
1559 forceClick = true;
1560 }
1561 }
1562
1563 if (event.LeftUp())
1564 {
1565 if (m_lineSelectSingleOnUp != (size_t)-1)
1566 {
1567 // select single line
1568 SelectAllRows( false );
1569 SelectRow( m_lineSelectSingleOnUp, true );
1570 }
1571
1572 if (m_lastOnSame)
1573 {
1574 if ((col == m_currentCol) && (current == m_currentRow) &&
1575 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
1576 {
1577 m_renameTimer->Start( 100, true );
1578 }
1579 }
1580
1581 m_lastOnSame = false;
1582 m_lineSelectSingleOnUp = (size_t)-1;
1583 }
1584 else
1585 {
1586 // This is necessary, because after a DnD operation in
1587 // from and to ourself, the up event is swallowed by the
1588 // DnD code. So on next non-up event (which means here and
1589 // now) m_lineSelectSingleOnUp should be reset.
1590 m_lineSelectSingleOnUp = (size_t)-1;
1591 }
1592
1593 if (event.RightDown())
1594 {
1595 m_lineBeforeLastClicked = m_lineLastClicked;
1596 m_lineLastClicked = current;
1597
1598 // If the item is already selected, do not update the selection.
1599 // Multi-selections should not be cleared if a selected item is clicked.
1600 if (!IsRowSelected(current))
1601 {
1602 SelectAllRows(false);
1603 ChangeCurrentRow(current);
1604 SelectRow(m_currentRow,true);
1605 }
1606
1607 // notify cell about right click
1608 // cell->...
1609
1610 // Allow generation of context menu event
1611 event.Skip();
1612 }
1613 else if (event.MiddleDown())
1614 {
1615 // notify cell about middle click
1616 // cell->...
1617 }
1618 if (event.LeftDown() || forceClick)
1619 {
1620 #ifdef __WXMSW__
1621 SetFocus();
1622 #endif
1623
1624 m_lineBeforeLastClicked = m_lineLastClicked;
1625 m_lineLastClicked = current;
1626
1627 size_t oldCurrentRow = m_currentRow;
1628 bool oldWasSelected = IsRowSelected(m_currentRow);
1629
1630 bool cmdModifierDown = event.CmdDown();
1631 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
1632 {
1633 if ( IsSingleSel() || !IsRowSelected(current) )
1634 {
1635 SelectAllRows( false );
1636
1637 ChangeCurrentRow(current);
1638
1639 SelectRow(m_currentRow,true);
1640 }
1641 else // multi sel & current is highlighted & no mod keys
1642 {
1643 m_lineSelectSingleOnUp = current;
1644 ChangeCurrentRow(current); // change focus
1645 }
1646 }
1647 else // multi sel & either ctrl or shift is down
1648 {
1649 if (cmdModifierDown)
1650 {
1651 ChangeCurrentRow(current);
1652
1653 ReverseRowSelection(m_currentRow);
1654 }
1655 else if (event.ShiftDown())
1656 {
1657 ChangeCurrentRow(current);
1658
1659 size_t lineFrom = oldCurrentRow,
1660 lineTo = current;
1661
1662 if ( lineTo < lineFrom )
1663 {
1664 lineTo = lineFrom;
1665 lineFrom = m_currentRow;
1666 }
1667
1668 SelectRows(lineFrom, lineTo, true);
1669 }
1670 else // !ctrl, !shift
1671 {
1672 // test in the enclosing if should make it impossible
1673 wxFAIL_MSG( _T("how did we get here?") );
1674 }
1675 }
1676
1677 if (m_currentRow != oldCurrentRow)
1678 RefreshRow( oldCurrentRow );
1679
1680 wxDataViewColumn *oldCurrentCol = m_currentCol;
1681
1682 // Update selection here...
1683 m_currentCol = col;
1684
1685 m_lastOnSame = !forceClick && ((col == oldCurrentCol) && (current == oldCurrentRow)) && oldWasSelected;
1686 }
1687 }
1688
1689 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
1690 {
1691 m_hasFocus = true;
1692
1693 if (HasCurrentRow())
1694 Refresh();
1695
1696 event.Skip();
1697 }
1698
1699 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
1700 {
1701 m_hasFocus = false;
1702
1703 if (HasCurrentRow())
1704 Refresh();
1705
1706 event.Skip();
1707 }
1708
1709 //-----------------------------------------------------------------------------
1710 // wxDataViewCtrl
1711 //-----------------------------------------------------------------------------
1712
1713 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
1714
1715 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
1716 EVT_SIZE(wxDataViewCtrl::OnSize)
1717 END_EVENT_TABLE()
1718
1719 wxDataViewCtrl::~wxDataViewCtrl()
1720 {
1721 if (m_notifier)
1722 GetModel()->RemoveNotifier( m_notifier );
1723 }
1724
1725 void wxDataViewCtrl::Init()
1726 {
1727 m_notifier = NULL;
1728 }
1729
1730 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
1731 const wxPoint& pos, const wxSize& size,
1732 long style, const wxValidator& validator )
1733 {
1734 if (!wxControl::Create( parent, id, pos, size, style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
1735 return false;
1736
1737 Init();
1738
1739 #ifdef __WXMAC__
1740 MacSetClipChildren( true ) ;
1741 #endif
1742
1743 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
1744 #ifdef __WXMSW__
1745 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,22) );
1746 #else
1747 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,25) );
1748 #endif
1749
1750 SetTargetWindow( m_clientArea );
1751
1752 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
1753 sizer->Add( m_headerArea, 0, wxGROW );
1754 sizer->Add( m_clientArea, 1, wxGROW );
1755 SetSizer( sizer );
1756
1757 return true;
1758 }
1759
1760 #ifdef __WXMSW__
1761 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
1762 WXWPARAM wParam,
1763 WXLPARAM lParam)
1764 {
1765 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
1766
1767 #ifndef __WXWINCE__
1768 // we need to process arrows ourselves for scrolling
1769 if ( nMsg == WM_GETDLGCODE )
1770 {
1771 rc |= DLGC_WANTARROWS;
1772 }
1773 #endif
1774
1775 return rc;
1776 }
1777 #endif
1778
1779 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
1780 {
1781 // We need to override OnSize so that our scrolled
1782 // window a) does call Layout() to use sizers for
1783 // positioning the controls but b) does not query
1784 // the sizer for their size and use that for setting
1785 // the scrollable area as set that ourselves by
1786 // calling SetScrollbar() further down.
1787
1788 Layout();
1789
1790 AdjustScrollbars();
1791 }
1792
1793 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
1794 {
1795 if (!wxDataViewCtrlBase::AssociateModel( model ))
1796 return false;
1797
1798 m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
1799
1800 model->AddNotifier( m_notifier );
1801
1802 m_clientArea->UpdateDisplay();
1803
1804 return true;
1805 }
1806
1807 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
1808 {
1809 if (!wxDataViewCtrlBase::AppendColumn(col))
1810 return false;
1811
1812 m_clientArea->UpdateDisplay();
1813
1814 return true;
1815 }
1816
1817 #endif
1818 // !wxUSE_GENERICDATAVIEWCTRL
1819
1820 #endif
1821 // wxUSE_DATAVIEWCTRL