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