Fix Refresh() problem.
[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 wxRect rect;
319 rect.x = cell.x + cell.width/2 - 10;
320 rect.width = 20;
321 rect.y = cell.y + cell.height/2 - 10;
322 rect.height = 20;
323
324 int flags = 0;
325 if (m_toggle)
326 flags |= wxCONTROL_CHECKED;
327 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
328 flags |= wxCONTROL_DISABLED;
329
330 wxRendererNative::Get().DrawCheckButton(
331 GetOwner()->GetOwner(),
332 *dc,
333 rect,
334 flags );
335
336 return true;
337 }
338
339 bool wxDataViewToggleCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
340 {
341 bool value = !m_toggle;
342 wxVariant variant = value;
343 model->SetValue( variant, col, row );
344 model->ValueChanged( col, row );
345 return true;
346 }
347
348 wxSize wxDataViewToggleCell::GetSize()
349 {
350 return wxSize(20,20);
351 }
352
353 // ---------------------------------------------------------
354 // wxDataViewProgressCell
355 // ---------------------------------------------------------
356
357 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell, wxDataViewCustomCell)
358
359 wxDataViewProgressCell::wxDataViewProgressCell( const wxString &label,
360 const wxString &varianttype, wxDataViewCellMode mode ) :
361 wxDataViewCustomCell( varianttype, mode )
362 {
363 m_label = label;
364 m_value = 0;
365 }
366
367 wxDataViewProgressCell::~wxDataViewProgressCell()
368 {
369 }
370
371 bool wxDataViewProgressCell::SetValue( const wxVariant &value )
372 {
373 m_value = (long) value;
374
375 if (m_value < 0) m_value = 0;
376 if (m_value > 100) m_value = 100;
377
378 return true;
379 }
380
381 bool wxDataViewProgressCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
382 {
383 double pct = (double)m_value / 100.0;
384 wxRect bar = cell;
385 bar.width = (int)(cell.width * pct);
386 dc->SetPen( *wxTRANSPARENT_PEN );
387 dc->SetBrush( *wxBLUE_BRUSH );
388 dc->DrawRectangle( bar );
389
390 dc->SetBrush( *wxTRANSPARENT_BRUSH );
391 dc->SetPen( *wxBLACK_PEN );
392 dc->DrawRectangle( cell );
393
394 return true;
395 }
396
397 wxSize wxDataViewProgressCell::GetSize()
398 {
399 return wxSize(40,12);
400 }
401
402 // ---------------------------------------------------------
403 // wxDataViewDateCell
404 // ---------------------------------------------------------
405
406 class wxDataViewDateCellPopupTransient: public wxPopupTransientWindow
407 {
408 public:
409 wxDataViewDateCellPopupTransient( wxWindow* parent, wxDateTime *value,
410 wxDataViewListModel *model, size_t col, size_t row ) :
411 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
412 {
413 m_model = model;
414 m_col = col;
415 m_row = row;
416 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
417 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
418 sizer->Add( m_cal, 1, wxGROW );
419 SetSizer( sizer );
420 sizer->Fit( this );
421 }
422
423 virtual void OnDismiss()
424 {
425 }
426
427 void OnCalendar( wxCalendarEvent &event );
428
429 wxCalendarCtrl *m_cal;
430 wxDataViewListModel *m_model;
431 size_t m_col;
432 size_t m_row;
433
434 private:
435 DECLARE_EVENT_TABLE()
436 };
437
438 BEGIN_EVENT_TABLE(wxDataViewDateCellPopupTransient,wxPopupTransientWindow)
439 EVT_CALENDAR( wxID_ANY, wxDataViewDateCellPopupTransient::OnCalendar )
440 END_EVENT_TABLE()
441
442 void wxDataViewDateCellPopupTransient::OnCalendar( wxCalendarEvent &event )
443 {
444 wxDateTime date = event.GetDate();
445 wxVariant value = date;
446 m_model->SetValue( value, m_col, m_row );
447 m_model->ValueChanged( m_col, m_row );
448 DismissAndNotify();
449 }
450
451 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateCell, wxDataViewCustomCell)
452
453 wxDataViewDateCell::wxDataViewDateCell( const wxString &varianttype,
454 wxDataViewCellMode mode ) :
455 wxDataViewCustomCell( varianttype, mode )
456 {
457 }
458
459 bool wxDataViewDateCell::SetValue( const wxVariant &value )
460 {
461 m_date = value.GetDateTime();
462
463 return true;
464 }
465
466 bool wxDataViewDateCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
467 {
468 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
469 wxString tmp = m_date.FormatDate();
470 dc->DrawText( tmp, cell.x, cell.y );
471
472 return true;
473 }
474
475 wxSize wxDataViewDateCell::GetSize()
476 {
477 wxDataViewCtrl* view = GetOwner()->GetOwner();
478 wxString tmp = m_date.FormatDate();
479 wxCoord x,y,d;
480 view->GetTextExtent( tmp, &x, &y, &d );
481 return wxSize(x,y+d);
482 }
483
484 bool wxDataViewDateCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
485 {
486 wxVariant variant;
487 model->GetValue( variant, col, row );
488 wxDateTime value = variant.GetDateTime();
489
490 wxDataViewDateCellPopupTransient *popup = new wxDataViewDateCellPopupTransient(
491 GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
492 wxPoint pos = wxGetMousePosition();
493 popup->Move( pos );
494 popup->Layout();
495 popup->Popup( popup->m_cal );
496
497 return true;
498 }
499
500 // ---------------------------------------------------------
501 // wxDataViewColumn
502 // ---------------------------------------------------------
503
504 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
505
506 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewCell *cell, size_t model_column,
507 int fixed_width, wxDataViewColumnSizing sizing, int flags ) :
508 wxDataViewColumnBase( title, cell, model_column, flags )
509 {
510 m_sizing = sizing;
511
512 m_width = fixed_width;
513 m_fixedWidth = fixed_width;
514 }
515
516 wxDataViewColumn::~wxDataViewColumn()
517 {
518 }
519
520 void wxDataViewColumn::SetTitle( const wxString &title )
521 {
522 wxDataViewColumnBase::SetTitle( title );
523
524 }
525
526 int wxDataViewColumn::GetWidth()
527 {
528 return m_width;
529 }
530
531 void wxDataViewColumn::SetFixedWidth( int width )
532 {
533 m_fixedWidth = width;
534
535 if (m_sizing == wxDATAVIEW_COL_WIDTH_FIXED)
536 {
537 m_width = width;
538 // Set dirty
539 }
540 }
541
542 int wxDataViewColumn::GetFixedWidth()
543 {
544 return m_fixedWidth;
545 }
546
547 //-----------------------------------------------------------------------------
548 // wxDataViewHeaderWindow
549 //-----------------------------------------------------------------------------
550
551 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow)
552
553 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow)
554 EVT_PAINT (wxDataViewHeaderWindow::OnPaint)
555 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse)
556 EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus)
557 END_EVENT_TABLE()
558
559 wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id,
560 const wxPoint &pos, const wxSize &size, const wxString &name ) :
561 wxWindow( parent, id, pos, size, 0, name )
562 {
563 SetOwner( parent );
564
565 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
566
567 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
568 SetOwnForegroundColour( attr.colFg );
569 SetOwnBackgroundColour( attr.colBg );
570 if (!m_hasFont)
571 SetOwnFont( attr.font );
572 }
573
574 wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
575 {
576 delete m_resizeCursor;
577 }
578
579 void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
580 {
581 int w, h;
582 GetClientSize( &w, &h );
583
584 wxPaintDC dc( this );
585
586 int xpix;
587 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
588
589 int x;
590 m_owner->GetViewStart( &x, NULL );
591
592 // account for the horz scrollbar offset
593 dc.SetDeviceOrigin( -x * xpix, 0 );
594
595 dc.SetFont( GetFont() );
596
597 size_t cols = GetOwner()->GetNumberOfColumns();
598 size_t i;
599 int xpos = 0;
600 for (i = 0; i < cols; i++)
601 {
602 wxDataViewColumn *col = GetOwner()->GetColumn( i );
603 int width = col->GetWidth();
604
605 // the width of the rect to draw: make it smaller to fit entirely
606 // inside the column rect
607 #ifdef __WXMAC__
608 int cw = width;
609 int ch = h;
610 #else
611 int cw = width - 2;
612 int ch = h - 2;
613 #endif
614
615 wxRendererNative::Get().DrawHeaderButton
616 (
617 this,
618 dc,
619 wxRect(xpos, 0, cw, ch),
620 m_parent->IsEnabled() ? 0
621 : (int)wxCONTROL_DISABLED
622 );
623
624 dc.DrawText( col->GetTitle(), xpos+3, 3 );
625
626 xpos += width;
627 }
628 }
629
630 void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) )
631 {
632 }
633
634 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
635 {
636 event.Skip();
637 }
638
639 //-----------------------------------------------------------------------------
640 // wxDataViewRenameTimer
641 //-----------------------------------------------------------------------------
642
643 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
644 {
645 m_owner = owner;
646 }
647
648 void wxDataViewRenameTimer::Notify()
649 {
650 m_owner->OnRenameTimer();
651 }
652
653 //-----------------------------------------------------------------------------
654 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
655 //-----------------------------------------------------------------------------
656
657 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler)
658 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar)
659 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp)
660 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus)
661 END_EVENT_TABLE()
662
663 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
664 wxDataViewMainWindow *owner,
665 wxTextCtrl *text,
666 wxDataViewListModel *model,
667 size_t col, size_t row,
668 wxRect rectLabel )
669 {
670 m_owner = owner;
671 m_model = model;
672 m_row = row;
673 m_col = col;
674 m_text = text;
675
676 m_finished = false;
677 m_aboutToFinish = false;
678
679 wxVariant value;
680 model->GetValue( value, col, row );
681 m_startValue = value.GetString();
682
683 m_owner->GetOwner()->CalcScrolledPosition(
684 rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
685
686 m_text->Create( owner, wxID_ANY, m_startValue,
687 wxPoint(rectLabel.x-2,rectLabel.y-2),
688 wxSize(rectLabel.width+7,rectLabel.height+4) );
689 m_text->SetFocus();
690
691 m_text->PushEventHandler(this);
692 }
693
694 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
695 {
696 m_aboutToFinish = true;
697
698 // Notify the owner about the changes
699 AcceptChanges();
700
701 // Even if vetoed, close the control (consistent with MSW)
702 Finish();
703 }
704
705 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent &event )
706 {
707 switch ( event.m_keyCode )
708 {
709 case WXK_RETURN:
710 AcceptChangesAndFinish();
711 break;
712
713 case WXK_ESCAPE:
714 // m_owner->OnRenameCancelled( m_itemEdited );
715 Finish();
716 break;
717
718 default:
719 event.Skip();
720 }
721 }
722
723 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
724 {
725 if (m_finished)
726 {
727 event.Skip();
728 return;
729 }
730
731 // auto-grow the textctrl
732 wxSize parentSize = m_owner->GetSize();
733 wxPoint myPos = m_text->GetPosition();
734 wxSize mySize = m_text->GetSize();
735 int sx, sy;
736 m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
737 if (myPos.x + sx > parentSize.x)
738 sx = parentSize.x - myPos.x;
739 if (mySize.x > sx)
740 sx = mySize.x;
741 m_text->SetSize(sx, wxDefaultCoord);
742
743 event.Skip();
744 }
745
746 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
747 {
748 if ( !m_finished && !m_aboutToFinish )
749 {
750 AcceptChanges();
751 //if ( !AcceptChanges() )
752 // m_owner->OnRenameCancelled( m_itemEdited );
753
754 Finish();
755 }
756
757 // We must let the native text control handle focus
758 event.Skip();
759 }
760
761 bool wxDataViewTextCtrlWrapper::AcceptChanges()
762 {
763 const wxString value = m_text->GetValue();
764
765 if ( value == m_startValue )
766 // nothing changed, always accept
767 return true;
768
769 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
770 // vetoed by the user
771 // return false;
772
773 // accepted, do rename the item
774 wxVariant variant;
775 variant = value;
776 m_model->SetValue( variant, m_col, m_row );
777 m_model->ValueChanged( m_col, m_row );
778
779 return true;
780 }
781
782 void wxDataViewTextCtrlWrapper::Finish()
783 {
784 if ( !m_finished )
785 {
786 m_finished = true;
787
788 m_text->RemoveEventHandler(this);
789 m_owner->FinishEditing(m_text);
790
791 // delete later
792 wxPendingDelete.Append( this );
793 }
794 }
795
796 //-----------------------------------------------------------------------------
797 // wxDataViewMainWindow
798 //-----------------------------------------------------------------------------
799
800 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
801
802 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
803 EVT_PAINT (wxDataViewMainWindow::OnPaint)
804 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
805 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
806 END_EVENT_TABLE()
807
808 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
809 const wxPoint &pos, const wxSize &size, const wxString &name ) :
810 wxWindow( parent, id, pos, size, 0, name )
811 {
812 SetOwner( parent );
813
814 m_lastOnSame = false;
815 m_renameTimer = new wxDataViewRenameTimer( this );
816 m_textctrlWrapper = NULL;
817
818 // TODO: user better initial values/nothing selected
819 m_currentCol = NULL;
820 m_currentRow = 0;
821
822 // TODO: we need to calculate this smartly
823 m_lineHeight = 20;
824
825 UpdateDisplay();
826 }
827
828 wxDataViewMainWindow::~wxDataViewMainWindow()
829 {
830 delete m_renameTimer;
831 }
832
833 void wxDataViewMainWindow::OnRenameTimer()
834 {
835 // We have to call this here because changes may just have
836 // been made and no screen update taken place.
837 if ( m_dirty )
838 wxSafeYield();
839
840
841 int xpos = 0;
842 size_t cols = GetOwner()->GetNumberOfColumns();
843 size_t i;
844 for (i = 0; i < cols; i++)
845 {
846 wxDataViewColumn *c = GetOwner()->GetColumn( i );
847 if (c == m_currentCol)
848 break;
849 xpos += c->GetWidth();
850 }
851 wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight );
852
853 wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
854
855 wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
856 m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
857 m_currentCol->GetModelColumn(), m_currentRow, labelRect );
858 }
859
860 void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text )
861 {
862 delete text;
863 m_textctrlWrapper = NULL;
864 SetFocus();
865 // SetFocusIgnoringChildren();
866 }
867
868 bool wxDataViewMainWindow::RowAppended()
869 {
870 return false;
871 }
872
873 bool wxDataViewMainWindow::RowPrepended()
874 {
875 return false;
876 }
877
878 bool wxDataViewMainWindow::RowInserted( size_t WXUNUSED(before) )
879 {
880 return false;
881 }
882
883 bool wxDataViewMainWindow::RowDeleted( size_t WXUNUSED(row) )
884 {
885 return false;
886 }
887
888 bool wxDataViewMainWindow::RowChanged( size_t WXUNUSED(row) )
889 {
890 return false;
891 }
892
893 bool wxDataViewMainWindow::ValueChanged( size_t WXUNUSED(col), size_t row )
894 {
895 wxRect rect( 0, row*m_lineHeight, 10000, m_lineHeight );
896 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
897 Refresh( true, &rect );
898
899 return true;
900 }
901
902 bool wxDataViewMainWindow::RowsReordered( size_t *WXUNUSED(new_order) )
903 {
904 Refresh();
905
906 return true;
907 }
908
909 bool wxDataViewMainWindow::Cleared()
910 {
911 return false;
912 }
913
914 void wxDataViewMainWindow::UpdateDisplay()
915 {
916 m_dirty = true;
917 }
918
919 void wxDataViewMainWindow::OnInternalIdle()
920 {
921 wxWindow::OnInternalIdle();
922
923 if (m_dirty)
924 {
925 RecalculateDisplay();
926 m_dirty = false;
927 }
928 }
929
930 void wxDataViewMainWindow::RecalculateDisplay()
931 {
932 wxDataViewListModel *model = GetOwner()->GetModel();
933 if (!model)
934 {
935 Refresh();
936 return;
937 }
938
939 int width = 0;
940 size_t cols = GetOwner()->GetNumberOfColumns();
941 size_t i;
942 for (i = 0; i < cols; i++)
943 {
944 wxDataViewColumn *col = GetOwner()->GetColumn( i );
945 width += col->GetWidth();
946 }
947
948 int height = model->GetNumberOfRows() * m_lineHeight;
949
950 SetVirtualSize( width, height );
951 GetOwner()->SetScrollRate( 10, m_lineHeight );
952
953 Refresh();
954 }
955
956 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
957 {
958 wxWindow::ScrollWindow( dx, dy, rect );
959 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
960 }
961
962 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
963 {
964 wxPaintDC dc( this );
965
966 GetOwner()->PrepareDC( dc );
967
968 dc.SetFont( GetFont() );
969
970 wxRect update = GetUpdateRegion().GetBox();
971 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
972
973 wxDataViewListModel *model = GetOwner()->GetModel();
974
975 size_t item_start = wxMax( 0, (update.y / m_lineHeight) );
976 size_t item_count = wxMin( ((update.y + update.height) / m_lineHeight) - item_start + 1,
977 (int)(model->GetNumberOfRows()-item_start) );
978
979 wxRect cell_rect;
980 cell_rect.x = 0;
981 cell_rect.height = m_lineHeight;
982 size_t cols = GetOwner()->GetNumberOfColumns();
983 size_t i;
984 for (i = 0; i < cols; i++)
985 {
986 wxDataViewColumn *col = GetOwner()->GetColumn( i );
987 wxDataViewCell *cell = col->GetCell();
988 cell_rect.width = col->GetWidth();
989
990 size_t item;
991 for (item = item_start; item < item_start+item_count; item++)
992 {
993 cell_rect.y = item*m_lineHeight;
994 wxVariant value;
995 model->GetValue( value, col->GetModelColumn(), item );
996 cell->SetValue( value );
997 wxSize size = cell->GetSize();
998 // cannot be bigger than allocated space
999 size.x = wxMin( size.x, cell_rect.width );
1000 size.y = wxMin( size.y, cell_rect.height );
1001 // TODO: check for left/right/centre alignment here
1002 wxRect item_rect;
1003 // for now: centre
1004 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1005 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1006
1007 item_rect.width = size.x;
1008 item_rect.height= size.y;
1009 cell->Render( item_rect, &dc, 0 );
1010 }
1011
1012 cell_rect.x += cell_rect.width;
1013 }
1014 }
1015
1016 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
1017 {
1018 int x = event.GetX();
1019 int y = event.GetY();
1020 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1021
1022 wxDataViewColumn *col = NULL;
1023
1024 int xpos = 0;
1025 size_t cols = GetOwner()->GetNumberOfColumns();
1026 size_t i;
1027 for (i = 0; i < cols; i++)
1028 {
1029 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1030 if (x < xpos + c->GetWidth())
1031 {
1032 col = c;
1033 break;
1034 }
1035 xpos += c->GetWidth();
1036 }
1037 if (!col)
1038 return;
1039 wxDataViewCell *cell = col->GetCell();
1040
1041 size_t row = y / m_lineHeight;
1042
1043 wxDataViewListModel *model = GetOwner()->GetModel();
1044
1045 if (event.ButtonDClick())
1046 {
1047 m_renameTimer->Stop();
1048 m_lastOnSame = false;
1049 }
1050
1051 if (event.LeftDClick())
1052 {
1053 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
1054 {
1055 wxVariant value;
1056 model->GetValue( value, col->GetModelColumn(), row );
1057 cell->SetValue( value );
1058 wxRect cell_rect( xpos, row * m_lineHeight, col->GetWidth(), m_lineHeight );
1059 cell->Activate( cell_rect, model, col->GetModelColumn(), row );
1060 }
1061
1062 return;
1063 } else
1064 if (event.LeftUp())
1065 {
1066 if (m_lastOnSame)
1067 {
1068 if ((col == m_currentCol) & (row == m_currentRow) &&
1069 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
1070 {
1071 m_renameTimer->Start( 100, true );
1072 }
1073 }
1074
1075 m_lastOnSame = false;
1076 } else
1077 if (event.LeftDown())
1078 {
1079 wxDataViewColumn *oldCurrentCol = m_currentCol;
1080 size_t oldCurrentRow = m_currentRow;
1081
1082 // Update selection here...
1083 m_currentCol = col;
1084 m_currentRow = row;
1085
1086 m_lastOnSame = (col == oldCurrentCol) && (row == oldCurrentRow);
1087
1088 return;
1089 }
1090
1091 event.Skip();
1092 }
1093
1094 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
1095 {
1096 event.Skip();
1097 }
1098
1099 //-----------------------------------------------------------------------------
1100 // wxDataViewCtrl
1101 //-----------------------------------------------------------------------------
1102
1103 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
1104
1105 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
1106 EVT_SIZE(wxDataViewCtrl::OnSize)
1107 END_EVENT_TABLE()
1108
1109 wxDataViewCtrl::~wxDataViewCtrl()
1110 {
1111 if (m_notifier)
1112 GetModel()->RemoveNotifier( m_notifier );
1113 }
1114
1115 void wxDataViewCtrl::Init()
1116 {
1117 m_notifier = NULL;
1118 }
1119
1120 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
1121 const wxPoint& pos, const wxSize& size,
1122 long style, const wxValidator& validator )
1123 {
1124 if (!wxControl::Create( parent, id, pos, size, style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
1125 return false;
1126
1127 Init();
1128
1129 #ifdef __WXMAC__
1130 MacSetClipChildren( true ) ;
1131 #endif
1132
1133 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
1134 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,25) );
1135
1136 SetTargetWindow( m_clientArea );
1137
1138 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
1139 sizer->Add( m_headerArea, 0, wxGROW );
1140 sizer->Add( m_clientArea, 1, wxGROW );
1141 SetSizer( sizer );
1142
1143 return true;
1144 }
1145
1146 #ifdef __WXMSW__
1147 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
1148 WXWPARAM wParam,
1149 WXLPARAM lParam)
1150 {
1151 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
1152
1153 #ifndef __WXWINCE__
1154 // we need to process arrows ourselves for scrolling
1155 if ( nMsg == WM_GETDLGCODE )
1156 {
1157 rc |= DLGC_WANTARROWS;
1158 }
1159 #endif
1160
1161 return rc;
1162 }
1163 #endif
1164
1165 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
1166 {
1167 // We need to override OnSize so that our scrolled
1168 // window a) does call Layout() to use sizers for
1169 // positioning the controls but b) does not query
1170 // the sizer for their size and use that for setting
1171 // the scrollable area as set that ourselves by
1172 // calling SetScrollbar() further down.
1173
1174 Layout();
1175
1176 AdjustScrollbars();
1177 }
1178
1179 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
1180 {
1181 if (!wxDataViewCtrlBase::AssociateModel( model ))
1182 return false;
1183
1184 m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
1185
1186 model->AddNotifier( m_notifier );
1187
1188 m_clientArea->UpdateDisplay();
1189
1190 return true;
1191 }
1192
1193 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
1194 {
1195 if (!wxDataViewCtrlBase::AppendColumn(col))
1196 return false;
1197
1198 m_clientArea->UpdateDisplay();
1199
1200 return true;
1201 }
1202
1203 #endif
1204 // !wxUSE_GENERICDATAVIEWCTRL
1205
1206 #endif
1207 // wxUSE_DATAVIEWCTRL
1208