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