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