]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
don't strip &s from tooltips (patch 1488318)
[wxWidgets.git] / src / generic / datavgen.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datavgen.cpp
3 // Purpose: wxDataViewCtrl generic implementation
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #ifdef __BORLANDC__
14 #pragma hdrstop
15 #endif
16
17 #if wxUSE_DATAVIEWCTRL
18
19 #include "wx/dataview.h"
20
21 #ifdef wxUSE_GENERICDATAVIEWCTRL
22
23 #ifndef WX_PRECOMP
24 #include "wx/sizer.h"
25 #include "wx/log.h"
26 #include "wx/dcclient.h"
27 #include "wx/timer.h"
28 #include "wx/settings.h"
29 #endif
30
31 #include "wx/stockitem.h"
32 #include "wx/calctrl.h"
33 #include "wx/popupwin.h"
34 #include "wx/renderer.h"
35
36 #ifdef __WXMSW__
37 #include "wx/msw/wrapwin.h"
38 #endif
39
40 //-----------------------------------------------------------------------------
41 // classes
42 //-----------------------------------------------------------------------------
43
44 class wxDataViewCtrl;
45
46 //-----------------------------------------------------------------------------
47 // wxDataViewHeaderWindow
48 //-----------------------------------------------------------------------------
49
50 class wxDataViewHeaderWindow: public wxWindow
51 {
52 public:
53 wxDataViewHeaderWindow( wxDataViewCtrl *parent,
54 wxWindowID id,
55 const wxPoint &pos = wxDefaultPosition,
56 const wxSize &size = wxDefaultSize,
57 const wxString &name = wxT("wxdataviewctrlheaderwindow") );
58 ~wxDataViewHeaderWindow();
59
60 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
61 wxDataViewCtrl *GetOwner() { return m_owner; }
62
63 void OnPaint( wxPaintEvent &event );
64 void OnMouse( wxMouseEvent &event );
65 void OnSetFocus( wxFocusEvent &event );
66
67 private:
68 wxDataViewCtrl *m_owner;
69 wxCursor *m_resizeCursor;
70
71 private:
72 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindow)
73 DECLARE_EVENT_TABLE()
74 };
75
76 //-----------------------------------------------------------------------------
77 // wxDataViewRenameTimer
78 //-----------------------------------------------------------------------------
79
80 class wxDataViewRenameTimer: public wxTimer
81 {
82 private:
83 wxDataViewMainWindow *m_owner;
84
85 public:
86 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
87 void Notify();
88 };
89
90 //-----------------------------------------------------------------------------
91 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
92 //-----------------------------------------------------------------------------
93
94 class wxDataViewTextCtrlWrapper : public wxEvtHandler
95 {
96 public:
97 // NB: text must be a valid object but not Create()d yet
98 wxDataViewTextCtrlWrapper( wxDataViewMainWindow *owner,
99 wxTextCtrl *text,
100 wxDataViewListModel *model,
101 size_t col, size_t row,
102 wxRect cellLabel );
103
104 wxTextCtrl *GetText() const { return m_text; }
105
106 void AcceptChangesAndFinish();
107
108 protected:
109 void OnChar( wxKeyEvent &event );
110 void OnKeyUp( wxKeyEvent &event );
111 void OnKillFocus( wxFocusEvent &event );
112
113 bool AcceptChanges();
114 void Finish();
115
116 private:
117 wxDataViewMainWindow *m_owner;
118 wxTextCtrl *m_text;
119 wxString m_startValue;
120 wxDataViewListModel *m_model;
121 size_t m_col;
122 size_t m_row;
123 bool m_finished;
124 bool m_aboutToFinish;
125
126 DECLARE_EVENT_TABLE()
127 };
128
129 //-----------------------------------------------------------------------------
130 // wxDataViewMainWindow
131 //-----------------------------------------------------------------------------
132
133 WX_DEFINE_SORTED_ARRAY_SIZE_T( size_t, wxDataViewSelection );
134
135 class wxDataViewMainWindow: public wxWindow
136 {
137 public:
138 wxDataViewMainWindow( wxDataViewCtrl *parent,
139 wxWindowID id,
140 const wxPoint &pos = wxDefaultPosition,
141 const wxSize &size = wxDefaultSize,
142 const wxString &name = wxT("wxdataviewctrlmainwindow") );
143 ~wxDataViewMainWindow();
144
145 // notifications from wxDataViewListModel
146 bool RowAppended();
147 bool RowPrepended();
148 bool RowInserted( size_t before );
149 bool RowDeleted( size_t row );
150 bool RowChanged( size_t row );
151 bool ValueChanged( size_t col, size_t row );
152 bool RowsReordered( size_t *new_order );
153 bool Cleared();
154
155 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
156 wxDataViewCtrl *GetOwner() { return m_owner; }
157
158 void OnPaint( wxPaintEvent &event );
159 void OnArrowChar(size_t newCurrent, const wxKeyEvent& event);
160 void OnChar( wxKeyEvent &event );
161 void OnMouse( wxMouseEvent &event );
162 void OnSetFocus( wxFocusEvent &event );
163 void OnKillFocus( wxFocusEvent &event );
164
165 void UpdateDisplay();
166 void RecalculateDisplay();
167 void OnInternalIdle();
168
169 void OnRenameTimer();
170 void FinishEditing( wxTextCtrl *text );
171
172 void ScrollWindow( int dx, int dy, const wxRect *rect );
173
174 bool HasCurrentRow() { return m_currentRow != (size_t)-1; }
175 void ChangeCurrentRow( size_t row );
176
177 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); };
178 bool IsEmpty() { return GetRowCount() == 0; }
179
180 int GetCountPerPage();
181 int GetEndOfLastCol();
182 size_t GetFirstVisibleRow();
183 size_t GetLastVisibleRow();
184 size_t GetRowCount();
185
186 void SelectAllRows( bool on );
187 void SelectRow( size_t row, bool on );
188 void SelectRows( size_t from, size_t to, bool on );
189 void ReverseRowSelection( size_t row );
190 bool IsRowSelected( size_t row );
191
192 void RefreshRow( size_t row );
193 void RefreshRows( size_t from, size_t to );
194 void RefreshRowsAfter( size_t firstRow );
195
196 private:
197 wxDataViewCtrl *m_owner;
198 int m_lineHeight;
199 bool m_dirty;
200
201 wxDataViewColumn *m_currentCol;
202 size_t m_currentRow;
203 wxDataViewSelection m_selection;
204
205 wxDataViewRenameTimer *m_renameTimer;
206 wxDataViewTextCtrlWrapper *m_textctrlWrapper;
207 bool m_lastOnSame;
208
209 bool m_hasFocus;
210
211 int m_dragCount;
212 wxPoint m_dragStart;
213
214 // for double click logic
215 size_t m_lineLastClicked,
216 m_lineBeforeLastClicked,
217 m_lineSelectSingleOnUp;
218
219 private:
220 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
221 DECLARE_EVENT_TABLE()
222 };
223
224 // ---------------------------------------------------------
225 // wxGenericDataViewListModelNotifier
226 // ---------------------------------------------------------
227
228 class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
229 {
230 public:
231 wxGenericDataViewListModelNotifier( wxDataViewMainWindow *mainWindow )
232 { m_mainWindow = mainWindow; }
233
234 virtual bool RowAppended()
235 { return m_mainWindow->RowAppended(); }
236 virtual bool RowPrepended()
237 { return m_mainWindow->RowPrepended(); }
238 virtual bool RowInserted( size_t before )
239 { return m_mainWindow->RowInserted( before ); }
240 virtual bool RowDeleted( size_t row )
241 { return m_mainWindow->RowDeleted( row ); }
242 virtual bool RowChanged( size_t row )
243 { return m_mainWindow->RowChanged( row ); }
244 virtual bool ValueChanged( size_t col, size_t row )
245 { return m_mainWindow->ValueChanged( col, row ); }
246 virtual bool RowsReordered( size_t *new_order )
247 { return m_mainWindow->RowsReordered( new_order ); }
248 virtual bool Cleared()
249 { return m_mainWindow->Cleared(); }
250
251 wxDataViewMainWindow *m_mainWindow;
252 };
253
254 // ---------------------------------------------------------
255 // wxDataViewCell
256 // ---------------------------------------------------------
257
258 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCell, wxDataViewCellBase)
259
260 wxDataViewCell::wxDataViewCell( const wxString &varianttype, wxDataViewCellMode mode ) :
261 wxDataViewCellBase( varianttype, mode )
262 {
263 m_dc = NULL;
264 }
265
266 wxDataViewCell::~wxDataViewCell()
267 {
268 if (m_dc)
269 delete m_dc;
270 }
271
272 wxDC *wxDataViewCell::GetDC()
273 {
274 if (m_dc == NULL)
275 {
276 if (GetOwner() == NULL)
277 return NULL;
278 if (GetOwner()->GetOwner() == NULL)
279 return NULL;
280 m_dc = new wxClientDC( GetOwner()->GetOwner() );
281 }
282
283 return m_dc;
284 }
285
286 // ---------------------------------------------------------
287 // wxDataViewCustomCell
288 // ---------------------------------------------------------
289
290 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomCell, wxDataViewCell)
291
292 wxDataViewCustomCell::wxDataViewCustomCell( const wxString &varianttype,
293 wxDataViewCellMode mode ) :
294 wxDataViewCell( varianttype, mode )
295 {
296 }
297
298 // ---------------------------------------------------------
299 // wxDataViewTextCell
300 // ---------------------------------------------------------
301
302 IMPLEMENT_ABSTRACT_CLASS(wxDataViewTextCell, wxDataViewCustomCell)
303
304 wxDataViewTextCell::wxDataViewTextCell( const wxString &varianttype, wxDataViewCellMode mode ) :
305 wxDataViewCustomCell( varianttype, mode )
306 {
307 }
308
309 bool wxDataViewTextCell::SetValue( const wxVariant &value )
310 {
311 m_text = value.GetString();
312
313 return true;
314 }
315
316 bool wxDataViewTextCell::GetValue( wxVariant& WXUNUSED(value) )
317 {
318 return false;
319 }
320
321 bool wxDataViewTextCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
322 {
323 dc->DrawText( m_text, cell.x, cell.y );
324
325 return true;
326 }
327
328 wxSize wxDataViewTextCell::GetSize()
329 {
330 return wxSize(80,20);
331 }
332
333 // ---------------------------------------------------------
334 // wxDataViewToggleCell
335 // ---------------------------------------------------------
336
337 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleCell, wxDataViewCustomCell)
338
339 wxDataViewToggleCell::wxDataViewToggleCell( const wxString &varianttype,
340 wxDataViewCellMode mode ) :
341 wxDataViewCustomCell( varianttype, mode )
342 {
343 m_toggle = false;
344 }
345
346 bool wxDataViewToggleCell::SetValue( const wxVariant &value )
347 {
348 m_toggle = value.GetBool();
349
350 return true;
351 }
352
353 bool wxDataViewToggleCell::GetValue( wxVariant &WXUNUSED(value) )
354 {
355 return false;
356 }
357
358 bool wxDataViewToggleCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
359 {
360 // User wxRenderer here
361
362 wxRect rect;
363 rect.x = cell.x + cell.width/2 - 10;
364 rect.width = 20;
365 rect.y = cell.y + cell.height/2 - 10;
366 rect.height = 20;
367
368 int flags = 0;
369 if (m_toggle)
370 flags |= wxCONTROL_CHECKED;
371 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
372 flags |= wxCONTROL_DISABLED;
373
374 wxRendererNative::Get().DrawCheckBox(
375 GetOwner()->GetOwner(),
376 *dc,
377 rect,
378 flags );
379
380 return true;
381 }
382
383 bool wxDataViewToggleCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
384 {
385 bool value = !m_toggle;
386 wxVariant variant = value;
387 model->SetValue( variant, col, row );
388 model->ValueChanged( col, row );
389 return true;
390 }
391
392 wxSize wxDataViewToggleCell::GetSize()
393 {
394 return wxSize(20,20);
395 }
396
397 // ---------------------------------------------------------
398 // wxDataViewProgressCell
399 // ---------------------------------------------------------
400
401 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell, wxDataViewCustomCell)
402
403 wxDataViewProgressCell::wxDataViewProgressCell( const wxString &label,
404 const wxString &varianttype, wxDataViewCellMode mode ) :
405 wxDataViewCustomCell( varianttype, mode )
406 {
407 m_label = label;
408 m_value = 0;
409 }
410
411 wxDataViewProgressCell::~wxDataViewProgressCell()
412 {
413 }
414
415 bool wxDataViewProgressCell::SetValue( const wxVariant &value )
416 {
417 m_value = (long) value;
418
419 if (m_value < 0) m_value = 0;
420 if (m_value > 100) m_value = 100;
421
422 return true;
423 }
424
425 bool wxDataViewProgressCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
426 {
427 double pct = (double)m_value / 100.0;
428 wxRect bar = cell;
429 bar.width = (int)(cell.width * pct);
430 dc->SetPen( *wxTRANSPARENT_PEN );
431 dc->SetBrush( *wxBLUE_BRUSH );
432 dc->DrawRectangle( bar );
433
434 dc->SetBrush( *wxTRANSPARENT_BRUSH );
435 dc->SetPen( *wxBLACK_PEN );
436 dc->DrawRectangle( cell );
437
438 return true;
439 }
440
441 wxSize wxDataViewProgressCell::GetSize()
442 {
443 return wxSize(40,12);
444 }
445
446 // ---------------------------------------------------------
447 // wxDataViewDateCell
448 // ---------------------------------------------------------
449
450 class wxDataViewDateCellPopupTransient: public wxPopupTransientWindow
451 {
452 public:
453 wxDataViewDateCellPopupTransient( wxWindow* parent, wxDateTime *value,
454 wxDataViewListModel *model, size_t col, size_t row ) :
455 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
456 {
457 m_model = model;
458 m_col = col;
459 m_row = row;
460 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
461 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
462 sizer->Add( m_cal, 1, wxGROW );
463 SetSizer( sizer );
464 sizer->Fit( this );
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 protected:
475 virtual void OnDismiss()
476 {
477 }
478
479 private:
480 DECLARE_EVENT_TABLE()
481 };
482
483 BEGIN_EVENT_TABLE(wxDataViewDateCellPopupTransient,wxPopupTransientWindow)
484 EVT_CALENDAR( wxID_ANY, wxDataViewDateCellPopupTransient::OnCalendar )
485 END_EVENT_TABLE()
486
487 void wxDataViewDateCellPopupTransient::OnCalendar( wxCalendarEvent &event )
488 {
489 wxDateTime date = event.GetDate();
490 wxVariant value = date;
491 m_model->SetValue( value, m_col, m_row );
492 m_model->ValueChanged( m_col, m_row );
493 DismissAndNotify();
494 }
495
496 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateCell, wxDataViewCustomCell)
497
498 wxDataViewDateCell::wxDataViewDateCell( const wxString &varianttype,
499 wxDataViewCellMode mode ) :
500 wxDataViewCustomCell( varianttype, mode )
501 {
502 }
503
504 bool wxDataViewDateCell::SetValue( const wxVariant &value )
505 {
506 m_date = value.GetDateTime();
507
508 return true;
509 }
510
511 bool wxDataViewDateCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
512 {
513 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
514 wxString tmp = m_date.FormatDate();
515 dc->DrawText( tmp, cell.x, cell.y );
516
517 return true;
518 }
519
520 wxSize wxDataViewDateCell::GetSize()
521 {
522 wxDataViewCtrl* view = GetOwner()->GetOwner();
523 wxString tmp = m_date.FormatDate();
524 wxCoord x,y,d;
525 view->GetTextExtent( tmp, &x, &y, &d );
526 return wxSize(x,y+d);
527 }
528
529 bool wxDataViewDateCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
530 {
531 wxVariant variant;
532 model->GetValue( variant, col, row );
533 wxDateTime value = variant.GetDateTime();
534
535 wxDataViewDateCellPopupTransient *popup = new wxDataViewDateCellPopupTransient(
536 GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
537 wxPoint pos = wxGetMousePosition();
538 popup->Move( pos );
539 popup->Layout();
540 popup->Popup( popup->m_cal );
541
542 return true;
543 }
544
545 // ---------------------------------------------------------
546 // wxDataViewColumn
547 // ---------------------------------------------------------
548
549 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
550
551 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewCell *cell, size_t model_column,
552 int fixed_width, wxDataViewColumnSizing sizing, int flags ) :
553 wxDataViewColumnBase( title, cell, model_column, flags )
554 {
555 m_sizing = sizing;
556
557 m_width = fixed_width;
558 m_fixedWidth = fixed_width;
559 }
560
561 wxDataViewColumn::~wxDataViewColumn()
562 {
563 }
564
565 void wxDataViewColumn::SetTitle( const wxString &title )
566 {
567 wxDataViewColumnBase::SetTitle( title );
568
569 }
570
571 int wxDataViewColumn::GetWidth()
572 {
573 return m_width;
574 }
575
576 void wxDataViewColumn::SetFixedWidth( int width )
577 {
578 m_fixedWidth = width;
579
580 if (m_sizing == wxDATAVIEW_COL_WIDTH_FIXED)
581 {
582 m_width = width;
583 // Set dirty
584 }
585 }
586
587 int wxDataViewColumn::GetFixedWidth()
588 {
589 return m_fixedWidth;
590 }
591
592 //-----------------------------------------------------------------------------
593 // wxDataViewHeaderWindow
594 //-----------------------------------------------------------------------------
595
596 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow)
597
598 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow)
599 EVT_PAINT (wxDataViewHeaderWindow::OnPaint)
600 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse)
601 EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus)
602 END_EVENT_TABLE()
603
604 wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id,
605 const wxPoint &pos, const wxSize &size, const wxString &name ) :
606 wxWindow( parent, id, pos, size, 0, name )
607 {
608 SetOwner( parent );
609
610 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
611
612 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
613 SetOwnForegroundColour( attr.colFg );
614 SetOwnBackgroundColour( attr.colBg );
615 if (!m_hasFont)
616 SetOwnFont( attr.font );
617 }
618
619 wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
620 {
621 delete m_resizeCursor;
622 }
623
624 void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
625 {
626 int w, h;
627 GetClientSize( &w, &h );
628
629 wxPaintDC dc( this );
630
631 int xpix;
632 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
633
634 int x;
635 m_owner->GetViewStart( &x, NULL );
636
637 // account for the horz scrollbar offset
638 dc.SetDeviceOrigin( -x * xpix, 0 );
639
640 dc.SetFont( GetFont() );
641
642 size_t cols = GetOwner()->GetNumberOfColumns();
643 size_t i;
644 int xpos = 0;
645 for (i = 0; i < cols; i++)
646 {
647 wxDataViewColumn *col = GetOwner()->GetColumn( i );
648 int width = col->GetWidth();
649
650 int cw = width;
651 int ch = h;
652
653 wxRendererNative::Get().DrawHeaderButton
654 (
655 this,
656 dc,
657 wxRect(xpos, 0, cw, ch-1),
658 m_parent->IsEnabled() ? 0
659 : (int)wxCONTROL_DISABLED
660 );
661
662 dc.DrawText( col->GetTitle(), xpos+3, 3 );
663
664 xpos += width;
665 }
666 }
667
668 void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) )
669 {
670 }
671
672 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
673 {
674 GetParent()->SetFocus();
675 event.Skip();
676 }
677
678 //-----------------------------------------------------------------------------
679 // wxDataViewRenameTimer
680 //-----------------------------------------------------------------------------
681
682 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
683 {
684 m_owner = owner;
685 }
686
687 void wxDataViewRenameTimer::Notify()
688 {
689 m_owner->OnRenameTimer();
690 }
691
692 //-----------------------------------------------------------------------------
693 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
694 //-----------------------------------------------------------------------------
695
696 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler)
697 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar)
698 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp)
699 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus)
700 END_EVENT_TABLE()
701
702 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
703 wxDataViewMainWindow *owner,
704 wxTextCtrl *text,
705 wxDataViewListModel *model,
706 size_t col, size_t row,
707 wxRect rectLabel )
708 {
709 m_owner = owner;
710 m_model = model;
711 m_row = row;
712 m_col = col;
713 m_text = text;
714
715 m_finished = false;
716 m_aboutToFinish = false;
717
718 wxVariant value;
719 model->GetValue( value, col, row );
720 m_startValue = value.GetString();
721
722 m_owner->GetOwner()->CalcScrolledPosition(
723 rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
724
725 m_text->Create( owner, wxID_ANY, m_startValue,
726 wxPoint(rectLabel.x-2,rectLabel.y-2),
727 wxSize(rectLabel.width+7,rectLabel.height+4) );
728 m_text->SetFocus();
729
730 m_text->PushEventHandler(this);
731 }
732
733 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
734 {
735 m_aboutToFinish = true;
736
737 // Notify the owner about the changes
738 AcceptChanges();
739
740 // Even if vetoed, close the control (consistent with MSW)
741 Finish();
742 }
743
744 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent &event )
745 {
746 switch ( event.m_keyCode )
747 {
748 case WXK_RETURN:
749 AcceptChangesAndFinish();
750 break;
751
752 case WXK_ESCAPE:
753 // m_owner->OnRenameCancelled( m_itemEdited );
754 Finish();
755 break;
756
757 default:
758 event.Skip();
759 }
760 }
761
762 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
763 {
764 if (m_finished)
765 {
766 event.Skip();
767 return;
768 }
769
770 // auto-grow the textctrl
771 wxSize parentSize = m_owner->GetSize();
772 wxPoint myPos = m_text->GetPosition();
773 wxSize mySize = m_text->GetSize();
774 int sx, sy;
775 m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
776 if (myPos.x + sx > parentSize.x)
777 sx = parentSize.x - myPos.x;
778 if (mySize.x > sx)
779 sx = mySize.x;
780 m_text->SetSize(sx, wxDefaultCoord);
781
782 event.Skip();
783 }
784
785 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
786 {
787 if ( !m_finished && !m_aboutToFinish )
788 {
789 AcceptChanges();
790 //if ( !AcceptChanges() )
791 // m_owner->OnRenameCancelled( m_itemEdited );
792
793 Finish();
794 }
795
796 // We must let the native text control handle focus
797 event.Skip();
798 }
799
800 bool wxDataViewTextCtrlWrapper::AcceptChanges()
801 {
802 const wxString value = m_text->GetValue();
803
804 if ( value == m_startValue )
805 // nothing changed, always accept
806 return true;
807
808 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
809 // vetoed by the user
810 // return false;
811
812 // accepted, do rename the item
813 wxVariant variant;
814 variant = value;
815 m_model->SetValue( variant, m_col, m_row );
816 m_model->ValueChanged( m_col, m_row );
817
818 return true;
819 }
820
821 void wxDataViewTextCtrlWrapper::Finish()
822 {
823 if ( !m_finished )
824 {
825 m_finished = true;
826
827 m_text->RemoveEventHandler(this);
828 m_owner->FinishEditing(m_text);
829
830 // delete later
831 wxPendingDelete.Append( this );
832 }
833 }
834
835 //-----------------------------------------------------------------------------
836 // wxDataViewMainWindow
837 //-----------------------------------------------------------------------------
838
839 int LINKAGEMODE wxDataViewSelectionCmp( size_t row1, size_t row2 )
840 {
841 if (row1 > row2) return 1;
842 if (row1 == row2) return 0;
843 return -1;
844 }
845
846
847 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
848
849 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
850 EVT_PAINT (wxDataViewMainWindow::OnPaint)
851 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
852 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
853 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
854 EVT_CHAR (wxDataViewMainWindow::OnChar)
855 END_EVENT_TABLE()
856
857 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
858 const wxPoint &pos, const wxSize &size, const wxString &name ) :
859 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
860 m_selection( wxDataViewSelectionCmp )
861
862 {
863 SetOwner( parent );
864
865 m_lastOnSame = false;
866 m_renameTimer = new wxDataViewRenameTimer( this );
867 m_textctrlWrapper = NULL;
868
869 // TODO: user better initial values/nothing selected
870 m_currentCol = NULL;
871 m_currentRow = 0;
872
873 // TODO: we need to calculate this smartly
874 m_lineHeight = 20;
875
876 m_dragCount = 0;
877 m_dragStart = wxPoint(0,0);
878 m_lineLastClicked = (size_t) -1;
879 m_lineBeforeLastClicked = (size_t) -1;
880 m_lineSelectSingleOnUp = (size_t) -1;
881
882 m_hasFocus = false;
883
884 SetBackgroundColour( *wxWHITE );
885
886 UpdateDisplay();
887 }
888
889 wxDataViewMainWindow::~wxDataViewMainWindow()
890 {
891 delete m_renameTimer;
892 }
893
894 void wxDataViewMainWindow::OnRenameTimer()
895 {
896 // We have to call this here because changes may just have
897 // been made and no screen update taken place.
898 if ( m_dirty )
899 wxSafeYield();
900
901
902 int xpos = 0;
903 size_t cols = GetOwner()->GetNumberOfColumns();
904 size_t i;
905 for (i = 0; i < cols; i++)
906 {
907 wxDataViewColumn *c = GetOwner()->GetColumn( i );
908 if (c == m_currentCol)
909 break;
910 xpos += c->GetWidth();
911 }
912 wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight );
913
914 wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
915
916 wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
917 m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
918 m_currentCol->GetModelColumn(), m_currentRow, labelRect );
919 }
920
921 void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text )
922 {
923 delete text;
924 m_textctrlWrapper = NULL;
925 SetFocus();
926 // SetFocusIgnoringChildren();
927 }
928
929 bool wxDataViewMainWindow::RowAppended()
930 {
931 return false;
932 }
933
934 bool wxDataViewMainWindow::RowPrepended()
935 {
936 return false;
937 }
938
939 bool wxDataViewMainWindow::RowInserted( size_t WXUNUSED(before) )
940 {
941 return false;
942 }
943
944 bool wxDataViewMainWindow::RowDeleted( size_t WXUNUSED(row) )
945 {
946 return false;
947 }
948
949 bool wxDataViewMainWindow::RowChanged( size_t WXUNUSED(row) )
950 {
951 return false;
952 }
953
954 bool wxDataViewMainWindow::ValueChanged( size_t WXUNUSED(col), size_t row )
955 {
956 wxRect rect( 0, row*m_lineHeight, 10000, m_lineHeight );
957 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
958 Refresh( true, &rect );
959
960 return true;
961 }
962
963 bool wxDataViewMainWindow::RowsReordered( size_t *WXUNUSED(new_order) )
964 {
965 Refresh();
966
967 return true;
968 }
969
970 bool wxDataViewMainWindow::Cleared()
971 {
972 return false;
973 }
974
975 void wxDataViewMainWindow::UpdateDisplay()
976 {
977 m_dirty = true;
978 }
979
980 void wxDataViewMainWindow::OnInternalIdle()
981 {
982 wxWindow::OnInternalIdle();
983
984 if (m_dirty)
985 {
986 RecalculateDisplay();
987 m_dirty = false;
988 }
989 }
990
991 void wxDataViewMainWindow::RecalculateDisplay()
992 {
993 wxDataViewListModel *model = GetOwner()->GetModel();
994 if (!model)
995 {
996 Refresh();
997 return;
998 }
999
1000 int width = 0;
1001 size_t cols = GetOwner()->GetNumberOfColumns();
1002 size_t i;
1003 for (i = 0; i < cols; i++)
1004 {
1005 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1006 width += col->GetWidth();
1007 }
1008
1009 int height = model->GetNumberOfRows() * m_lineHeight;
1010
1011 SetVirtualSize( width, height );
1012 GetOwner()->SetScrollRate( 10, m_lineHeight );
1013
1014 Refresh();
1015 }
1016
1017 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1018 {
1019 wxWindow::ScrollWindow( dx, dy, rect );
1020 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1021 }
1022
1023 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1024 {
1025 wxPaintDC dc( this );
1026
1027 GetOwner()->PrepareDC( dc );
1028
1029 dc.SetFont( GetFont() );
1030
1031 wxRect update = GetUpdateRegion().GetBox();
1032 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1033
1034 wxDataViewListModel *model = GetOwner()->GetModel();
1035
1036 size_t item_start = wxMax( 0, (update.y / m_lineHeight) );
1037 size_t item_count = wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1038 (int)(model->GetNumberOfRows()-item_start) );
1039
1040
1041
1042 size_t item;
1043 for (item = item_start; item < item_start+item_count; item++)
1044 {
1045 if (m_selection.Index( item ) != wxNOT_FOUND)
1046 {
1047 int flags = wxCONTROL_SELECTED;
1048 if (item == m_currentRow)
1049 flags |= wxCONTROL_CURRENT;
1050 if (m_hasFocus)
1051 flags |= wxCONTROL_FOCUSED;
1052 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1053 wxRendererNative::Get().DrawItemSelectionRect
1054 (
1055 this,
1056 dc,
1057 rect,
1058 flags
1059 );
1060 }
1061 else
1062 {
1063 if (item == m_currentRow)
1064 {
1065 int flags = wxCONTROL_CURRENT;
1066 if (m_hasFocus)
1067 flags |= wxCONTROL_FOCUSED; // should have no effect
1068 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1069 wxRendererNative::Get().DrawItemSelectionRect
1070 (
1071 this,
1072 dc,
1073 rect,
1074 flags
1075 );
1076
1077 }
1078 }
1079 }
1080
1081 wxRect cell_rect;
1082 cell_rect.x = 0;
1083 cell_rect.height = m_lineHeight;
1084 size_t cols = GetOwner()->GetNumberOfColumns();
1085 size_t i;
1086 for (i = 0; i < cols; i++)
1087 {
1088 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1089 wxDataViewCell *cell = col->GetCell();
1090 cell_rect.width = col->GetWidth();
1091
1092 for (item = item_start; item < item_start+item_count; item++)
1093 {
1094 cell_rect.y = item*m_lineHeight;
1095 wxVariant value;
1096 model->GetValue( value, col->GetModelColumn(), item );
1097 cell->SetValue( value );
1098 wxSize size = cell->GetSize();
1099 // cannot be bigger than allocated space
1100 size.x = wxMin( size.x, cell_rect.width );
1101 size.y = wxMin( size.y, cell_rect.height );
1102 // TODO: check for left/right/centre alignment here
1103 wxRect item_rect;
1104 // for now: centre
1105 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1106 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1107
1108 item_rect.width = size.x;
1109 item_rect.height= size.y;
1110 cell->Render( item_rect, &dc, 0 );
1111 }
1112
1113 cell_rect.x += cell_rect.width;
1114 }
1115 }
1116
1117 int wxDataViewMainWindow::GetCountPerPage()
1118 {
1119 wxSize size = GetClientSize();
1120 return size.y / m_lineHeight;
1121 }
1122
1123 int wxDataViewMainWindow::GetEndOfLastCol()
1124 {
1125 int width = 0;
1126 size_t i;
1127 for (i = 0; i < GetOwner()->GetNumberOfColumns(); i++)
1128 {
1129 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1130 width += c->GetWidth();
1131 }
1132 return width;
1133 }
1134
1135 size_t wxDataViewMainWindow::GetFirstVisibleRow()
1136 {
1137 int x = 0;
1138 int y = 0;
1139 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1140
1141 return y / m_lineHeight;
1142 }
1143
1144 size_t wxDataViewMainWindow::GetLastVisibleRow()
1145 {
1146 wxSize client_size = GetClientSize();
1147 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y, &client_size.x, &client_size.y );
1148
1149 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
1150 }
1151
1152 size_t wxDataViewMainWindow::GetRowCount()
1153 {
1154 return GetOwner()->GetModel()->GetNumberOfRows();
1155 }
1156
1157 void wxDataViewMainWindow::ChangeCurrentRow( size_t row )
1158 {
1159 m_currentRow = row;
1160
1161 // send event
1162 }
1163
1164 void wxDataViewMainWindow::SelectAllRows( bool on )
1165 {
1166 if (IsEmpty())
1167 return;
1168
1169 if (on)
1170 {
1171 m_selection.Clear();
1172 for (size_t i = 0; i < GetRowCount(); i++)
1173 m_selection.Add( i );
1174 Refresh();
1175 }
1176 else
1177 {
1178 size_t first_visible = GetFirstVisibleRow();
1179 size_t last_visible = GetLastVisibleRow();
1180 size_t i;
1181 for (i = 0; i < m_selection.GetCount(); i++)
1182 {
1183 size_t row = m_selection[i];
1184 if ((row >= first_visible) && (row <= last_visible))
1185 RefreshRow( row );
1186 }
1187 m_selection.Clear();
1188 }
1189 }
1190
1191 void wxDataViewMainWindow::SelectRow( size_t row, bool on )
1192 {
1193 if (m_selection.Index( row ) == wxNOT_FOUND)
1194 {
1195 if (on)
1196 {
1197 m_selection.Add( row );
1198 RefreshRow( row );
1199 }
1200 }
1201 else
1202 {
1203 if (!on)
1204 {
1205 m_selection.Remove( row );
1206 RefreshRow( row );
1207 }
1208 }
1209 }
1210
1211 void wxDataViewMainWindow::SelectRows( size_t from, size_t to, bool on )
1212 {
1213 if (from > to)
1214 {
1215 size_t tmp = from;
1216 from = to;
1217 to = tmp;
1218 }
1219
1220 size_t i;
1221 for (i = from; i <= to; i++)
1222 {
1223 if (m_selection.Index( i ) == wxNOT_FOUND)
1224 {
1225 if (on)
1226 m_selection.Add( i );
1227 }
1228 else
1229 {
1230 if (!on)
1231 m_selection.Remove( i );
1232 }
1233 }
1234 RefreshRows( from, to );
1235 }
1236
1237 void wxDataViewMainWindow::ReverseRowSelection( size_t row )
1238 {
1239 if (m_selection.Index( row ) == wxNOT_FOUND)
1240 m_selection.Add( row );
1241 else
1242 m_selection.Remove( row );
1243 RefreshRow( row );
1244 }
1245
1246 bool wxDataViewMainWindow::IsRowSelected( size_t row )
1247 {
1248 return (m_selection.Index( row ) != wxNOT_FOUND);
1249 }
1250
1251 void wxDataViewMainWindow::RefreshRow( size_t row )
1252 {
1253 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
1254 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1255
1256 wxSize client_size = GetClientSize();
1257 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1258 wxRect intersect_rect = client_rect.Intersect( rect );
1259 if (intersect_rect.width > 0)
1260 Refresh( true, &intersect_rect );
1261 }
1262
1263 void wxDataViewMainWindow::RefreshRows( size_t from, size_t to )
1264 {
1265 if (from > to)
1266 {
1267 size_t tmp = to;
1268 to = from;
1269 from = tmp;
1270 }
1271
1272 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
1273 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1274
1275 wxSize client_size = GetClientSize();
1276 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1277 wxRect intersect_rect = client_rect.Intersect( rect );
1278 if (intersect_rect.width > 0)
1279 Refresh( true, &intersect_rect );
1280 }
1281
1282 void wxDataViewMainWindow::RefreshRowsAfter( size_t firstRow )
1283 {
1284 size_t count = GetRowCount();
1285 if (firstRow > count)
1286 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 < 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 < 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