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