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