Added selection API.
[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 unsigned int col, unsigned int 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 unsigned int m_col;
122 unsigned int 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_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection, WXDLLIMPEXP_ADV);
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( unsigned int before );
149 bool RowDeleted( unsigned int row );
150 bool RowChanged( unsigned int row );
151 bool ValueChanged( unsigned int col, unsigned int row );
152 bool RowsReordered( unsigned int *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(unsigned int 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 != (unsigned int)-1; }
175 void ChangeCurrentRow( unsigned int 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 unsigned int GetFirstVisibleRow();
183 unsigned int GetLastVisibleRow();
184 unsigned int GetRowCount();
185
186 void SelectAllRows( bool on );
187 void SelectRow( unsigned int row, bool on );
188 void SelectRows( unsigned int from, unsigned int to, bool on );
189 void ReverseRowSelection( unsigned int row );
190 bool IsRowSelected( unsigned int row );
191
192 void RefreshRow( unsigned int row );
193 void RefreshRows( unsigned int from, unsigned int to );
194 void RefreshRowsAfter( unsigned int firstRow );
195
196 private:
197 wxDataViewCtrl *m_owner;
198 int m_lineHeight;
199 bool m_dirty;
200
201 wxDataViewColumn *m_currentCol;
202 unsigned int 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 unsigned int 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( unsigned int before )
239 { return m_mainWindow->RowInserted( before ); }
240 virtual bool RowDeleted( unsigned int row )
241 { return m_mainWindow->RowDeleted( row ); }
242 virtual bool RowChanged( unsigned int row )
243 { return m_mainWindow->RowChanged( row ); }
244 virtual bool ValueChanged( unsigned int col, unsigned int row )
245 { return m_mainWindow->ValueChanged( col, row ); }
246 virtual bool RowsReordered( unsigned int *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, unsigned int col, unsigned int 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, unsigned int col, unsigned int 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 unsigned int m_col;
518 unsigned int 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, unsigned int col, unsigned int 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, unsigned int model_column,
598 int width, int flags ) :
599 wxDataViewColumnBase( title, cell, model_column, width, flags )
600 {
601 m_width = width;
602 if (m_width < 0)
603 m_width = 80;
604 }
605
606 wxDataViewColumn::~wxDataViewColumn()
607 {
608 }
609
610 void wxDataViewColumn::SetTitle( const wxString &title )
611 {
612 wxDataViewColumnBase::SetTitle( title );
613
614 }
615
616 int wxDataViewColumn::GetWidth()
617 {
618 return m_width;
619 }
620
621 //-----------------------------------------------------------------------------
622 // wxDataViewHeaderWindow
623 //-----------------------------------------------------------------------------
624
625 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow)
626
627 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow)
628 EVT_PAINT (wxDataViewHeaderWindow::OnPaint)
629 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse)
630 EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus)
631 END_EVENT_TABLE()
632
633 wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id,
634 const wxPoint &pos, const wxSize &size, const wxString &name ) :
635 wxWindow( parent, id, pos, size, 0, name )
636 {
637 SetOwner( parent );
638
639 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
640
641 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
642 SetOwnForegroundColour( attr.colFg );
643 SetOwnBackgroundColour( attr.colBg );
644 if (!m_hasFont)
645 SetOwnFont( attr.font );
646 }
647
648 wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
649 {
650 delete m_resizeCursor;
651 }
652
653 void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
654 {
655 int w, h;
656 GetClientSize( &w, &h );
657
658 wxPaintDC dc( this );
659
660 int xpix;
661 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
662
663 int x;
664 m_owner->GetViewStart( &x, NULL );
665
666 // account for the horz scrollbar offset
667 dc.SetDeviceOrigin( -x * xpix, 0 );
668
669 dc.SetFont( GetFont() );
670
671 unsigned int cols = GetOwner()->GetNumberOfColumns();
672 unsigned int i;
673 int xpos = 0;
674 for (i = 0; i < cols; i++)
675 {
676 wxDataViewColumn *col = GetOwner()->GetColumn( i );
677 int width = col->GetWidth();
678
679 int cw = width;
680 int ch = h;
681
682 wxRendererNative::Get().DrawHeaderButton
683 (
684 this,
685 dc,
686 wxRect(xpos, 0, cw, ch-1),
687 m_parent->IsEnabled() ? 0
688 : (int)wxCONTROL_DISABLED
689 );
690
691 dc.DrawText( col->GetTitle(), xpos+3, 3 );
692
693 xpos += width;
694 }
695 }
696
697 void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) )
698 {
699 }
700
701 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
702 {
703 GetParent()->SetFocus();
704 event.Skip();
705 }
706
707 //-----------------------------------------------------------------------------
708 // wxDataViewRenameTimer
709 //-----------------------------------------------------------------------------
710
711 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
712 {
713 m_owner = owner;
714 }
715
716 void wxDataViewRenameTimer::Notify()
717 {
718 m_owner->OnRenameTimer();
719 }
720
721 //-----------------------------------------------------------------------------
722 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
723 //-----------------------------------------------------------------------------
724
725 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler)
726 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar)
727 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp)
728 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus)
729 END_EVENT_TABLE()
730
731 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
732 wxDataViewMainWindow *owner,
733 wxTextCtrl *text,
734 wxDataViewListModel *model,
735 unsigned int col, unsigned int row,
736 wxRect rectLabel )
737 {
738 m_owner = owner;
739 m_model = model;
740 m_row = row;
741 m_col = col;
742 m_text = text;
743
744 m_finished = false;
745 m_aboutToFinish = false;
746
747 wxVariant value;
748 model->GetValue( value, col, row );
749 m_startValue = value.GetString();
750
751 m_owner->GetOwner()->CalcScrolledPosition(
752 rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
753
754 m_text->Create( owner, wxID_ANY, m_startValue,
755 wxPoint(rectLabel.x-2,rectLabel.y-2),
756 wxSize(rectLabel.width+7,rectLabel.height+4) );
757 m_text->SetFocus();
758
759 m_text->PushEventHandler(this);
760 }
761
762 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
763 {
764 m_aboutToFinish = true;
765
766 // Notify the owner about the changes
767 AcceptChanges();
768
769 // Even if vetoed, close the control (consistent with MSW)
770 Finish();
771 }
772
773 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent &event )
774 {
775 switch ( event.m_keyCode )
776 {
777 case WXK_RETURN:
778 AcceptChangesAndFinish();
779 break;
780
781 case WXK_ESCAPE:
782 // m_owner->OnRenameCancelled( m_itemEdited );
783 Finish();
784 break;
785
786 default:
787 event.Skip();
788 }
789 }
790
791 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
792 {
793 if (m_finished)
794 {
795 event.Skip();
796 return;
797 }
798
799 // auto-grow the textctrl
800 wxSize parentSize = m_owner->GetSize();
801 wxPoint myPos = m_text->GetPosition();
802 wxSize mySize = m_text->GetSize();
803 int sx, sy;
804 m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
805 if (myPos.x + sx > parentSize.x)
806 sx = parentSize.x - myPos.x;
807 if (mySize.x > sx)
808 sx = mySize.x;
809 m_text->SetSize(sx, wxDefaultCoord);
810
811 event.Skip();
812 }
813
814 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
815 {
816 if ( !m_finished && !m_aboutToFinish )
817 {
818 AcceptChanges();
819 //if ( !AcceptChanges() )
820 // m_owner->OnRenameCancelled( m_itemEdited );
821
822 Finish();
823 }
824
825 // We must let the native text control handle focus
826 event.Skip();
827 }
828
829 bool wxDataViewTextCtrlWrapper::AcceptChanges()
830 {
831 const wxString value = m_text->GetValue();
832
833 if ( value == m_startValue )
834 // nothing changed, always accept
835 return true;
836
837 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
838 // vetoed by the user
839 // return false;
840
841 // accepted, do rename the item
842 wxVariant variant;
843 variant = value;
844 m_model->SetValue( variant, m_col, m_row );
845 m_model->ValueChanged( m_col, m_row );
846
847 return true;
848 }
849
850 void wxDataViewTextCtrlWrapper::Finish()
851 {
852 if ( !m_finished )
853 {
854 m_finished = true;
855
856 m_text->RemoveEventHandler(this);
857 m_owner->FinishEditing(m_text);
858
859 // delete later
860 wxPendingDelete.Append( this );
861 }
862 }
863
864 //-----------------------------------------------------------------------------
865 // wxDataViewMainWindow
866 //-----------------------------------------------------------------------------
867
868 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
869 {
870 if (row1 > row2) return 1;
871 if (row1 == row2) return 0;
872 return -1;
873 }
874
875
876 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
877
878 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
879 EVT_PAINT (wxDataViewMainWindow::OnPaint)
880 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
881 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
882 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
883 EVT_CHAR (wxDataViewMainWindow::OnChar)
884 END_EVENT_TABLE()
885
886 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
887 const wxPoint &pos, const wxSize &size, const wxString &name ) :
888 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
889 m_selection( wxDataViewSelectionCmp )
890
891 {
892 SetOwner( parent );
893
894 m_lastOnSame = false;
895 m_renameTimer = new wxDataViewRenameTimer( this );
896 m_textctrlWrapper = NULL;
897
898 // TODO: user better initial values/nothing selected
899 m_currentCol = NULL;
900 m_currentRow = 0;
901
902 // TODO: we need to calculate this smartly
903 m_lineHeight = 20;
904
905 m_dragCount = 0;
906 m_dragStart = wxPoint(0,0);
907 m_lineLastClicked = (unsigned int) -1;
908 m_lineBeforeLastClicked = (unsigned int) -1;
909 m_lineSelectSingleOnUp = (unsigned int) -1;
910
911 m_hasFocus = false;
912
913 SetBackgroundColour( *wxWHITE );
914
915 UpdateDisplay();
916 }
917
918 wxDataViewMainWindow::~wxDataViewMainWindow()
919 {
920 delete m_renameTimer;
921 }
922
923 void wxDataViewMainWindow::OnRenameTimer()
924 {
925 // We have to call this here because changes may just have
926 // been made and no screen update taken place.
927 if ( m_dirty )
928 wxSafeYield();
929
930
931 int xpos = 0;
932 unsigned int cols = GetOwner()->GetNumberOfColumns();
933 unsigned int i;
934 for (i = 0; i < cols; i++)
935 {
936 wxDataViewColumn *c = GetOwner()->GetColumn( i );
937 if (c == m_currentCol)
938 break;
939 xpos += c->GetWidth();
940 }
941 wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight );
942
943 wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
944
945 wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
946 m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
947 m_currentCol->GetModelColumn(), m_currentRow, labelRect );
948 }
949
950 void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text )
951 {
952 delete text;
953 m_textctrlWrapper = NULL;
954 SetFocus();
955 // SetFocusIgnoringChildren();
956 }
957
958 bool wxDataViewMainWindow::RowAppended()
959 {
960 return false;
961 }
962
963 bool wxDataViewMainWindow::RowPrepended()
964 {
965 return false;
966 }
967
968 bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before) )
969 {
970 return false;
971 }
972
973 bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row) )
974 {
975 return false;
976 }
977
978 bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row) )
979 {
980 return false;
981 }
982
983 bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col), unsigned int row )
984 {
985 wxRect rect( 0, row*m_lineHeight, 10000, m_lineHeight );
986 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
987 Refresh( true, &rect );
988
989 return true;
990 }
991
992 bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order) )
993 {
994 Refresh();
995
996 return true;
997 }
998
999 bool wxDataViewMainWindow::Cleared()
1000 {
1001 return false;
1002 }
1003
1004 void wxDataViewMainWindow::UpdateDisplay()
1005 {
1006 m_dirty = true;
1007 }
1008
1009 void wxDataViewMainWindow::OnInternalIdle()
1010 {
1011 wxWindow::OnInternalIdle();
1012
1013 if (m_dirty)
1014 {
1015 RecalculateDisplay();
1016 m_dirty = false;
1017 }
1018 }
1019
1020 void wxDataViewMainWindow::RecalculateDisplay()
1021 {
1022 wxDataViewListModel *model = GetOwner()->GetModel();
1023 if (!model)
1024 {
1025 Refresh();
1026 return;
1027 }
1028
1029 int width = 0;
1030 unsigned int cols = GetOwner()->GetNumberOfColumns();
1031 unsigned int i;
1032 for (i = 0; i < cols; i++)
1033 {
1034 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1035 width += col->GetWidth();
1036 }
1037
1038 int height = model->GetNumberOfRows() * m_lineHeight;
1039
1040 SetVirtualSize( width, height );
1041 GetOwner()->SetScrollRate( 10, m_lineHeight );
1042
1043 Refresh();
1044 }
1045
1046 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1047 {
1048 wxWindow::ScrollWindow( dx, dy, rect );
1049 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1050 }
1051
1052 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1053 {
1054 wxPaintDC dc( this );
1055
1056 GetOwner()->PrepareDC( dc );
1057
1058 dc.SetFont( GetFont() );
1059
1060 wxRect update = GetUpdateRegion().GetBox();
1061 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1062
1063 wxDataViewListModel *model = GetOwner()->GetModel();
1064
1065 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
1066 unsigned int item_count = wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1067 (int)(model->GetNumberOfRows()-item_start) );
1068
1069
1070
1071 unsigned int item;
1072 for (item = item_start; item < item_start+item_count; item++)
1073 {
1074 if (m_selection.Index( item ) != wxNOT_FOUND)
1075 {
1076 int flags = wxCONTROL_SELECTED;
1077 if (item == m_currentRow)
1078 flags |= wxCONTROL_CURRENT;
1079 if (m_hasFocus)
1080 flags |= wxCONTROL_FOCUSED;
1081 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1082 wxRendererNative::Get().DrawItemSelectionRect
1083 (
1084 this,
1085 dc,
1086 rect,
1087 flags
1088 );
1089 }
1090 else
1091 {
1092 if (item == m_currentRow)
1093 {
1094 int flags = wxCONTROL_CURRENT;
1095 if (m_hasFocus)
1096 flags |= wxCONTROL_FOCUSED; // should have no effect
1097 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1098 wxRendererNative::Get().DrawItemSelectionRect
1099 (
1100 this,
1101 dc,
1102 rect,
1103 flags
1104 );
1105
1106 }
1107 }
1108 }
1109
1110 wxRect cell_rect;
1111 cell_rect.x = 0;
1112 cell_rect.height = m_lineHeight;
1113 unsigned int cols = GetOwner()->GetNumberOfColumns();
1114 unsigned int i;
1115 for (i = 0; i < cols; i++)
1116 {
1117 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1118 wxDataViewCell *cell = col->GetCell();
1119 cell_rect.width = col->GetWidth();
1120
1121 for (item = item_start; item < item_start+item_count; item++)
1122 {
1123 cell_rect.y = item*m_lineHeight;
1124 wxVariant value;
1125 model->GetValue( value, col->GetModelColumn(), item );
1126 cell->SetValue( value );
1127 wxSize size = cell->GetSize();
1128 // cannot be bigger than allocated space
1129 size.x = wxMin( size.x, cell_rect.width );
1130 size.y = wxMin( size.y, cell_rect.height );
1131 // TODO: check for left/right/centre alignment here
1132 wxRect item_rect;
1133 // for now: centre
1134 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1135 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1136
1137 item_rect.width = size.x;
1138 item_rect.height= size.y;
1139 cell->Render( item_rect, &dc, 0 );
1140 }
1141
1142 cell_rect.x += cell_rect.width;
1143 }
1144 }
1145
1146 int wxDataViewMainWindow::GetCountPerPage()
1147 {
1148 wxSize size = GetClientSize();
1149 return size.y / m_lineHeight;
1150 }
1151
1152 int wxDataViewMainWindow::GetEndOfLastCol()
1153 {
1154 int width = 0;
1155 unsigned int i;
1156 for (i = 0; i < GetOwner()->GetNumberOfColumns(); i++)
1157 {
1158 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1159 width += c->GetWidth();
1160 }
1161 return width;
1162 }
1163
1164 unsigned int wxDataViewMainWindow::GetFirstVisibleRow()
1165 {
1166 int x = 0;
1167 int y = 0;
1168 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1169
1170 return y / m_lineHeight;
1171 }
1172
1173 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
1174 {
1175 wxSize client_size = GetClientSize();
1176 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y, &client_size.x, &client_size.y );
1177
1178 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
1179 }
1180
1181 unsigned int wxDataViewMainWindow::GetRowCount()
1182 {
1183 return GetOwner()->GetModel()->GetNumberOfRows();
1184 }
1185
1186 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
1187 {
1188 m_currentRow = row;
1189
1190 // send event
1191 }
1192
1193 void wxDataViewMainWindow::SelectAllRows( bool on )
1194 {
1195 if (IsEmpty())
1196 return;
1197
1198 if (on)
1199 {
1200 m_selection.Clear();
1201 for (unsigned int i = 0; i < GetRowCount(); i++)
1202 m_selection.Add( i );
1203 Refresh();
1204 }
1205 else
1206 {
1207 unsigned int first_visible = GetFirstVisibleRow();
1208 unsigned int last_visible = GetLastVisibleRow();
1209 unsigned int i;
1210 for (i = 0; i < m_selection.GetCount(); i++)
1211 {
1212 unsigned int row = m_selection[i];
1213 if ((row >= first_visible) && (row <= last_visible))
1214 RefreshRow( row );
1215 }
1216 m_selection.Clear();
1217 }
1218 }
1219
1220 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
1221 {
1222 if (m_selection.Index( row ) == wxNOT_FOUND)
1223 {
1224 if (on)
1225 {
1226 m_selection.Add( row );
1227 RefreshRow( row );
1228 }
1229 }
1230 else
1231 {
1232 if (!on)
1233 {
1234 m_selection.Remove( row );
1235 RefreshRow( row );
1236 }
1237 }
1238 }
1239
1240 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
1241 {
1242 if (from > to)
1243 {
1244 unsigned int tmp = from;
1245 from = to;
1246 to = tmp;
1247 }
1248
1249 unsigned int i;
1250 for (i = from; i <= to; i++)
1251 {
1252 if (m_selection.Index( i ) == wxNOT_FOUND)
1253 {
1254 if (on)
1255 m_selection.Add( i );
1256 }
1257 else
1258 {
1259 if (!on)
1260 m_selection.Remove( i );
1261 }
1262 }
1263 RefreshRows( from, to );
1264 }
1265
1266 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
1267 {
1268 if (m_selection.Index( row ) == wxNOT_FOUND)
1269 m_selection.Add( row );
1270 else
1271 m_selection.Remove( row );
1272 RefreshRow( row );
1273 }
1274
1275 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
1276 {
1277 return (m_selection.Index( row ) != wxNOT_FOUND);
1278 }
1279
1280 void wxDataViewMainWindow::RefreshRow( unsigned int row )
1281 {
1282 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
1283 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1284
1285 wxSize client_size = GetClientSize();
1286 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1287 wxRect intersect_rect = client_rect.Intersect( rect );
1288 if (intersect_rect.width > 0)
1289 Refresh( true, &intersect_rect );
1290 }
1291
1292 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
1293 {
1294 if (from > to)
1295 {
1296 unsigned int tmp = to;
1297 to = from;
1298 from = tmp;
1299 }
1300
1301 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
1302 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1303
1304 wxSize client_size = GetClientSize();
1305 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1306 wxRect intersect_rect = client_rect.Intersect( rect );
1307 if (intersect_rect.width > 0)
1308 Refresh( true, &intersect_rect );
1309 }
1310
1311 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
1312 {
1313 unsigned int count = GetRowCount();
1314 if (firstRow > count)
1315 return;
1316
1317 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
1318 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1319
1320 wxSize client_size = GetClientSize();
1321 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1322 wxRect intersect_rect = client_rect.Intersect( rect );
1323 if (intersect_rect.width > 0)
1324 Refresh( true, &intersect_rect );
1325 }
1326
1327 void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
1328 {
1329 wxCHECK_RET( newCurrent < GetRowCount(),
1330 _T("invalid item index in OnArrowChar()") );
1331
1332 // if there is no selection, we cannot move it anywhere
1333 if (!HasCurrentRow())
1334 return;
1335
1336 unsigned int oldCurrent = m_currentRow;
1337
1338 // in single selection we just ignore Shift as we can't select several
1339 // items anyhow
1340 if ( event.ShiftDown() && !IsSingleSel() )
1341 {
1342 RefreshRow( oldCurrent );
1343
1344 ChangeCurrentRow( newCurrent );
1345
1346 // select all the items between the old and the new one
1347 if ( oldCurrent > newCurrent )
1348 {
1349 newCurrent = oldCurrent;
1350 oldCurrent = m_currentRow;
1351 }
1352
1353 SelectRows( oldCurrent, newCurrent, true );
1354 }
1355 else // !shift
1356 {
1357 RefreshRow( oldCurrent );
1358
1359 // all previously selected items are unselected unless ctrl is held
1360 if ( !event.ControlDown() )
1361 SelectAllRows(false);
1362
1363 ChangeCurrentRow( newCurrent );
1364
1365 if ( !event.ControlDown() )
1366 SelectRow( m_currentRow, true );
1367 else
1368 RefreshRow( m_currentRow );
1369 }
1370
1371 // MoveToFocus();
1372 }
1373
1374 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
1375 {
1376 if (event.GetKeyCode() == WXK_TAB)
1377 {
1378 wxNavigationKeyEvent nevent;
1379 nevent.SetWindowChange( event.ControlDown() );
1380 nevent.SetDirection( !event.ShiftDown() );
1381 nevent.SetEventObject( GetParent()->GetParent() );
1382 nevent.SetCurrentFocus( m_parent );
1383 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
1384 return;
1385 }
1386
1387 // no item -> nothing to do
1388 if (!HasCurrentRow())
1389 {
1390 event.Skip();
1391 return;
1392 }
1393
1394 // don't use m_linesPerPage directly as it might not be computed yet
1395 const int pageSize = GetCountPerPage();
1396 wxCHECK_RET( pageSize, _T("should have non zero page size") );
1397
1398 switch ( event.GetKeyCode() )
1399 {
1400 case WXK_UP:
1401 if ( m_currentRow > 0 )
1402 OnArrowChar( m_currentRow - 1, event );
1403 break;
1404
1405 case WXK_DOWN:
1406 if ( m_currentRow < GetRowCount() - 1 )
1407 OnArrowChar( m_currentRow + 1, event );
1408 break;
1409
1410 case WXK_END:
1411 if (!IsEmpty())
1412 OnArrowChar( GetRowCount() - 1, event );
1413 break;
1414
1415 case WXK_HOME:
1416 if (!IsEmpty())
1417 OnArrowChar( 0, event );
1418 break;
1419
1420 case WXK_PAGEUP:
1421 {
1422 int steps = pageSize - 1;
1423 int index = m_currentRow - steps;
1424 if (index < 0)
1425 index = 0;
1426
1427 OnArrowChar( index, event );
1428 }
1429 break;
1430
1431 case WXK_PAGEDOWN:
1432 {
1433 int steps = pageSize - 1;
1434 unsigned int index = m_currentRow + steps;
1435 unsigned int count = GetRowCount();
1436 if ( index >= count )
1437 index = count - 1;
1438
1439 OnArrowChar( index, event );
1440 }
1441 break;
1442
1443 default:
1444 event.Skip();
1445 }
1446 }
1447
1448 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
1449 {
1450 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
1451 {
1452 // let the base handle mouse wheel events.
1453 event.Skip();
1454 return;
1455 }
1456
1457 int x = event.GetX();
1458 int y = event.GetY();
1459 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1460
1461 wxDataViewColumn *col = NULL;
1462
1463 int xpos = 0;
1464 unsigned int cols = GetOwner()->GetNumberOfColumns();
1465 unsigned int i;
1466 for (i = 0; i < cols; i++)
1467 {
1468 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1469 if (x < xpos + c->GetWidth())
1470 {
1471 col = c;
1472 break;
1473 }
1474 xpos += c->GetWidth();
1475 }
1476 if (!col)
1477 return;
1478 wxDataViewCell *cell = col->GetCell();
1479
1480 unsigned int current = y / m_lineHeight;
1481
1482 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
1483 {
1484 // Unselect all if below the last row ?
1485 return;
1486 }
1487
1488 wxDataViewListModel *model = GetOwner()->GetModel();
1489
1490 if (event.Dragging())
1491 {
1492 if (m_dragCount == 0)
1493 {
1494 // we have to report the raw, physical coords as we want to be
1495 // able to call HitTest(event.m_pointDrag) from the user code to
1496 // get the item being dragged
1497 m_dragStart = event.GetPosition();
1498 }
1499
1500 m_dragCount++;
1501
1502 if (m_dragCount != 3)
1503 return;
1504
1505 if (event.LeftIsDown())
1506 {
1507 // Notify cell about drag
1508 }
1509 return;
1510 }
1511 else
1512 {
1513 m_dragCount = 0;
1514 }
1515
1516 bool forceClick = false;
1517
1518 if (event.ButtonDClick())
1519 {
1520 m_renameTimer->Stop();
1521 m_lastOnSame = false;
1522 }
1523
1524 if (event.LeftDClick())
1525 {
1526 if ( current == m_lineLastClicked )
1527 {
1528 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
1529 {
1530 wxVariant value;
1531 model->GetValue( value, col->GetModelColumn(), current );
1532 cell->SetValue( value );
1533 wxRect cell_rect( xpos, current * m_lineHeight, col->GetWidth(), m_lineHeight );
1534 cell->Activate( cell_rect, model, col->GetModelColumn(), current );
1535 }
1536 return;
1537 }
1538 else
1539 {
1540 // The first click was on another item, so don't interpret this as
1541 // a double click, but as a simple click instead
1542 forceClick = true;
1543 }
1544 }
1545
1546 if (event.LeftUp())
1547 {
1548 if (m_lineSelectSingleOnUp != (unsigned int)-1)
1549 {
1550 // select single line
1551 SelectAllRows( false );
1552 SelectRow( m_lineSelectSingleOnUp, true );
1553 }
1554
1555 if (m_lastOnSame)
1556 {
1557 if ((col == m_currentCol) && (current == m_currentRow) &&
1558 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
1559 {
1560 m_renameTimer->Start( 100, true );
1561 }
1562 }
1563
1564 m_lastOnSame = false;
1565 m_lineSelectSingleOnUp = (unsigned int)-1;
1566 }
1567 else
1568 {
1569 // This is necessary, because after a DnD operation in
1570 // from and to ourself, the up event is swallowed by the
1571 // DnD code. So on next non-up event (which means here and
1572 // now) m_lineSelectSingleOnUp should be reset.
1573 m_lineSelectSingleOnUp = (unsigned int)-1;
1574 }
1575
1576 if (event.RightDown())
1577 {
1578 m_lineBeforeLastClicked = m_lineLastClicked;
1579 m_lineLastClicked = current;
1580
1581 // If the item is already selected, do not update the selection.
1582 // Multi-selections should not be cleared if a selected item is clicked.
1583 if (!IsRowSelected(current))
1584 {
1585 SelectAllRows(false);
1586 ChangeCurrentRow(current);
1587 SelectRow(m_currentRow,true);
1588 }
1589
1590 // notify cell about right click
1591 // cell->...
1592
1593 // Allow generation of context menu event
1594 event.Skip();
1595 }
1596 else if (event.MiddleDown())
1597 {
1598 // notify cell about middle click
1599 // cell->...
1600 }
1601 if (event.LeftDown() || forceClick)
1602 {
1603 #ifdef __WXMSW__
1604 SetFocus();
1605 #endif
1606
1607 m_lineBeforeLastClicked = m_lineLastClicked;
1608 m_lineLastClicked = current;
1609
1610 unsigned int oldCurrentRow = m_currentRow;
1611 bool oldWasSelected = IsRowSelected(m_currentRow);
1612
1613 bool cmdModifierDown = event.CmdDown();
1614 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
1615 {
1616 if ( IsSingleSel() || !IsRowSelected(current) )
1617 {
1618 SelectAllRows( false );
1619
1620 ChangeCurrentRow(current);
1621
1622 SelectRow(m_currentRow,true);
1623 }
1624 else // multi sel & current is highlighted & no mod keys
1625 {
1626 m_lineSelectSingleOnUp = current;
1627 ChangeCurrentRow(current); // change focus
1628 }
1629 }
1630 else // multi sel & either ctrl or shift is down
1631 {
1632 if (cmdModifierDown)
1633 {
1634 ChangeCurrentRow(current);
1635
1636 ReverseRowSelection(m_currentRow);
1637 }
1638 else if (event.ShiftDown())
1639 {
1640 ChangeCurrentRow(current);
1641
1642 unsigned int lineFrom = oldCurrentRow,
1643 lineTo = current;
1644
1645 if ( lineTo < lineFrom )
1646 {
1647 lineTo = lineFrom;
1648 lineFrom = m_currentRow;
1649 }
1650
1651 SelectRows(lineFrom, lineTo, true);
1652 }
1653 else // !ctrl, !shift
1654 {
1655 // test in the enclosing if should make it impossible
1656 wxFAIL_MSG( _T("how did we get here?") );
1657 }
1658 }
1659
1660 if (m_currentRow != oldCurrentRow)
1661 RefreshRow( oldCurrentRow );
1662
1663 wxDataViewColumn *oldCurrentCol = m_currentCol;
1664
1665 // Update selection here...
1666 m_currentCol = col;
1667
1668 m_lastOnSame = !forceClick && ((col == oldCurrentCol) && (current == oldCurrentRow)) && oldWasSelected;
1669 }
1670 }
1671
1672 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
1673 {
1674 m_hasFocus = true;
1675
1676 if (HasCurrentRow())
1677 Refresh();
1678
1679 event.Skip();
1680 }
1681
1682 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
1683 {
1684 m_hasFocus = false;
1685
1686 if (HasCurrentRow())
1687 Refresh();
1688
1689 event.Skip();
1690 }
1691
1692 //-----------------------------------------------------------------------------
1693 // wxDataViewCtrl
1694 //-----------------------------------------------------------------------------
1695
1696 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
1697
1698 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
1699 EVT_SIZE(wxDataViewCtrl::OnSize)
1700 END_EVENT_TABLE()
1701
1702 wxDataViewCtrl::~wxDataViewCtrl()
1703 {
1704 if (m_notifier)
1705 GetModel()->RemoveNotifier( m_notifier );
1706 }
1707
1708 void wxDataViewCtrl::Init()
1709 {
1710 m_notifier = NULL;
1711 }
1712
1713 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
1714 const wxPoint& pos, const wxSize& size,
1715 long style, const wxValidator& validator )
1716 {
1717 if (!wxControl::Create( parent, id, pos, size, style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
1718 return false;
1719
1720 Init();
1721
1722 #ifdef __WXMAC__
1723 MacSetClipChildren( true ) ;
1724 #endif
1725
1726 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
1727 #ifdef __WXMSW__
1728 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,22) );
1729 #else
1730 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,25) );
1731 #endif
1732
1733 SetTargetWindow( m_clientArea );
1734
1735 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
1736 sizer->Add( m_headerArea, 0, wxGROW );
1737 sizer->Add( m_clientArea, 1, wxGROW );
1738 SetSizer( sizer );
1739
1740 return true;
1741 }
1742
1743 #ifdef __WXMSW__
1744 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
1745 WXWPARAM wParam,
1746 WXLPARAM lParam)
1747 {
1748 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
1749
1750 #ifndef __WXWINCE__
1751 // we need to process arrows ourselves for scrolling
1752 if ( nMsg == WM_GETDLGCODE )
1753 {
1754 rc |= DLGC_WANTARROWS;
1755 }
1756 #endif
1757
1758 return rc;
1759 }
1760 #endif
1761
1762 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
1763 {
1764 // We need to override OnSize so that our scrolled
1765 // window a) does call Layout() to use sizers for
1766 // positioning the controls but b) does not query
1767 // the sizer for their size and use that for setting
1768 // the scrollable area as set that ourselves by
1769 // calling SetScrollbar() further down.
1770
1771 Layout();
1772
1773 AdjustScrollbars();
1774 }
1775
1776 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
1777 {
1778 if (!wxDataViewCtrlBase::AssociateModel( model ))
1779 return false;
1780
1781 m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
1782
1783 model->AddNotifier( m_notifier );
1784
1785 m_clientArea->UpdateDisplay();
1786
1787 return true;
1788 }
1789
1790 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
1791 {
1792 if (!wxDataViewCtrlBase::AppendColumn(col))
1793 return false;
1794
1795 m_clientArea->UpdateDisplay();
1796
1797 return true;
1798 }
1799
1800 void wxDataViewCtrl::SetSelection( int row )
1801 {
1802 }
1803
1804 void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to )
1805 {
1806 }
1807
1808 void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections)
1809 {
1810 }
1811
1812 bool wxDataViewCtrl::IsSelected( unsigned int row ) const
1813 {
1814 return false;
1815 }
1816
1817 int wxDataViewCtrl::GetSelection() const
1818 {
1819 return -1;
1820 }
1821
1822 int wxDataViewCtrl::GetSelections(wxArrayInt& aSelections) const
1823 {
1824 return 0;
1825 }
1826
1827 #endif
1828 // !wxUSE_GENERICDATAVIEWCTRL
1829
1830 #endif
1831 // wxUSE_DATAVIEWCTRL