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