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