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