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