]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datavgen.cpp
Added sort order and alignement for column headers.
[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::SetSortOrder( bool WXUNUSED(ascending) )
621 {
622 // TODO
623 }
624
625 wxDataViewColumn::~wxDataViewColumn()
626 {
627 }
628
629 void wxDataViewColumn::SetTitle( const wxString &title )
630 {
631 wxDataViewColumnBase::SetTitle( title );
632
633 }
634
635 void wxDataViewColumn::SetBitmap( const wxBitmap &bitmap )
636 {
637 wxDataViewColumnBase::SetBitmap( bitmap );
638
639 }
640
641 int wxDataViewColumn::GetWidth()
642 {
643 return m_width;
644 }
645
646 //-----------------------------------------------------------------------------
647 // wxDataViewHeaderWindow
648 //-----------------------------------------------------------------------------
649
650 IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow)
651
652 BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow)
653 EVT_PAINT (wxDataViewHeaderWindow::OnPaint)
654 EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse)
655 EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus)
656 END_EVENT_TABLE()
657
658 wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id,
659 const wxPoint &pos, const wxSize &size, const wxString &name ) :
660 wxWindow( parent, id, pos, size, 0, name )
661 {
662 SetOwner( parent );
663
664 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
665
666 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
667 SetOwnForegroundColour( attr.colFg );
668 SetOwnBackgroundColour( attr.colBg );
669 if (!m_hasFont)
670 SetOwnFont( attr.font );
671 }
672
673 wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
674 {
675 delete m_resizeCursor;
676 }
677
678 void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
679 {
680 int w, h;
681 GetClientSize( &w, &h );
682
683 wxPaintDC dc( this );
684
685 int xpix;
686 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
687
688 int x;
689 m_owner->GetViewStart( &x, NULL );
690
691 // account for the horz scrollbar offset
692 dc.SetDeviceOrigin( -x * xpix, 0 );
693
694 dc.SetFont( GetFont() );
695
696 unsigned int cols = GetOwner()->GetNumberOfColumns();
697 unsigned int i;
698 int xpos = 0;
699 for (i = 0; i < cols; i++)
700 {
701 wxDataViewColumn *col = GetOwner()->GetColumn( i );
702 int width = col->GetWidth();
703
704 int cw = width;
705 int ch = h;
706
707 wxRendererNative::Get().DrawHeaderButton
708 (
709 this,
710 dc,
711 wxRect(xpos, 0, cw, ch-1),
712 m_parent->IsEnabled() ? 0
713 : (int)wxCONTROL_DISABLED
714 );
715
716 dc.DrawText( col->GetTitle(), xpos+3, 3 );
717
718 xpos += width;
719 }
720 }
721
722 void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) )
723 {
724 }
725
726 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
727 {
728 GetParent()->SetFocus();
729 event.Skip();
730 }
731
732 //-----------------------------------------------------------------------------
733 // wxDataViewRenameTimer
734 //-----------------------------------------------------------------------------
735
736 wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
737 {
738 m_owner = owner;
739 }
740
741 void wxDataViewRenameTimer::Notify()
742 {
743 m_owner->OnRenameTimer();
744 }
745
746 //-----------------------------------------------------------------------------
747 // wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
748 //-----------------------------------------------------------------------------
749
750 BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler)
751 EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar)
752 EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp)
753 EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus)
754 END_EVENT_TABLE()
755
756 wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
757 wxDataViewMainWindow *owner,
758 wxTextCtrl *text,
759 wxDataViewListModel *model,
760 unsigned int col, unsigned int row,
761 wxRect rectLabel )
762 {
763 m_owner = owner;
764 m_model = model;
765 m_row = row;
766 m_col = col;
767 m_text = text;
768
769 m_finished = false;
770 m_aboutToFinish = false;
771
772 wxVariant value;
773 model->GetValue( value, col, row );
774 m_startValue = value.GetString();
775
776 m_owner->GetOwner()->CalcScrolledPosition(
777 rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
778
779 m_text->Create( owner, wxID_ANY, m_startValue,
780 wxPoint(rectLabel.x-2,rectLabel.y-2),
781 wxSize(rectLabel.width+7,rectLabel.height+4) );
782 m_text->SetFocus();
783
784 m_text->PushEventHandler(this);
785 }
786
787 void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
788 {
789 m_aboutToFinish = true;
790
791 // Notify the owner about the changes
792 AcceptChanges();
793
794 // Even if vetoed, close the control (consistent with MSW)
795 Finish();
796 }
797
798 void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent &event )
799 {
800 switch ( event.m_keyCode )
801 {
802 case WXK_RETURN:
803 AcceptChangesAndFinish();
804 break;
805
806 case WXK_ESCAPE:
807 // m_owner->OnRenameCancelled( m_itemEdited );
808 Finish();
809 break;
810
811 default:
812 event.Skip();
813 }
814 }
815
816 void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
817 {
818 if (m_finished)
819 {
820 event.Skip();
821 return;
822 }
823
824 // auto-grow the textctrl
825 wxSize parentSize = m_owner->GetSize();
826 wxPoint myPos = m_text->GetPosition();
827 wxSize mySize = m_text->GetSize();
828 int sx, sy;
829 m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
830 if (myPos.x + sx > parentSize.x)
831 sx = parentSize.x - myPos.x;
832 if (mySize.x > sx)
833 sx = mySize.x;
834 m_text->SetSize(sx, wxDefaultCoord);
835
836 event.Skip();
837 }
838
839 void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
840 {
841 if ( !m_finished && !m_aboutToFinish )
842 {
843 AcceptChanges();
844 //if ( !AcceptChanges() )
845 // m_owner->OnRenameCancelled( m_itemEdited );
846
847 Finish();
848 }
849
850 // We must let the native text control handle focus
851 event.Skip();
852 }
853
854 bool wxDataViewTextCtrlWrapper::AcceptChanges()
855 {
856 const wxString value = m_text->GetValue();
857
858 if ( value == m_startValue )
859 // nothing changed, always accept
860 return true;
861
862 // if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
863 // vetoed by the user
864 // return false;
865
866 // accepted, do rename the item
867 wxVariant variant;
868 variant = value;
869 m_model->SetValue( variant, m_col, m_row );
870 m_model->ValueChanged( m_col, m_row );
871
872 return true;
873 }
874
875 void wxDataViewTextCtrlWrapper::Finish()
876 {
877 if ( !m_finished )
878 {
879 m_finished = true;
880
881 m_text->RemoveEventHandler(this);
882 m_owner->FinishEditing(m_text);
883
884 // delete later
885 wxPendingDelete.Append( this );
886 }
887 }
888
889 //-----------------------------------------------------------------------------
890 // wxDataViewMainWindow
891 //-----------------------------------------------------------------------------
892
893 int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
894 {
895 if (row1 > row2) return 1;
896 if (row1 == row2) return 0;
897 return -1;
898 }
899
900
901 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
902
903 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
904 EVT_PAINT (wxDataViewMainWindow::OnPaint)
905 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
906 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
907 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
908 EVT_CHAR (wxDataViewMainWindow::OnChar)
909 END_EVENT_TABLE()
910
911 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
912 const wxPoint &pos, const wxSize &size, const wxString &name ) :
913 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
914 m_selection( wxDataViewSelectionCmp )
915
916 {
917 SetOwner( parent );
918
919 m_lastOnSame = false;
920 m_renameTimer = new wxDataViewRenameTimer( this );
921 m_textctrlWrapper = NULL;
922
923 // TODO: user better initial values/nothing selected
924 m_currentCol = NULL;
925 m_currentRow = 0;
926
927 // TODO: we need to calculate this smartly
928 m_lineHeight = 20;
929
930 m_dragCount = 0;
931 m_dragStart = wxPoint(0,0);
932 m_lineLastClicked = (unsigned int) -1;
933 m_lineBeforeLastClicked = (unsigned int) -1;
934 m_lineSelectSingleOnUp = (unsigned int) -1;
935
936 m_hasFocus = false;
937
938 SetBackgroundColour( *wxWHITE );
939
940 UpdateDisplay();
941 }
942
943 wxDataViewMainWindow::~wxDataViewMainWindow()
944 {
945 delete m_renameTimer;
946 }
947
948 void wxDataViewMainWindow::OnRenameTimer()
949 {
950 // We have to call this here because changes may just have
951 // been made and no screen update taken place.
952 if ( m_dirty )
953 wxSafeYield();
954
955
956 int xpos = 0;
957 unsigned int cols = GetOwner()->GetNumberOfColumns();
958 unsigned int i;
959 for (i = 0; i < cols; i++)
960 {
961 wxDataViewColumn *c = GetOwner()->GetColumn( i );
962 if (c == m_currentCol)
963 break;
964 xpos += c->GetWidth();
965 }
966 wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight );
967
968 wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
969
970 wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
971 m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
972 m_currentCol->GetModelColumn(), m_currentRow, labelRect );
973 }
974
975 void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text )
976 {
977 delete text;
978 m_textctrlWrapper = NULL;
979 SetFocus();
980 // SetFocusIgnoringChildren();
981 }
982
983 bool wxDataViewMainWindow::RowAppended()
984 {
985 return false;
986 }
987
988 bool wxDataViewMainWindow::RowPrepended()
989 {
990 return false;
991 }
992
993 bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before) )
994 {
995 return false;
996 }
997
998 bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row) )
999 {
1000 return false;
1001 }
1002
1003 bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row) )
1004 {
1005 return false;
1006 }
1007
1008 bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col), unsigned int row )
1009 {
1010 wxRect rect( 0, row*m_lineHeight, 10000, m_lineHeight );
1011 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1012 Refresh( true, &rect );
1013
1014 return true;
1015 }
1016
1017 bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order) )
1018 {
1019 Refresh();
1020
1021 return true;
1022 }
1023
1024 bool wxDataViewMainWindow::Cleared()
1025 {
1026 return false;
1027 }
1028
1029 void wxDataViewMainWindow::UpdateDisplay()
1030 {
1031 m_dirty = true;
1032 }
1033
1034 void wxDataViewMainWindow::OnInternalIdle()
1035 {
1036 wxWindow::OnInternalIdle();
1037
1038 if (m_dirty)
1039 {
1040 RecalculateDisplay();
1041 m_dirty = false;
1042 }
1043 }
1044
1045 void wxDataViewMainWindow::RecalculateDisplay()
1046 {
1047 wxDataViewListModel *model = GetOwner()->GetModel();
1048 if (!model)
1049 {
1050 Refresh();
1051 return;
1052 }
1053
1054 int width = 0;
1055 unsigned int cols = GetOwner()->GetNumberOfColumns();
1056 unsigned int i;
1057 for (i = 0; i < cols; i++)
1058 {
1059 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1060 width += col->GetWidth();
1061 }
1062
1063 int height = model->GetNumberOfRows() * m_lineHeight;
1064
1065 SetVirtualSize( width, height );
1066 GetOwner()->SetScrollRate( 10, m_lineHeight );
1067
1068 Refresh();
1069 }
1070
1071 void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1072 {
1073 wxWindow::ScrollWindow( dx, dy, rect );
1074 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
1075 }
1076
1077 void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1078 {
1079 wxPaintDC dc( this );
1080
1081 GetOwner()->PrepareDC( dc );
1082
1083 dc.SetFont( GetFont() );
1084
1085 wxRect update = GetUpdateRegion().GetBox();
1086 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1087
1088 wxDataViewListModel *model = GetOwner()->GetModel();
1089
1090 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
1091 unsigned int item_count = wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
1092 (int)(model->GetNumberOfRows()-item_start) );
1093
1094
1095
1096 unsigned int item;
1097 for (item = item_start; item < item_start+item_count; item++)
1098 {
1099 if (m_selection.Index( item ) != wxNOT_FOUND)
1100 {
1101 int flags = wxCONTROL_SELECTED;
1102 if (item == m_currentRow)
1103 flags |= wxCONTROL_CURRENT;
1104 if (m_hasFocus)
1105 flags |= wxCONTROL_FOCUSED;
1106 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1107 wxRendererNative::Get().DrawItemSelectionRect
1108 (
1109 this,
1110 dc,
1111 rect,
1112 flags
1113 );
1114 }
1115 else
1116 {
1117 if (item == m_currentRow)
1118 {
1119 int flags = wxCONTROL_CURRENT;
1120 if (m_hasFocus)
1121 flags |= wxCONTROL_FOCUSED; // should have no effect
1122 wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
1123 wxRendererNative::Get().DrawItemSelectionRect
1124 (
1125 this,
1126 dc,
1127 rect,
1128 flags
1129 );
1130
1131 }
1132 }
1133 }
1134
1135 wxRect cell_rect;
1136 cell_rect.x = 0;
1137 cell_rect.height = m_lineHeight;
1138 unsigned int cols = GetOwner()->GetNumberOfColumns();
1139 unsigned int i;
1140 for (i = 0; i < cols; i++)
1141 {
1142 wxDataViewColumn *col = GetOwner()->GetColumn( i );
1143 wxDataViewRenderer *cell = col->GetRenderer();
1144 cell_rect.width = col->GetWidth();
1145
1146 for (item = item_start; item < item_start+item_count; item++)
1147 {
1148 cell_rect.y = item*m_lineHeight;
1149 wxVariant value;
1150 model->GetValue( value, col->GetModelColumn(), item );
1151 cell->SetValue( value );
1152 wxSize size = cell->GetSize();
1153 // cannot be bigger than allocated space
1154 size.x = wxMin( size.x, cell_rect.width );
1155 size.y = wxMin( size.y, cell_rect.height );
1156 // TODO: check for left/right/centre alignment here
1157 wxRect item_rect;
1158 // for now: centre
1159 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1160 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
1161
1162 item_rect.width = size.x;
1163 item_rect.height= size.y;
1164 cell->Render( item_rect, &dc, 0 );
1165 }
1166
1167 cell_rect.x += cell_rect.width;
1168 }
1169 }
1170
1171 int wxDataViewMainWindow::GetCountPerPage()
1172 {
1173 wxSize size = GetClientSize();
1174 return size.y / m_lineHeight;
1175 }
1176
1177 int wxDataViewMainWindow::GetEndOfLastCol()
1178 {
1179 int width = 0;
1180 unsigned int i;
1181 for (i = 0; i < GetOwner()->GetNumberOfColumns(); i++)
1182 {
1183 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1184 width += c->GetWidth();
1185 }
1186 return width;
1187 }
1188
1189 unsigned int wxDataViewMainWindow::GetFirstVisibleRow()
1190 {
1191 int x = 0;
1192 int y = 0;
1193 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1194
1195 return y / m_lineHeight;
1196 }
1197
1198 unsigned int wxDataViewMainWindow::GetLastVisibleRow()
1199 {
1200 wxSize client_size = GetClientSize();
1201 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y, &client_size.x, &client_size.y );
1202
1203 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
1204 }
1205
1206 unsigned int wxDataViewMainWindow::GetRowCount()
1207 {
1208 return GetOwner()->GetModel()->GetNumberOfRows();
1209 }
1210
1211 void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
1212 {
1213 m_currentRow = row;
1214
1215 // send event
1216 }
1217
1218 void wxDataViewMainWindow::SelectAllRows( bool on )
1219 {
1220 if (IsEmpty())
1221 return;
1222
1223 if (on)
1224 {
1225 m_selection.Clear();
1226 for (unsigned int i = 0; i < GetRowCount(); i++)
1227 m_selection.Add( i );
1228 Refresh();
1229 }
1230 else
1231 {
1232 unsigned int first_visible = GetFirstVisibleRow();
1233 unsigned int last_visible = GetLastVisibleRow();
1234 unsigned int i;
1235 for (i = 0; i < m_selection.GetCount(); i++)
1236 {
1237 unsigned int row = m_selection[i];
1238 if ((row >= first_visible) && (row <= last_visible))
1239 RefreshRow( row );
1240 }
1241 m_selection.Clear();
1242 }
1243 }
1244
1245 void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
1246 {
1247 if (m_selection.Index( row ) == wxNOT_FOUND)
1248 {
1249 if (on)
1250 {
1251 m_selection.Add( row );
1252 RefreshRow( row );
1253 }
1254 }
1255 else
1256 {
1257 if (!on)
1258 {
1259 m_selection.Remove( row );
1260 RefreshRow( row );
1261 }
1262 }
1263 }
1264
1265 void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
1266 {
1267 if (from > to)
1268 {
1269 unsigned int tmp = from;
1270 from = to;
1271 to = tmp;
1272 }
1273
1274 unsigned int i;
1275 for (i = from; i <= to; i++)
1276 {
1277 if (m_selection.Index( i ) == wxNOT_FOUND)
1278 {
1279 if (on)
1280 m_selection.Add( i );
1281 }
1282 else
1283 {
1284 if (!on)
1285 m_selection.Remove( i );
1286 }
1287 }
1288 RefreshRows( from, to );
1289 }
1290
1291 void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
1292 {
1293 if (m_selection.Index( row ) == wxNOT_FOUND)
1294 m_selection.Add( row );
1295 else
1296 m_selection.Remove( row );
1297 RefreshRow( row );
1298 }
1299
1300 bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
1301 {
1302 return (m_selection.Index( row ) != wxNOT_FOUND);
1303 }
1304
1305 void wxDataViewMainWindow::RefreshRow( unsigned int row )
1306 {
1307 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
1308 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1309
1310 wxSize client_size = GetClientSize();
1311 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1312 wxRect intersect_rect = client_rect.Intersect( rect );
1313 if (intersect_rect.width > 0)
1314 Refresh( true, &intersect_rect );
1315 }
1316
1317 void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
1318 {
1319 if (from > to)
1320 {
1321 unsigned int tmp = to;
1322 to = from;
1323 from = tmp;
1324 }
1325
1326 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
1327 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1328
1329 wxSize client_size = GetClientSize();
1330 wxRect client_rect( 0, 0, client_size.x, client_size.y );
1331 wxRect intersect_rect = client_rect.Intersect( rect );
1332 if (intersect_rect.width > 0)
1333 Refresh( true, &intersect_rect );
1334 }
1335
1336 void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
1337 {
1338 unsigned int count = GetRowCount();
1339 if (firstRow > count)
1340 return;
1341
1342 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * 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::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
1353 {
1354 wxCHECK_RET( newCurrent < GetRowCount(),
1355 _T("invalid item index in OnArrowChar()") );
1356
1357 // if there is no selection, we cannot move it anywhere
1358 if (!HasCurrentRow())
1359 return;
1360
1361 unsigned int oldCurrent = m_currentRow;
1362
1363 // in single selection we just ignore Shift as we can't select several
1364 // items anyhow
1365 if ( event.ShiftDown() && !IsSingleSel() )
1366 {
1367 RefreshRow( oldCurrent );
1368
1369 ChangeCurrentRow( newCurrent );
1370
1371 // select all the items between the old and the new one
1372 if ( oldCurrent > newCurrent )
1373 {
1374 newCurrent = oldCurrent;
1375 oldCurrent = m_currentRow;
1376 }
1377
1378 SelectRows( oldCurrent, newCurrent, true );
1379 }
1380 else // !shift
1381 {
1382 RefreshRow( oldCurrent );
1383
1384 // all previously selected items are unselected unless ctrl is held
1385 if ( !event.ControlDown() )
1386 SelectAllRows(false);
1387
1388 ChangeCurrentRow( newCurrent );
1389
1390 if ( !event.ControlDown() )
1391 SelectRow( m_currentRow, true );
1392 else
1393 RefreshRow( m_currentRow );
1394 }
1395
1396 // MoveToFocus();
1397 }
1398
1399 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
1400 {
1401 if (event.GetKeyCode() == WXK_TAB)
1402 {
1403 wxNavigationKeyEvent nevent;
1404 nevent.SetWindowChange( event.ControlDown() );
1405 nevent.SetDirection( !event.ShiftDown() );
1406 nevent.SetEventObject( GetParent()->GetParent() );
1407 nevent.SetCurrentFocus( m_parent );
1408 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
1409 return;
1410 }
1411
1412 // no item -> nothing to do
1413 if (!HasCurrentRow())
1414 {
1415 event.Skip();
1416 return;
1417 }
1418
1419 // don't use m_linesPerPage directly as it might not be computed yet
1420 const int pageSize = GetCountPerPage();
1421 wxCHECK_RET( pageSize, _T("should have non zero page size") );
1422
1423 switch ( event.GetKeyCode() )
1424 {
1425 case WXK_UP:
1426 if ( m_currentRow > 0 )
1427 OnArrowChar( m_currentRow - 1, event );
1428 break;
1429
1430 case WXK_DOWN:
1431 if ( m_currentRow < GetRowCount() - 1 )
1432 OnArrowChar( m_currentRow + 1, event );
1433 break;
1434
1435 case WXK_END:
1436 if (!IsEmpty())
1437 OnArrowChar( GetRowCount() - 1, event );
1438 break;
1439
1440 case WXK_HOME:
1441 if (!IsEmpty())
1442 OnArrowChar( 0, event );
1443 break;
1444
1445 case WXK_PAGEUP:
1446 {
1447 int steps = pageSize - 1;
1448 int index = m_currentRow - steps;
1449 if (index < 0)
1450 index = 0;
1451
1452 OnArrowChar( index, event );
1453 }
1454 break;
1455
1456 case WXK_PAGEDOWN:
1457 {
1458 int steps = pageSize - 1;
1459 unsigned int index = m_currentRow + steps;
1460 unsigned int count = GetRowCount();
1461 if ( index >= count )
1462 index = count - 1;
1463
1464 OnArrowChar( index, event );
1465 }
1466 break;
1467
1468 default:
1469 event.Skip();
1470 }
1471 }
1472
1473 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
1474 {
1475 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
1476 {
1477 // let the base handle mouse wheel events.
1478 event.Skip();
1479 return;
1480 }
1481
1482 int x = event.GetX();
1483 int y = event.GetY();
1484 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
1485
1486 wxDataViewColumn *col = NULL;
1487
1488 int xpos = 0;
1489 unsigned int cols = GetOwner()->GetNumberOfColumns();
1490 unsigned int i;
1491 for (i = 0; i < cols; i++)
1492 {
1493 wxDataViewColumn *c = GetOwner()->GetColumn( i );
1494 if (x < xpos + c->GetWidth())
1495 {
1496 col = c;
1497 break;
1498 }
1499 xpos += c->GetWidth();
1500 }
1501 if (!col)
1502 return;
1503 wxDataViewRenderer *cell = col->GetRenderer();
1504
1505 unsigned int current = y / m_lineHeight;
1506
1507 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
1508 {
1509 // Unselect all if below the last row ?
1510 return;
1511 }
1512
1513 wxDataViewListModel *model = GetOwner()->GetModel();
1514
1515 if (event.Dragging())
1516 {
1517 if (m_dragCount == 0)
1518 {
1519 // we have to report the raw, physical coords as we want to be
1520 // able to call HitTest(event.m_pointDrag) from the user code to
1521 // get the item being dragged
1522 m_dragStart = event.GetPosition();
1523 }
1524
1525 m_dragCount++;
1526
1527 if (m_dragCount != 3)
1528 return;
1529
1530 if (event.LeftIsDown())
1531 {
1532 // Notify cell about drag
1533 }
1534 return;
1535 }
1536 else
1537 {
1538 m_dragCount = 0;
1539 }
1540
1541 bool forceClick = false;
1542
1543 if (event.ButtonDClick())
1544 {
1545 m_renameTimer->Stop();
1546 m_lastOnSame = false;
1547 }
1548
1549 if (event.LeftDClick())
1550 {
1551 if ( current == m_lineLastClicked )
1552 {
1553 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
1554 {
1555 wxVariant value;
1556 model->GetValue( value, col->GetModelColumn(), current );
1557 cell->SetValue( value );
1558 wxRect cell_rect( xpos, current * m_lineHeight, col->GetWidth(), m_lineHeight );
1559 cell->Activate( cell_rect, model, col->GetModelColumn(), current );
1560 }
1561 return;
1562 }
1563 else
1564 {
1565 // The first click was on another item, so don't interpret this as
1566 // a double click, but as a simple click instead
1567 forceClick = true;
1568 }
1569 }
1570
1571 if (event.LeftUp())
1572 {
1573 if (m_lineSelectSingleOnUp != (unsigned int)-1)
1574 {
1575 // select single line
1576 SelectAllRows( false );
1577 SelectRow( m_lineSelectSingleOnUp, true );
1578 }
1579
1580 if (m_lastOnSame)
1581 {
1582 if ((col == m_currentCol) && (current == m_currentRow) &&
1583 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
1584 {
1585 m_renameTimer->Start( 100, true );
1586 }
1587 }
1588
1589 m_lastOnSame = false;
1590 m_lineSelectSingleOnUp = (unsigned int)-1;
1591 }
1592 else
1593 {
1594 // This is necessary, because after a DnD operation in
1595 // from and to ourself, the up event is swallowed by the
1596 // DnD code. So on next non-up event (which means here and
1597 // now) m_lineSelectSingleOnUp should be reset.
1598 m_lineSelectSingleOnUp = (unsigned int)-1;
1599 }
1600
1601 if (event.RightDown())
1602 {
1603 m_lineBeforeLastClicked = m_lineLastClicked;
1604 m_lineLastClicked = current;
1605
1606 // If the item is already selected, do not update the selection.
1607 // Multi-selections should not be cleared if a selected item is clicked.
1608 if (!IsRowSelected(current))
1609 {
1610 SelectAllRows(false);
1611 ChangeCurrentRow(current);
1612 SelectRow(m_currentRow,true);
1613 }
1614
1615 // notify cell about right click
1616 // cell->...
1617
1618 // Allow generation of context menu event
1619 event.Skip();
1620 }
1621 else if (event.MiddleDown())
1622 {
1623 // notify cell about middle click
1624 // cell->...
1625 }
1626 if (event.LeftDown() || forceClick)
1627 {
1628 #ifdef __WXMSW__
1629 SetFocus();
1630 #endif
1631
1632 m_lineBeforeLastClicked = m_lineLastClicked;
1633 m_lineLastClicked = current;
1634
1635 unsigned int oldCurrentRow = m_currentRow;
1636 bool oldWasSelected = IsRowSelected(m_currentRow);
1637
1638 bool cmdModifierDown = event.CmdDown();
1639 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
1640 {
1641 if ( IsSingleSel() || !IsRowSelected(current) )
1642 {
1643 SelectAllRows( false );
1644
1645 ChangeCurrentRow(current);
1646
1647 SelectRow(m_currentRow,true);
1648 }
1649 else // multi sel & current is highlighted & no mod keys
1650 {
1651 m_lineSelectSingleOnUp = current;
1652 ChangeCurrentRow(current); // change focus
1653 }
1654 }
1655 else // multi sel & either ctrl or shift is down
1656 {
1657 if (cmdModifierDown)
1658 {
1659 ChangeCurrentRow(current);
1660
1661 ReverseRowSelection(m_currentRow);
1662 }
1663 else if (event.ShiftDown())
1664 {
1665 ChangeCurrentRow(current);
1666
1667 unsigned int lineFrom = oldCurrentRow,
1668 lineTo = current;
1669
1670 if ( lineTo < lineFrom )
1671 {
1672 lineTo = lineFrom;
1673 lineFrom = m_currentRow;
1674 }
1675
1676 SelectRows(lineFrom, lineTo, true);
1677 }
1678 else // !ctrl, !shift
1679 {
1680 // test in the enclosing if should make it impossible
1681 wxFAIL_MSG( _T("how did we get here?") );
1682 }
1683 }
1684
1685 if (m_currentRow != oldCurrentRow)
1686 RefreshRow( oldCurrentRow );
1687
1688 wxDataViewColumn *oldCurrentCol = m_currentCol;
1689
1690 // Update selection here...
1691 m_currentCol = col;
1692
1693 m_lastOnSame = !forceClick && ((col == oldCurrentCol) && (current == oldCurrentRow)) && oldWasSelected;
1694 }
1695 }
1696
1697 void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
1698 {
1699 m_hasFocus = true;
1700
1701 if (HasCurrentRow())
1702 Refresh();
1703
1704 event.Skip();
1705 }
1706
1707 void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
1708 {
1709 m_hasFocus = false;
1710
1711 if (HasCurrentRow())
1712 Refresh();
1713
1714 event.Skip();
1715 }
1716
1717 //-----------------------------------------------------------------------------
1718 // wxDataViewCtrl
1719 //-----------------------------------------------------------------------------
1720
1721 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
1722
1723 BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
1724 EVT_SIZE(wxDataViewCtrl::OnSize)
1725 END_EVENT_TABLE()
1726
1727 wxDataViewCtrl::~wxDataViewCtrl()
1728 {
1729 if (m_notifier)
1730 GetModel()->RemoveNotifier( m_notifier );
1731 }
1732
1733 void wxDataViewCtrl::Init()
1734 {
1735 m_notifier = NULL;
1736 }
1737
1738 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
1739 const wxPoint& pos, const wxSize& size,
1740 long style, const wxValidator& validator )
1741 {
1742 if (!wxControl::Create( parent, id, pos, size, style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
1743 return false;
1744
1745 Init();
1746
1747 #ifdef __WXMAC__
1748 MacSetClipChildren( true ) ;
1749 #endif
1750
1751 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
1752 #ifdef __WXMSW__
1753 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,22) );
1754 #else
1755 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,25) );
1756 #endif
1757
1758 SetTargetWindow( m_clientArea );
1759
1760 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
1761 sizer->Add( m_headerArea, 0, wxGROW );
1762 sizer->Add( m_clientArea, 1, wxGROW );
1763 SetSizer( sizer );
1764
1765 return true;
1766 }
1767
1768 #ifdef __WXMSW__
1769 WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
1770 WXWPARAM wParam,
1771 WXLPARAM lParam)
1772 {
1773 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
1774
1775 #ifndef __WXWINCE__
1776 // we need to process arrows ourselves for scrolling
1777 if ( nMsg == WM_GETDLGCODE )
1778 {
1779 rc |= DLGC_WANTARROWS;
1780 }
1781 #endif
1782
1783 return rc;
1784 }
1785 #endif
1786
1787 void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
1788 {
1789 // We need to override OnSize so that our scrolled
1790 // window a) does call Layout() to use sizers for
1791 // positioning the controls but b) does not query
1792 // the sizer for their size and use that for setting
1793 // the scrollable area as set that ourselves by
1794 // calling SetScrollbar() further down.
1795
1796 Layout();
1797
1798 AdjustScrollbars();
1799 }
1800
1801 bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
1802 {
1803 if (!wxDataViewCtrlBase::AssociateModel( model ))
1804 return false;
1805
1806 m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
1807
1808 model->AddNotifier( m_notifier );
1809
1810 m_clientArea->UpdateDisplay();
1811
1812 return true;
1813 }
1814
1815 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
1816 {
1817 if (!wxDataViewCtrlBase::AppendColumn(col))
1818 return false;
1819
1820 m_clientArea->UpdateDisplay();
1821
1822 return true;
1823 }
1824
1825 void wxDataViewCtrl::SetSelection( int WXUNUSED(row) )
1826 {
1827 // FIXME - TODO
1828 }
1829
1830 void wxDataViewCtrl::SetSelectionRange( unsigned int WXUNUSED(from), unsigned int WXUNUSED(to) )
1831 {
1832 // FIXME - TODO
1833 }
1834
1835 void wxDataViewCtrl::SetSelections( const wxArrayInt& WXUNUSED(aSelections) )
1836 {
1837 // FIXME - TODO
1838 }
1839
1840 void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) )
1841 {
1842 // FIXME - TODO
1843 }
1844
1845 bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row) ) const
1846 {
1847 // FIXME - TODO
1848
1849 return false;
1850 }
1851
1852 int wxDataViewCtrl::GetSelection() const
1853 {
1854 // FIXME - TODO
1855
1856 return -1;
1857 }
1858
1859 int wxDataViewCtrl::GetSelections(wxArrayInt& WXUNUSED(aSelections) ) const
1860 {
1861 // FIXME - TODO
1862
1863 return 0;
1864 }
1865
1866 #endif
1867 // !wxUSE_GENERICDATAVIEWCTRL
1868
1869 #endif
1870 // wxUSE_DATAVIEWCTRL