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