Added windowing and scrolling logic to generic
[wxWidgets.git] / src / generic / datavgen.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #include "wx/defs.h"
14
15 #if wxUSE_DATAVIEWCTRL
16
17 #include "wx/dataview.h"
18
19 #ifdef wxUSE_GENERICDATAVIEWCTRL
20
21 #include "wx/stockitem.h"
22 #include "wx/dcclient.h"
23 #include "wx/calctrl.h"
24 #include "wx/popupwin.h"
25 #include "wx/sizer.h"
26 #include "wx/log.h"
27 #include "wx/renderer.h"
28
29 #ifdef __WXMSW__
30 #include <windows.h> // for DLGC_WANTARROWS
31 #include "wx/msw/winundef.h"
32 #endif
33
34 //-----------------------------------------------------------------------------
35 // classes
36 //-----------------------------------------------------------------------------
37
38 class wxDataViewCtrl;
39
40 // ---------------------------------------------------------
41 // wxGenericDataViewListModelNotifier
42 // ---------------------------------------------------------
43
44 class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
45 {
46 public:
47 wxGenericDataViewListModelNotifier( wxDataViewListModel *wx_model );
48
49 virtual bool RowAppended();
50 virtual bool RowPrepended();
51 virtual bool RowInserted( size_t before );
52 virtual bool RowDeleted( size_t row );
53 virtual bool RowChanged( size_t row );
54 virtual bool ValueChanged( size_t col, size_t row );
55 virtual bool RowsReordered( size_t *new_order );
56 virtual bool Cleared();
57
58 wxDataViewListModel *m_wx_model;
59 };
60
61 // ---------------------------------------------------------
62 // wxGenericDataViewListModelNotifier
63 // ---------------------------------------------------------
64
65 wxGenericDataViewListModelNotifier::wxGenericDataViewListModelNotifier(
66 wxDataViewListModel *wx_model )
67 {
68 m_wx_model = wx_model;
69 }
70
71 bool wxGenericDataViewListModelNotifier::RowAppended()
72 {
73 size_t pos = m_wx_model->GetNumberOfRows()-1;
74
75 return false;
76 }
77
78 bool wxGenericDataViewListModelNotifier::RowPrepended()
79 {
80 return false;
81 }
82
83 bool wxGenericDataViewListModelNotifier::RowInserted( size_t before )
84 {
85 return false;
86 }
87
88 bool wxGenericDataViewListModelNotifier::RowDeleted( size_t row )
89 {
90 return false;
91 }
92
93 bool wxGenericDataViewListModelNotifier::RowChanged( size_t row )
94 {
95 return true;
96 }
97
98 bool wxGenericDataViewListModelNotifier::ValueChanged( size_t model_col, size_t model_row )
99 {
100 wxNode *node = GetOwner()->m_viewingColumns.GetFirst();
101 while (node)
102 {
103 wxDataViewViewingColumn* viewing_column = (wxDataViewViewingColumn*) node->GetData();
104 if (viewing_column->m_modelColumn == model_col)
105 {
106
107 }
108
109 node = node->GetNext();
110 }
111
112 return false;
113 }
114
115 bool wxGenericDataViewListModelNotifier::RowsReordered( size_t *new_order )
116 {
117 wxNode *node = GetOwner()->m_viewingColumns.GetFirst();
118 while (node)
119 {
120 wxDataViewViewingColumn* viewing_column = (wxDataViewViewingColumn*) node->GetData();
121
122 node = node->GetNext();
123 }
124
125 return false;
126 }
127
128 bool wxGenericDataViewListModelNotifier::Cleared()
129 {
130 return false;
131 }
132
133 // ---------------------------------------------------------
134 // wxDataViewCell
135 // ---------------------------------------------------------
136
137 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCell, wxDataViewCellBase)
138
139 wxDataViewCell::wxDataViewCell( const wxString &varianttype, wxDataViewCellMode mode ) :
140 wxDataViewCellBase( varianttype, mode )
141 {
142 }
143
144 // ---------------------------------------------------------
145 // wxDataViewTextCell
146 // ---------------------------------------------------------
147
148 IMPLEMENT_ABSTRACT_CLASS(wxDataViewTextCell, wxDataViewCell)
149
150 wxDataViewTextCell::wxDataViewTextCell( const wxString &varianttype, wxDataViewCellMode mode ) :
151 wxDataViewCell( varianttype, mode )
152 {
153 }
154
155 bool wxDataViewTextCell::SetValue( const wxVariant &value )
156 {
157 return false;
158 }
159
160 bool wxDataViewTextCell::GetValue( wxVariant &value )
161 {
162 return false;
163 }
164
165 // ---------------------------------------------------------
166 // wxDataViewToggleCell
167 // ---------------------------------------------------------
168
169 IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleCell, wxDataViewCell)
170
171 wxDataViewToggleCell::wxDataViewToggleCell( const wxString &varianttype,
172 wxDataViewCellMode mode ) :
173 wxDataViewCell( varianttype, mode )
174 {
175 }
176
177 bool wxDataViewToggleCell::SetValue( const wxVariant &value )
178 {
179 return false;
180 }
181
182 bool wxDataViewToggleCell::GetValue( wxVariant &value )
183 {
184 return false;
185 }
186
187 // ---------------------------------------------------------
188 // wxDataViewCustomCell
189 // ---------------------------------------------------------
190
191 IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomCell, wxDataViewCell)
192
193 wxDataViewCustomCell::wxDataViewCustomCell( const wxString &varianttype,
194 wxDataViewCellMode mode ) :
195 wxDataViewCell( varianttype, mode )
196 {
197 m_dc = NULL;
198
199 Init();
200 }
201
202 bool wxDataViewCustomCell::Init()
203 {
204 return false;
205 }
206
207 wxDataViewCustomCell::~wxDataViewCustomCell()
208 {
209 if (m_dc)
210 delete m_dc;
211 }
212
213 wxDC *wxDataViewCustomCell::GetDC()
214 {
215 if (m_dc == NULL)
216 {
217 if (GetOwner() == NULL)
218 return NULL;
219 if (GetOwner()->GetOwner() == NULL)
220 return NULL;
221 m_dc = new wxClientDC( GetOwner()->GetOwner() );
222 }
223
224 return m_dc;
225 }
226
227 // ---------------------------------------------------------
228 // wxDataViewProgressCell
229 // ---------------------------------------------------------
230
231 IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell, wxDataViewCustomCell)
232
233 wxDataViewProgressCell::wxDataViewProgressCell( const wxString &label,
234 const wxString &varianttype, wxDataViewCellMode mode ) :
235 wxDataViewCustomCell( varianttype, mode )
236 {
237 m_label = label;
238 m_value = 0;
239 }
240
241 wxDataViewProgressCell::~wxDataViewProgressCell()
242 {
243 }
244
245 bool wxDataViewProgressCell::SetValue( const wxVariant &value )
246 {
247 m_value = (long) value;
248
249 if (m_value < 0) m_value = 0;
250 if (m_value > 100) m_value = 100;
251
252 return true;
253 }
254
255 bool wxDataViewProgressCell::Render( wxRect cell, wxDC *dc, int state )
256 {
257 double pct = (double)m_value / 100.0;
258 wxRect bar = cell;
259 bar.width = (int)(cell.width * pct);
260 dc->SetPen( *wxTRANSPARENT_PEN );
261 dc->SetBrush( *wxBLUE_BRUSH );
262 dc->DrawRectangle( bar );
263
264 dc->SetBrush( *wxTRANSPARENT_BRUSH );
265 dc->SetPen( *wxBLACK_PEN );
266 dc->DrawRectangle( cell );
267
268 return true;
269 }
270
271 wxSize wxDataViewProgressCell::GetSize()
272 {
273 return wxSize(40,12);
274 }
275
276 // ---------------------------------------------------------
277 // wxDataViewDateCell
278 // ---------------------------------------------------------
279
280 class wxDataViewDateCellPopupTransient: public wxPopupTransientWindow
281 {
282 public:
283 wxDataViewDateCellPopupTransient( wxWindow* parent, wxDateTime *value,
284 wxDataViewListModel *model, size_t col, size_t row ) :
285 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
286 {
287 m_model = model;
288 m_col = col;
289 m_row = row;
290 m_cal = new wxCalendarCtrl( this, -1, *value );
291 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
292 sizer->Add( m_cal, 1, wxGROW );
293 SetSizer( sizer );
294 sizer->Fit( this );
295 }
296
297 virtual void OnDismiss()
298 {
299 }
300
301 void OnCalendar( wxCalendarEvent &event );
302
303 wxCalendarCtrl *m_cal;
304 wxDataViewListModel *m_model;
305 size_t m_col;
306 size_t m_row;
307
308 private:
309 DECLARE_EVENT_TABLE()
310 };
311
312 BEGIN_EVENT_TABLE(wxDataViewDateCellPopupTransient,wxPopupTransientWindow)
313 EVT_CALENDAR( -1, wxDataViewDateCellPopupTransient::OnCalendar )
314 END_EVENT_TABLE()
315
316 void wxDataViewDateCellPopupTransient::OnCalendar( wxCalendarEvent &event )
317 {
318 wxDateTime date = event.GetDate();
319 wxVariant value = date;
320 m_model->SetValue( value, m_col, m_row );
321 m_model->ValueChanged( m_col, m_row );
322 DismissAndNotify();
323 }
324
325 IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateCell, wxDataViewCustomCell)
326
327 wxDataViewDateCell::wxDataViewDateCell( const wxString &varianttype,
328 wxDataViewCellMode mode ) :
329 wxDataViewCustomCell( varianttype, mode )
330 {
331 }
332
333 bool wxDataViewDateCell::SetValue( const wxVariant &value )
334 {
335 m_date = value.GetDateTime();
336
337 return true;
338 }
339
340 bool wxDataViewDateCell::Render( wxRect cell, wxDC *dc, int state )
341 {
342 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
343 wxString tmp = m_date.FormatDate();
344 dc->DrawText( tmp, cell.x, cell.y );
345
346 return true;
347 }
348
349 wxSize wxDataViewDateCell::GetSize()
350 {
351 wxDataViewCtrl* view = GetOwner()->GetOwner();
352 wxString tmp = m_date.FormatDate();
353 wxCoord x,y,d;
354 view->GetTextExtent( tmp, &x, &y, &d );
355 return wxSize(x,y+d);
356 }
357
358 bool wxDataViewDateCell::Activate( wxRect cell, wxDataViewListModel *model, size_t col, size_t row )
359 {
360 wxVariant variant;
361 model->GetValue( variant, col, row );
362 wxDateTime value = variant.GetDateTime();
363
364 wxDataViewDateCellPopupTransient *popup = new wxDataViewDateCellPopupTransient(
365 GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
366 wxPoint pos = wxGetMousePosition();
367 popup->Move( pos );
368 popup->Layout();
369 popup->Popup( popup->m_cal );
370
371 return true;
372 }
373
374 // ---------------------------------------------------------
375 // wxDataViewColumn
376 // ---------------------------------------------------------
377
378 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
379
380 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewCell *cell,
381 size_t model_column, int flags ) :
382 wxDataViewColumnBase( title, cell, model_column, flags )
383 {
384 m_width = 80;
385 }
386
387 wxDataViewColumn::~wxDataViewColumn()
388 {
389 }
390
391 void wxDataViewColumn::SetTitle( const wxString &title )
392 {
393 wxDataViewColumnBase::SetTitle( title );
394
395 }
396
397 //-----------------------------------------------------------------------------
398 // wxDataViewHeaderWindow
399 //-----------------------------------------------------------------------------
400
401 class wxDataViewHeaderWindow: public wxWindow
402 {
403 public:
404 wxDataViewHeaderWindow( wxDataViewCtrl *parent,
405 wxWindowID id,
406 const wxPoint &pos = wxDefaultPosition,
407 const wxSize &size = wxDefaultSize,
408 const wxString &name = wxT("wxdataviewctrlheaderwindow") );
409 ~wxDataViewHeaderWindow();
410
411 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
412 wxDataViewCtrl *GetOwner() { return m_owner; }
413
414 void OnPaint( wxPaintEvent &event );
415 void OnMouse( wxMouseEvent &event );
416 void OnSetFocus( wxFocusEvent &event );
417
418 private:
419 wxDataViewCtrl *m_owner;
420 wxCursor *m_resizeCursor;
421
422 private:
423 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindow)
424 DECLARE_EVENT_TABLE()
425 };
426
427 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow)
428
429 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow)
430 EVT_PAINT (wxDataViewHeaderWindow::OnPaint)
431 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse)
432 EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus)
433 END_EVENT_TABLE()
434
435 wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id,
436 const wxPoint &pos, const wxSize &size, const wxString &name ) :
437 wxWindow( parent, id, pos, size, 0, name )
438 {
439 SetOwner( parent );
440
441 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
442
443 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
444 SetOwnForegroundColour( attr.colFg );
445 SetOwnBackgroundColour( attr.colBg );
446 if (!m_hasFont)
447 SetOwnFont( attr.font );
448 }
449
450 wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
451 {
452 delete m_resizeCursor;
453 }
454
455 void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &event )
456 {
457 int w, h;
458 GetClientSize( &w, &h );
459
460 wxPaintDC dc( this );
461
462 int xpix;
463 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
464
465 int x;
466 m_owner->GetViewStart( &x, NULL );
467
468 // account for the horz scrollbar offset
469 dc.SetDeviceOrigin( -x * xpix, 0 );
470
471 dc.SetFont( GetFont() );
472
473 size_t cols = GetOwner()->GetNumberOfColumns();
474 size_t i;
475 int xpos = 0;
476 for (i = 0; i < cols; i++)
477 {
478 wxDataViewColumn *col = GetOwner()->GetColumn( i );
479 int width = col->GetWidth();
480
481 // the width of the rect to draw: make it smaller to fit entirely
482 // inside the column rect
483 #ifdef __WXMAC__
484 int cw = width;
485 int ch = h;
486 #else
487 int cw = width - 2;
488 int ch = h - 2;
489 #endif
490
491 wxRendererNative::Get().DrawHeaderButton
492 (
493 this,
494 dc,
495 wxRect(xpos, 0, cw, ch),
496 m_parent->IsEnabled() ? 0
497 : (int)wxCONTROL_DISABLED
498 );
499
500 dc.DrawText( col->GetTitle(), xpos+3, 3 );
501
502 xpos += width;
503 }
504 }
505
506 void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &event )
507 {
508 }
509
510 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
511 {
512 event.Skip();
513 }
514
515 //-----------------------------------------------------------------------------
516 // wxDataViewMainWindow
517 //-----------------------------------------------------------------------------
518
519 class wxDataViewMainWindow: public wxWindow
520 {
521 public:
522 wxDataViewMainWindow( wxDataViewCtrl *parent,
523 wxWindowID id,
524 const wxPoint &pos = wxDefaultPosition,
525 const wxSize &size = wxDefaultSize,
526 const wxString &name = wxT("wxdataviewctrlmainwindow") );
527 ~wxDataViewMainWindow();
528
529 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
530 wxDataViewCtrl *GetOwner() { return m_owner; }
531
532 void OnPaint( wxPaintEvent &event );
533 void OnMouse( wxMouseEvent &event );
534 void OnSetFocus( wxFocusEvent &event );
535
536 void UpdateDisplay();
537 void RecalculateDisplay();
538 void OnInternalIdle();
539
540 void ScrollWindow( int dx, int dy, const wxRect *rect );
541 private:
542 wxDataViewCtrl *m_owner;
543 int m_lineHeight;
544 bool m_dirty;
545
546 private:
547 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
548 DECLARE_EVENT_TABLE()
549 };
550
551 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
552
553 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
554 EVT_PAINT (wxDataViewMainWindow::OnPaint)
555 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
556 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
557 END_EVENT_TABLE()
558
559 wxDataViewMainWindow::wxDataViewMainWindow( 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 // We need to calculate this smartly..
566 m_lineHeight = 20;
567
568 UpdateDisplay();
569 }
570
571 wxDataViewMainWindow::~wxDataViewMainWindow()
572 {
573 }
574
575 void wxDataViewMainWindow::UpdateDisplay()
576 {
577 m_dirty = true;
578 }
579
580 void wxDataViewMainWindow::OnInternalIdle()
581 {
582 wxWindow::OnInternalIdle();
583
584 if (m_dirty)
585 {
586 RecalculateDisplay();
587 m_dirty = false;
588 }
589 }
590
591 void wxDataViewMainWindow::RecalculateDisplay()
592 {
593 wxDataViewListModel *model = GetOwner()->GetModel();
594 if (!model)
595 {
596 Refresh();
597 return;
598 }
599
600 int width = 0;
601 size_t cols = GetOwner()->GetNumberOfColumns();
602 size_t i;
603 for (i = 0; i < cols; i++)
604 {
605 wxDataViewColumn *col = GetOwner()->GetColumn( i );
606 width += col->GetWidth();
607 }
608
609 int height = model->GetNumberOfRows() * m_lineHeight;
610
611 SetVirtualSize( width, height );
612 GetOwner()->SetScrollRate( 10, m_lineHeight );
613
614 Refresh();
615 }
616
617 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
618 {
619 wxWindow::ScrollWindow( dx, dy, rect );
620 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
621 }
622
623 void wxDataViewMainWindow::OnPaint( wxPaintEvent &event )
624 {
625 wxPaintDC dc( this );
626
627 GetOwner()->PrepareDC( dc );
628
629 dc.SetFont( GetFont() );
630
631 dc.DrawText( wxT("main window"), 5, 5 );
632 }
633
634 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
635 {
636 event.Skip();
637 }
638
639 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
640 {
641 event.Skip();
642 }
643
644 //-----------------------------------------------------------------------------
645 // wxDataViewCtrl
646 //-----------------------------------------------------------------------------
647
648 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
649
650 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
651 EVT_SIZE(wxDataViewCtrl::OnSize)
652 END_EVENT_TABLE()
653
654 wxDataViewCtrl::~wxDataViewCtrl()
655 {
656 if (m_notifier)
657 GetModel()->RemoveNotifier( m_notifier );
658 }
659
660 void wxDataViewCtrl::Init()
661 {
662 m_notifier = NULL;
663 }
664
665 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
666 const wxPoint& pos, const wxSize& size,
667 long style, const wxValidator& validator )
668 {
669 if (!wxControl::Create( parent, id, pos, size, style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
670 return false;
671
672 Init();
673
674 #ifdef __WXMAC__
675 MacSetClipChildren( true ) ;
676 #endif
677
678 m_clientArea = new wxDataViewMainWindow( this, -1 );
679 m_headerArea = new wxDataViewHeaderWindow( this, -1, wxDefaultPosition, wxSize(-1,25) );
680
681 SetTargetWindow( m_clientArea );
682
683 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
684 sizer->Add( m_headerArea, 0, wxGROW );
685 sizer->Add( m_clientArea, 1, wxGROW );
686 SetSizer( sizer );
687
688 return true;
689 }
690
691 #ifdef __WXMSW__
692 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
693 WXWPARAM wParam,
694 WXLPARAM lParam)
695 {
696 WXLRESULT rc = wxPanel::MSWWindowProc(nMsg, wParam, lParam);
697
698 #ifndef __WXWINCE__
699 // we need to process arrows ourselves for scrolling
700 if ( nMsg == WM_GETDLGCODE )
701 {
702 rc |= DLGC_WANTARROWS;
703 }
704 #endif
705
706 return rc;
707 }
708 #endif
709
710 void wxDataViewCtrl::OnSize( wxSizeEvent &event )
711 {
712 // We need to override OnSize so that our scrolled
713 // window a) does call Layout() to use sizers for
714 // positioning the controls but b) does not query
715 // the sizer for their size and use that for setting
716 // the scrollable area as set that ourselves by
717 // calling SetScrollbar() further down.
718
719 Layout();
720
721 AdjustScrollbars();
722 }
723
724 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
725 {
726 if (!wxDataViewCtrlBase::AssociateModel( model ))
727 return false;
728
729 m_notifier = new wxGenericDataViewListModelNotifier( model );
730
731 model->AddNotifier( m_notifier );
732
733 m_clientArea->UpdateDisplay();
734
735 return true;
736 }
737
738 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
739 {
740 if (!wxDataViewCtrlBase::AppendColumn(col))
741 return false;
742
743 m_clientArea->UpdateDisplay();
744
745 return true;
746 }
747
748 #endif
749 // !wxUSE_GENERICDATAVIEWCTRL
750
751 #endif
752 // wxUSE_DATAVIEWCTRL
753