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