]> git.saurik.com Git - wxWidgets.git/blame - src/generic/datavgen.cpp
document that SetValue() accepts values in 0..GetRange interval, inclusive
[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
87f0efe2 5// Modified by: Francesco Montorsi, Guru Kathiresan, Otto Wyss
4ed7af08
RR
6// Id: $Id$
7// Copyright: (c) 1998 Robert Roebling
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
ed4b0fdc
WS
14#ifdef __BORLANDC__
15 #pragma hdrstop
16#endif
17
4ed7af08
RR
18#if wxUSE_DATAVIEWCTRL
19
20#include "wx/dataview.h"
21
22#ifdef wxUSE_GENERICDATAVIEWCTRL
23
f554a14b 24#ifndef WX_PRECOMP
57bd4c60 25 #ifdef __WXMSW__
87f0efe2 26 #include "wx/msw/private.h"
57bd4c60 27 #include "wx/msw/wrapwin.h"
87f0efe2 28 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
57bd4c60 29 #endif
f554a14b
WS
30 #include "wx/sizer.h"
31 #include "wx/log.h"
ed4b0fdc 32 #include "wx/dcclient.h"
c0badb70 33 #include "wx/timer.h"
9eddec69 34 #include "wx/settings.h"
8d0ca292 35 #include "wx/msgdlg.h"
99d471a5 36 #include "wx/dcscreen.h"
f554a14b
WS
37#endif
38
4ed7af08 39#include "wx/stockitem.h"
4ed7af08
RR
40#include "wx/calctrl.h"
41#include "wx/popupwin.h"
4ed7af08 42#include "wx/renderer.h"
2e992e06 43#include "wx/dcbuffer.h"
2586d4a1 44#include "wx/icon.h"
4ed7af08 45
4ed7af08
RR
46//-----------------------------------------------------------------------------
47// classes
48//-----------------------------------------------------------------------------
49
50class wxDataViewCtrl;
51
9861f022
RR
52static const int SCROLL_UNIT_X = 15;
53
54// the cell padding on the left/right
55static const int PADDING_RIGHTLEFT = 3;
56
57// the cell padding on the top/bottom
58static const int PADDING_TOPBOTTOM = 1;
59
60
a0f3af5f
RR
61//-----------------------------------------------------------------------------
62// wxDataViewHeaderWindow
63//-----------------------------------------------------------------------------
4ed7af08 64
87f0efe2
RR
65#define USE_NATIVE_HEADER_WINDOW 1
66
c741d33f
VZ
67// NB: for some reason, this class must be dllexport'ed or we get warnings from
68// MSVC in DLL build
69class WXDLLIMPEXP_ADV wxDataViewHeaderWindowBase : public wxControl
87f0efe2
RR
70{
71public:
72 wxDataViewHeaderWindowBase()
73 { m_owner = NULL; }
74
75 bool Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f 76 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
77 const wxString &name)
78 {
79 return wxWindow::Create(parent, id, pos, size, wxNO_BORDER, name);
80 }
81
82 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
83 wxDataViewCtrl *GetOwner() { return m_owner; }
84
85 // called on column addition/removal
86 virtual void UpdateDisplay() { /* by default, do nothing */ }
87
87f0efe2 88 // returns the n-th column
9861f022 89 virtual wxDataViewColumn *GetColumn(unsigned int n)
87f0efe2
RR
90 {
91 wxASSERT(m_owner);
c741d33f 92 wxDataViewColumn *ret = m_owner->GetColumn(n);
87f0efe2
RR
93 wxASSERT(ret);
94
c741d33f 95 return ret;
87f0efe2
RR
96 }
97
98protected:
99 wxDataViewCtrl *m_owner;
100
101 // sends an event generated from the n-th wxDataViewColumn
102 void SendEvent(wxEventType type, unsigned int n);
103};
104
105// on wxMSW the header window (only that part however) can be made native!
106#if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
107
9861f022 108#define COLUMN_WIDTH_OFFSET 2
87f0efe2
RR
109#define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow
110
111class wxDataViewHeaderWindowMSW : public wxDataViewHeaderWindowBase
4ed7af08
RR
112{
113public:
87f0efe2
RR
114
115 wxDataViewHeaderWindowMSW( wxDataViewCtrl *parent,
9861f022
RR
116 wxWindowID id,
117 const wxPoint &pos = wxDefaultPosition,
118 const wxSize &size = wxDefaultSize,
119 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
c741d33f 120 {
87f0efe2
RR
121 Create(parent, id, pos, size, name);
122 }
4ed7af08 123
87f0efe2 124 bool Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f 125 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
126 const wxString &name);
127
128 ~wxDataViewHeaderWindowMSW();
129
9861f022
RR
130 // called when any column setting is changed and/or changed
131 // the column count
87f0efe2
RR
132 virtual void UpdateDisplay();
133
134 // called when the main window gets scrolled
135 virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL);
136
137protected:
138 virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result);
9861f022
RR
139 virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags);
140
141 unsigned int GetColumnIdxFromHeader(NMHEADER *nmHDR);
142
143 wxDataViewColumn *GetColumnFromHeader(NMHEADER *nmHDR)
144 { return GetColumn(GetColumnIdxFromHeader(nmHDR)); }
145
87f0efe2
RR
146private:
147 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW)
148};
149
150#else // !defined(__WXMSW__)
151
152#define HEADER_WINDOW_HEIGHT 25
9861f022
RR
153#define HEADER_HORIZ_BORDER 5
154#define HEADER_VERT_BORDER 3
87f0efe2
RR
155#define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow
156
157class wxGenericDataViewHeaderWindow : public wxDataViewHeaderWindowBase
158{
159public:
160 wxGenericDataViewHeaderWindow( wxDataViewCtrl *parent,
161 wxWindowID id,
162 const wxPoint &pos = wxDefaultPosition,
163 const wxSize &size = wxDefaultSize,
164 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
c741d33f 165 {
87f0efe2
RR
166 Init();
167 Create(parent, id, pos, size, name);
168 }
169
170 bool Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f 171 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
172 const wxString &name);
173
174 ~wxGenericDataViewHeaderWindow()
175 {
176 delete m_resizeCursor;
177 }
178
c3112d56
RR
179 virtual void UpdateDisplay() { Refresh(); }
180
87f0efe2 181 // event handlers:
4ed7af08 182
a0f3af5f
RR
183 void OnPaint( wxPaintEvent &event );
184 void OnMouse( wxMouseEvent &event );
185 void OnSetFocus( wxFocusEvent &event );
f554a14b 186
87f0efe2
RR
187
188protected:
189
190 // vars used for column resizing:
191
a0f3af5f 192 wxCursor *m_resizeCursor;
87f0efe2
RR
193 const wxCursor *m_currentCursor;
194 bool m_isDragging;
195
196 bool m_dirty; // needs refresh?
197 int m_column; // index of the column being resized
198 int m_currentX; // divider line position in logical (unscrolled) coords
c741d33f 199 int m_minX; // minimal position beyond which the divider line
87f0efe2
RR
200 // can't be dragged in logical coords
201
9861f022
RR
202 // the pen used to draw the current column width drag line
203 // when resizing the columsn
204 wxPen m_penCurrent;
205
206
87f0efe2
RR
207 // internal utilities:
208
209 void Init()
210 {
211 m_currentCursor = (wxCursor *) NULL;
212 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
213
214 m_isDragging = false;
215 m_dirty = false;
216
217 m_column = wxNOT_FOUND;
218 m_currentX = 0;
219 m_minX = 0;
9861f022
RR
220
221 wxColour col = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
222 m_penCurrent = wxPen(col, 1, wxSOLID);
87f0efe2
RR
223 }
224
225 void DrawCurrent();
226 void AdjustDC(wxDC& dc);
f554a14b 227
a0f3af5f 228private:
87f0efe2 229 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow)
a0f3af5f
RR
230 DECLARE_EVENT_TABLE()
231};
4ed7af08 232
87f0efe2
RR
233#endif // defined(__WXMSW__)
234
0fcce6b9
RR
235//-----------------------------------------------------------------------------
236// wxDataViewRenameTimer
237//-----------------------------------------------------------------------------
238
239class wxDataViewRenameTimer: public wxTimer
240{
241private:
242 wxDataViewMainWindow *m_owner;
243
244public:
245 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
246 void Notify();
247};
248
a0f3af5f
RR
249//-----------------------------------------------------------------------------
250// wxDataViewMainWindow
251//-----------------------------------------------------------------------------
4ed7af08 252
c741d33f 253WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
87f0efe2 254 WXDLLIMPEXP_ADV);
cab07038 255
a0f3af5f 256class wxDataViewMainWindow: public wxWindow
4ed7af08 257{
a0f3af5f
RR
258public:
259 wxDataViewMainWindow( wxDataViewCtrl *parent,
260 wxWindowID id,
261 const wxPoint &pos = wxDefaultPosition,
262 const wxSize &size = wxDefaultSize,
263 const wxString &name = wxT("wxdataviewctrlmainwindow") );
d3c7fc99 264 virtual ~wxDataViewMainWindow();
4ed7af08 265
a0f3af5f
RR
266 // notifications from wxDataViewListModel
267 bool RowAppended();
268 bool RowPrepended();
0a71f9e9
RR
269 bool RowInserted( unsigned int before );
270 bool RowDeleted( unsigned int row );
271 bool RowChanged( unsigned int row );
272 bool ValueChanged( unsigned int col, unsigned int row );
273 bool RowsReordered( unsigned int *new_order );
a0f3af5f 274 bool Cleared();
4ed7af08 275
a0f3af5f
RR
276 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
277 wxDataViewCtrl *GetOwner() { return m_owner; }
9861f022 278 const wxDataViewCtrl *GetOwner() const { return m_owner; }
4ed7af08 279
a0f3af5f 280 void OnPaint( wxPaintEvent &event );
0a71f9e9 281 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
cab07038 282 void OnChar( wxKeyEvent &event );
a0f3af5f
RR
283 void OnMouse( wxMouseEvent &event );
284 void OnSetFocus( wxFocusEvent &event );
cab07038 285 void OnKillFocus( wxFocusEvent &event );
f554a14b 286
a0f3af5f
RR
287 void UpdateDisplay();
288 void RecalculateDisplay();
289 void OnInternalIdle();
f554a14b 290
0fcce6b9 291 void OnRenameTimer();
0fcce6b9 292
9861f022 293 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
120b9b05 294
0a71f9e9
RR
295 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
296 void ChangeCurrentRow( unsigned int row );
120b9b05 297
47b378bd 298 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
cab07038 299 bool IsEmpty() { return GetRowCount() == 0; }
120b9b05 300
9861f022
RR
301 int GetCountPerPage() const;
302 int GetEndOfLastCol() const;
303 unsigned int GetFirstVisibleRow() const;
304 unsigned int GetLastVisibleRow() const;
305 unsigned int GetRowCount() const;
120b9b05 306
87f0efe2 307 void Select( const wxArrayInt& aSelections );
cab07038 308 void SelectAllRows( bool on );
0a71f9e9
RR
309 void SelectRow( unsigned int row, bool on );
310 void SelectRows( unsigned int from, unsigned int to, bool on );
311 void ReverseRowSelection( unsigned int row );
312 bool IsRowSelected( unsigned int row );
120b9b05 313
0a71f9e9
RR
314 void RefreshRow( unsigned int row );
315 void RefreshRows( unsigned int from, unsigned int to );
316 void RefreshRowsAfter( unsigned int firstRow );
120b9b05 317
9861f022
RR
318 // returns the colour to be used for drawing the rules
319 wxColour GetRuleColour() const
320 {
321 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
322 }
323
324 //void EnsureVisible( unsigned int row );
325 wxRect GetLineRect( unsigned int row ) const;
326
a0f3af5f 327private:
0fcce6b9
RR
328 wxDataViewCtrl *m_owner;
329 int m_lineHeight;
330 bool m_dirty;
120b9b05 331
0fcce6b9 332 wxDataViewColumn *m_currentCol;
99d471a5 333 unsigned int m_currentRow;
cab07038 334 wxDataViewSelection m_selection;
120b9b05 335
0fcce6b9 336 wxDataViewRenameTimer *m_renameTimer;
0fcce6b9 337 bool m_lastOnSame;
f554a14b 338
cab07038
RR
339 bool m_hasFocus;
340
e21f75bd
RR
341 int m_dragCount;
342 wxPoint m_dragStart;
343
344 // for double click logic
0a71f9e9 345 unsigned int m_lineLastClicked,
e21f75bd
RR
346 m_lineBeforeLastClicked,
347 m_lineSelectSingleOnUp;
cab07038 348
9861f022
RR
349 // the pen used to draw horiz/vertical rules
350 wxPen m_penRule;
351
a0f3af5f
RR
352private:
353 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
354 DECLARE_EVENT_TABLE()
355};
4ed7af08 356
f554a14b 357// ---------------------------------------------------------
a0f3af5f 358// wxGenericDataViewListModelNotifier
f554a14b 359// ---------------------------------------------------------
a0f3af5f
RR
360
361class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
4ed7af08 362{
a0f3af5f
RR
363public:
364 wxGenericDataViewListModelNotifier( wxDataViewMainWindow *mainWindow )
365 { m_mainWindow = mainWindow; }
f554a14b 366
a0f3af5f
RR
367 virtual bool RowAppended()
368 { return m_mainWindow->RowAppended(); }
369 virtual bool RowPrepended()
370 { return m_mainWindow->RowPrepended(); }
0a71f9e9 371 virtual bool RowInserted( unsigned int before )
a0f3af5f 372 { return m_mainWindow->RowInserted( before ); }
0a71f9e9 373 virtual bool RowDeleted( unsigned int row )
a0f3af5f 374 { return m_mainWindow->RowDeleted( row ); }
0a71f9e9 375 virtual bool RowChanged( unsigned int row )
a0f3af5f 376 { return m_mainWindow->RowChanged( row ); }
0a71f9e9 377 virtual bool ValueChanged( unsigned int col, unsigned int row )
a0f3af5f 378 { return m_mainWindow->ValueChanged( col, row ); }
0a71f9e9 379 virtual bool RowsReordered( unsigned int *new_order )
a0f3af5f
RR
380 { return m_mainWindow->RowsReordered( new_order ); }
381 virtual bool Cleared()
382 { return m_mainWindow->Cleared(); }
f554a14b 383
a0f3af5f
RR
384 wxDataViewMainWindow *m_mainWindow;
385};
4ed7af08 386
f554a14b 387// ---------------------------------------------------------
baa9ebc4 388// wxDataViewRenderer
f554a14b 389// ---------------------------------------------------------
4ed7af08 390
baa9ebc4 391IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
4ed7af08 392
c741d33f 393wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
9861f022
RR
394 wxDataViewCellMode mode,
395 int align) :
396 wxDataViewRendererBase( varianttype, mode, align )
4ed7af08 397{
3d9d7cc4 398 m_dc = NULL;
9861f022
RR
399 m_align = align;
400 m_mode = mode;
4ed7af08
RR
401}
402
baa9ebc4 403wxDataViewRenderer::~wxDataViewRenderer()
3d9d7cc4
RR
404{
405 if (m_dc)
406 delete m_dc;
407}
408
baa9ebc4 409wxDC *wxDataViewRenderer::GetDC()
3d9d7cc4
RR
410{
411 if (m_dc == NULL)
412 {
413 if (GetOwner() == NULL)
414 return NULL;
415 if (GetOwner()->GetOwner() == NULL)
416 return NULL;
417 m_dc = new wxClientDC( GetOwner()->GetOwner() );
418 }
f554a14b 419
3d9d7cc4
RR
420 return m_dc;
421}
422
f554a14b 423// ---------------------------------------------------------
baa9ebc4 424// wxDataViewCustomRenderer
f554a14b 425// ---------------------------------------------------------
3d9d7cc4 426
baa9ebc4 427IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
3d9d7cc4 428
baa9ebc4 429wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
9861f022
RR
430 wxDataViewCellMode mode, int align ) :
431 wxDataViewRenderer( varianttype, mode, align )
3d9d7cc4
RR
432{
433}
434
f554a14b 435// ---------------------------------------------------------
baa9ebc4 436// wxDataViewTextRenderer
f554a14b 437// ---------------------------------------------------------
4ed7af08 438
baa9ebc4 439IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
4ed7af08 440
c741d33f 441wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
9861f022
RR
442 wxDataViewCellMode mode, int align ) :
443 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08
RR
444{
445}
446
baa9ebc4 447bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
4ed7af08 448{
90675b95 449 m_text = value.GetString();
f554a14b 450
90675b95 451 return true;
4ed7af08
RR
452}
453
9861f022 454bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
4ed7af08
RR
455{
456 return false;
457}
458
99d471a5
RR
459bool wxDataViewTextRenderer::HasEditorCtrl()
460{
461 return true;
462}
463
464wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
465 wxRect labelRect, const wxVariant &value )
466{
467 return new wxTextCtrl( parent, wxID_ANY, value,
468 wxPoint(labelRect.x,labelRect.y),
469 wxSize(labelRect.width,labelRect.height) );
470}
471
472bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
473{
474 wxTextCtrl *text = (wxTextCtrl*) editor;
475 value = text->GetValue();
476 return true;
477}
478
87f0efe2 479bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state )
3d9d7cc4 480{
87f0efe2
RR
481 wxDataViewCtrl *view = GetOwner()->GetOwner();
482 wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ?
483 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) :
484 view->GetForegroundColour();
485
486 dc->SetTextForeground(col);
90675b95
RR
487 dc->DrawText( m_text, cell.x, cell.y );
488
489 return true;
3d9d7cc4
RR
490}
491
9861f022 492wxSize wxDataViewTextRenderer::GetSize() const
3d9d7cc4 493{
9861f022 494 const wxDataViewCtrl *view = GetView();
87f0efe2
RR
495 if (!m_text.empty())
496 {
497 int x,y;
498 view->GetTextExtent( m_text, &x, &y );
499 return wxSize( x, y );
500 }
3d9d7cc4
RR
501 return wxSize(80,20);
502}
503
2586d4a1 504// ---------------------------------------------------------
baa9ebc4 505// wxDataViewBitmapRenderer
2586d4a1
RR
506// ---------------------------------------------------------
507
baa9ebc4 508IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
2586d4a1 509
c741d33f 510wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
9861f022
RR
511 wxDataViewCellMode mode, int align ) :
512 wxDataViewCustomRenderer( varianttype, mode, align )
2586d4a1
RR
513{
514}
515
baa9ebc4 516bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
2586d4a1
RR
517{
518 if (value.GetType() == wxT("wxBitmap"))
519 m_bitmap << value;
520 if (value.GetType() == wxT("wxIcon"))
521 m_icon << value;
522
523 return true;
524}
525
9861f022 526bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
2586d4a1
RR
527{
528 return false;
529}
530
baa9ebc4 531bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
2586d4a1
RR
532{
533 if (m_bitmap.Ok())
534 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
535 else if (m_icon.Ok())
536 dc->DrawIcon( m_icon, cell.x, cell.y );
537
538 return true;
539}
540
9861f022 541wxSize wxDataViewBitmapRenderer::GetSize() const
2586d4a1
RR
542{
543 if (m_bitmap.Ok())
544 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
545 else if (m_icon.Ok())
546 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
547
548 return wxSize(16,16);
549}
550
f554a14b 551// ---------------------------------------------------------
baa9ebc4 552// wxDataViewToggleRenderer
f554a14b 553// ---------------------------------------------------------
4ed7af08 554
baa9ebc4 555IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
4ed7af08 556
baa9ebc4 557wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
9861f022
RR
558 wxDataViewCellMode mode, int align ) :
559 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08 560{
90675b95 561 m_toggle = false;
4ed7af08
RR
562}
563
baa9ebc4 564bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
4ed7af08 565{
90675b95 566 m_toggle = value.GetBool();
f554a14b 567
a8461d31 568 return true;
4ed7af08
RR
569}
570
9861f022 571bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
4ed7af08
RR
572{
573 return false;
574}
f554a14b 575
baa9ebc4 576bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
4ed7af08 577{
90675b95 578 // User wxRenderer here
f554a14b 579
90675b95
RR
580 wxRect rect;
581 rect.x = cell.x + cell.width/2 - 10;
582 rect.width = 20;
583 rect.y = cell.y + cell.height/2 - 10;
584 rect.height = 20;
120b9b05 585
862d8041 586 int flags = 0;
90675b95 587 if (m_toggle)
862d8041
RR
588 flags |= wxCONTROL_CHECKED;
589 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
590 flags |= wxCONTROL_DISABLED;
591
90b903c2 592 wxRendererNative::Get().DrawCheckBox(
862d8041
RR
593 GetOwner()->GetOwner(),
594 *dc,
595 rect,
596 flags );
f554a14b 597
90675b95 598 return true;
4ed7af08
RR
599}
600
c741d33f
VZ
601bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
602 wxDataViewListModel *model,
87f0efe2 603 unsigned int col, unsigned int row )
0fdc2321
RR
604{
605 bool value = !m_toggle;
606 wxVariant variant = value;
607 model->SetValue( variant, col, row );
f554a14b 608 model->ValueChanged( col, row );
0fdc2321
RR
609 return true;
610}
611
9861f022 612wxSize wxDataViewToggleRenderer::GetSize() const
4ed7af08 613{
3d9d7cc4 614 return wxSize(20,20);
4ed7af08
RR
615}
616
f554a14b 617// ---------------------------------------------------------
baa9ebc4 618// wxDataViewProgressRenderer
f554a14b 619// ---------------------------------------------------------
4ed7af08 620
baa9ebc4 621IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
4ed7af08 622
baa9ebc4 623wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
9861f022
RR
624 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
625 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08
RR
626{
627 m_label = label;
628 m_value = 0;
629}
630
baa9ebc4 631wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
4ed7af08
RR
632{
633}
634
baa9ebc4 635bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
4ed7af08
RR
636{
637 m_value = (long) value;
f554a14b 638
4ed7af08
RR
639 if (m_value < 0) m_value = 0;
640 if (m_value > 100) m_value = 100;
f554a14b 641
4ed7af08
RR
642 return true;
643}
f554a14b 644
9861f022
RR
645bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
646{
647 value = (long) m_value;
648 return true;
649}
650
baa9ebc4 651bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
4ed7af08
RR
652{
653 double pct = (double)m_value / 100.0;
654 wxRect bar = cell;
655 bar.width = (int)(cell.width * pct);
656 dc->SetPen( *wxTRANSPARENT_PEN );
657 dc->SetBrush( *wxBLUE_BRUSH );
658 dc->DrawRectangle( bar );
659
660 dc->SetBrush( *wxTRANSPARENT_BRUSH );
661 dc->SetPen( *wxBLACK_PEN );
662 dc->DrawRectangle( cell );
f554a14b 663
4ed7af08
RR
664 return true;
665}
666
9861f022 667wxSize wxDataViewProgressRenderer::GetSize() const
4ed7af08
RR
668{
669 return wxSize(40,12);
670}
f554a14b
WS
671
672// ---------------------------------------------------------
baa9ebc4 673// wxDataViewDateRenderer
f554a14b 674// ---------------------------------------------------------
4ed7af08 675
21ead767
VZ
676#define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
677
678#if wxUSE_DATE_RENDERER_POPUP
8d0ca292 679
baa9ebc4 680class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
4ed7af08 681{
f554a14b 682public:
baa9ebc4 683 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
0a71f9e9 684 wxDataViewListModel *model, unsigned int col, unsigned int row ) :
4ed7af08
RR
685 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
686 {
687 m_model = model;
688 m_col = col;
689 m_row = row;
f554a14b 690 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
4ed7af08
RR
691 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
692 sizer->Add( m_cal, 1, wxGROW );
693 SetSizer( sizer );
694 sizer->Fit( this );
695 }
f554a14b 696
4ed7af08 697 void OnCalendar( wxCalendarEvent &event );
f554a14b 698
4ed7af08 699 wxCalendarCtrl *m_cal;
f554a14b 700 wxDataViewListModel *m_model;
0a71f9e9
RR
701 unsigned int m_col;
702 unsigned int m_row;
f554a14b 703
a8461d31
PC
704protected:
705 virtual void OnDismiss()
706 {
707 }
708
4ed7af08
RR
709private:
710 DECLARE_EVENT_TABLE()
711};
712
baa9ebc4
RR
713BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
714 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
4ed7af08
RR
715END_EVENT_TABLE()
716
baa9ebc4 717void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
4ed7af08
RR
718{
719 wxDateTime date = event.GetDate();
720 wxVariant value = date;
721 m_model->SetValue( value, m_col, m_row );
722 m_model->ValueChanged( m_col, m_row );
723 DismissAndNotify();
724}
725
21ead767 726#endif // wxUSE_DATE_RENDERER_POPUP
8d0ca292 727
baa9ebc4 728IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
4ed7af08 729
baa9ebc4 730wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
9861f022
RR
731 wxDataViewCellMode mode, int align ) :
732 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08
RR
733{
734}
f554a14b 735
baa9ebc4 736bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
4ed7af08
RR
737{
738 m_date = value.GetDateTime();
f554a14b 739
4ed7af08
RR
740 return true;
741}
742
9861f022
RR
743bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
744{
745 value = m_date;
746 return true;
747}
748
baa9ebc4 749bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
4ed7af08
RR
750{
751 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
752 wxString tmp = m_date.FormatDate();
753 dc->DrawText( tmp, cell.x, cell.y );
754
755 return true;
756}
757
9861f022 758wxSize wxDataViewDateRenderer::GetSize() const
4ed7af08 759{
9861f022 760 const wxDataViewCtrl* view = GetView();
4ed7af08
RR
761 wxString tmp = m_date.FormatDate();
762 wxCoord x,y,d;
763 view->GetTextExtent( tmp, &x, &y, &d );
764 return wxSize(x,y+d);
765}
766
c741d33f 767bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model,
87f0efe2 768 unsigned int col, unsigned int row )
4ed7af08
RR
769{
770 wxVariant variant;
771 model->GetValue( variant, col, row );
772 wxDateTime value = variant.GetDateTime();
773
21ead767 774#if wxUSE_DATE_RENDERER_POPUP
baa9ebc4 775 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
4ed7af08
RR
776 GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
777 wxPoint pos = wxGetMousePosition();
778 popup->Move( pos );
779 popup->Layout();
780 popup->Popup( popup->m_cal );
21ead767 781#else // !wxUSE_DATE_RENDERER_POPUP
8d0ca292 782 wxMessageBox(value.Format());
21ead767 783#endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
4ed7af08
RR
784 return true;
785}
786
f554a14b 787// ---------------------------------------------------------
4ed7af08 788// wxDataViewColumn
f554a14b 789// ---------------------------------------------------------
4ed7af08
RR
790
791IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
792
c741d33f 793wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
87f0efe2
RR
794 unsigned int model_column,
795 int width, wxAlignment align, int flags ) :
796 wxDataViewColumnBase( title, cell, model_column, width, align, flags )
4ed7af08 797{
9861f022
RR
798 SetAlignment(align);
799 SetTitle(title);
800 SetFlags(flags);
801
802 Init(width < 0 ? wxDVC_DEFAULT_WIDTH : width);
4ed7af08
RR
803}
804
c741d33f 805wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
87f0efe2
RR
806 unsigned int model_column,
807 int width, wxAlignment align, int flags ) :
808 wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags )
07a84e7b 809{
9861f022
RR
810 SetAlignment(align);
811 SetFlags(flags);
812
813 Init(width < 0 ? wxDVC_TOGGLE_DEFAULT_WIDTH : width);
07a84e7b
RR
814}
815
9861f022
RR
816wxDataViewColumn::~wxDataViewColumn()
817{
818}
819
820void wxDataViewColumn::Init( int width )
47cef10f 821{
87f0efe2 822 m_width = width;
9861f022 823 m_minWidth = wxDVC_DEFAULT_MINWIDTH;
c3112d56 824 m_ascending = true;
47cef10f
RR
825}
826
9861f022 827void wxDataViewColumn::SetResizeable( bool resizeable )
31fb32e1 828{
9861f022
RR
829 if (resizeable)
830 m_flags |= wxDATAVIEW_COL_RESIZABLE;
831 else
832 m_flags &= ~wxDATAVIEW_COL_RESIZABLE;
833}
834
835void wxDataViewColumn::SetHidden( bool hidden )
836{
837 if (hidden)
838 m_flags |= wxDATAVIEW_COL_HIDDEN;
839 else
840 m_flags &= ~wxDATAVIEW_COL_HIDDEN;
841
842 // tell our owner to e.g. update its scrollbars:
843 if (GetOwner())
844 GetOwner()->OnColumnChange();
845}
846
847void wxDataViewColumn::SetSortable( bool sortable )
848{
849 if (sortable)
850 m_flags |= wxDATAVIEW_COL_SORTABLE;
851 else
852 m_flags &= ~wxDATAVIEW_COL_SORTABLE;
c3112d56
RR
853
854 // Update header button
855 if (GetOwner())
856 GetOwner()->OnColumnChange();
31fb32e1
RR
857}
858
c3112d56 859void wxDataViewColumn::SetSortOrder( bool ascending )
47cef10f 860{
c3112d56
RR
861 m_ascending = ascending;
862
863 // Update header button
864 if (GetOwner())
865 GetOwner()->OnColumnChange();
47cef10f
RR
866}
867
87f0efe2 868bool wxDataViewColumn::IsSortOrderAscending() const
31fb32e1 869{
c3112d56 870 return m_ascending;
31fb32e1
RR
871}
872
9861f022 873void wxDataViewColumn::SetInternalWidth( int width )
4ed7af08 874{
9861f022 875 m_width = width;
f554a14b 876
9861f022
RR
877 // the scrollbars of the wxDataViewCtrl needs to be recalculated!
878 if (m_owner && m_owner->m_clientArea)
879 m_owner->m_clientArea->RecalculateDisplay();
4ed7af08
RR
880}
881
9861f022 882void wxDataViewColumn::SetWidth( int width )
47cef10f 883{
9861f022 884 m_owner->m_headerArea->UpdateDisplay();
47cef10f 885
9861f022 886 SetInternalWidth(width);
47cef10f
RR
887}
888
533544f2 889
4ed7af08 890//-----------------------------------------------------------------------------
87f0efe2 891// wxDataViewHeaderWindowBase
4ed7af08
RR
892//-----------------------------------------------------------------------------
893
87f0efe2
RR
894void wxDataViewHeaderWindowBase::SendEvent(wxEventType type, unsigned int n)
895{
896 wxWindow *parent = GetParent();
897 wxDataViewEvent le(type, parent->GetId());
898
899 le.SetEventObject(parent);
900 le.SetColumn(n);
901 le.SetDataViewColumn(GetColumn(n));
902 le.SetModel(GetOwner()->GetModel());
903
904 // for events created by wxDataViewHeaderWindow the
905 // row / value fields are not valid
906
907 parent->GetEventHandler()->ProcessEvent(le);
908}
909
910#if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
911
87f0efe2 912// implemented in msw/listctrl.cpp:
a68962fc 913int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
87f0efe2 914
5d3f234b 915IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow)
87f0efe2
RR
916
917bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl *parent, wxWindowID id,
c741d33f 918 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
919 const wxString &name )
920{
921 m_owner = parent;
922
923 if ( !CreateControl(parent, id, pos, size, 0, wxDefaultValidator, name) )
924 return false;
925
926 int x = pos.x == wxDefaultCoord ? 0 : pos.x,
927 y = pos.y == wxDefaultCoord ? 0 : pos.y,
928 w = size.x == wxDefaultCoord ? 1 : size.x,
929 h = size.y == wxDefaultCoord ? 22 : size.y;
930
931 // create the native WC_HEADER window:
932 WXHWND hwndParent = (HWND)parent->GetHandle();
933 WXDWORD msStyle = WS_CHILD | HDS_BUTTONS | HDS_HORZ | HDS_HOTTRACK | HDS_FULLDRAG;
c741d33f
VZ
934 m_hWnd = CreateWindowEx(0,
935 WC_HEADER,
936 (LPCTSTR) NULL,
87f0efe2
RR
937 msStyle,
938 x, y, w, h,
c741d33f
VZ
939 (HWND)hwndParent,
940 (HMENU)-1,
941 wxGetInstance(),
87f0efe2 942 (LPVOID) NULL);
c741d33f 943 if (m_hWnd == NULL)
87f0efe2
RR
944 {
945 wxLogLastError(_T("CreateWindowEx"));
946 return false;
947 }
948
5d3f234b 949 // we need to subclass the m_hWnd to force wxWindow::HandleNotify
87f0efe2 950 // to call wxDataViewHeaderWindow::MSWOnNotify
5d3f234b 951 SubclassWin(m_hWnd);
87f0efe2 952
c741d33f 953 // the following is required to get the default win's font for
9861f022
RR
954 // header windows and must be done befor sending the HDM_LAYOUT msg
955 SetFont(GetFont());
87f0efe2 956
9861f022
RR
957 RECT rcParent;
958 HDLAYOUT hdl;
959 WINDOWPOS wp;
87f0efe2 960
c741d33f
VZ
961 // Retrieve the bounding rectangle of the parent window's
962 // client area, and then request size and position values
963 // from the header control.
964 ::GetClientRect((HWND)hwndParent, &rcParent);
965
966 hdl.prc = &rcParent;
967 hdl.pwpos = &wp;
968 if (!SendMessage((HWND)m_hWnd, HDM_LAYOUT, 0, (LPARAM) &hdl))
87f0efe2
RR
969 {
970 wxLogLastError(_T("SendMessage"));
971 return false;
972 }
c741d33f
VZ
973
974 // Set the size, position, and visibility of the header control.
975 SetWindowPos((HWND)m_hWnd,
976 wp.hwndInsertAfter,
977 wp.x, wp.y,
978 wp.cx, wp.cy,
87f0efe2
RR
979 wp.flags | SWP_SHOWWINDOW);
980
981 // set our size hints: wxDataViewCtrl will put this wxWindow inside
982 // a wxBoxSizer and in order to avoid super-big header windows,
983 // we need to set our height as fixed
984 SetMinSize(wxSize(-1, wp.cy));
985 SetMaxSize(wxSize(-1, wp.cy));
986
87f0efe2
RR
987 return true;
988}
989
990wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
991{
5d3f234b 992 UnsubclassWin();
87f0efe2
RR
993}
994
995void wxDataViewHeaderWindowMSW::UpdateDisplay()
996{
997 // remove old columns
914e6945 998 for (int j=0, max=Header_GetItemCount((HWND)m_hWnd); j < max; j++)
87f0efe2 999 Header_DeleteItem((HWND)m_hWnd, 0);
c741d33f 1000
87f0efe2 1001 // add the updated array of columns to the header control
9861f022
RR
1002 unsigned int cols = GetOwner()->GetColumnCount();
1003 unsigned int added = 0;
87f0efe2
RR
1004 for (unsigned int i = 0; i < cols; i++)
1005 {
1006 wxDataViewColumn *col = GetColumn( i );
1007 if (col->IsHidden())
1008 continue; // don't add it!
1009
1010 HDITEM hdi;
1011 hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
fab3f50e 1012 hdi.pszText = (wxChar *) col->GetTitle().wx_str();
87f0efe2
RR
1013 hdi.cxy = col->GetWidth();
1014 hdi.cchTextMax = sizeof(hdi.pszText)/sizeof(hdi.pszText[0]);
1015 hdi.fmt = HDF_LEFT | HDF_STRING;
9861f022
RR
1016
1017 // lParam is reserved for application's use:
1018 // we store there the column index to use it later in MSWOnNotify
1019 // (since columns may have been hidden)
1020 hdi.lParam = (LPARAM)i;
1021
1022 // the native wxMSW implementation of the header window
c741d33f 1023 // draws the column separator COLUMN_WIDTH_OFFSET pixels
9861f022
RR
1024 // on the right: to correct this effect we make the column
1025 // exactly COLUMN_WIDTH_OFFSET wider (for the first column):
1026 if (i == 0)
1027 hdi.cxy += COLUMN_WIDTH_OFFSET;
1028
1029 switch (col->GetAlignment())
1030 {
1031 case wxALIGN_LEFT:
1032 hdi.fmt |= HDF_LEFT;
1033 break;
1034 case wxALIGN_CENTER:
1035 case wxALIGN_CENTER_HORIZONTAL:
1036 hdi.fmt |= HDF_CENTER;
1037 break;
1038 case wxALIGN_RIGHT:
1039 hdi.fmt |= HDF_RIGHT;
1040 break;
5d3f234b
RR
1041
1042 default:
1043 // such alignment is not allowed for the column header!
1044 wxFAIL;
9861f022 1045 }
c741d33f
VZ
1046
1047 SendMessage((HWND)m_hWnd, HDM_INSERTITEM,
1048 (WPARAM)added, (LPARAM)&hdi);
9861f022 1049 added++;
87f0efe2
RR
1050 }
1051}
1052
9861f022
RR
1053unsigned int wxDataViewHeaderWindowMSW::GetColumnIdxFromHeader(NMHEADER *nmHDR)
1054{
1055 unsigned int idx;
1056
1057 // NOTE: we don't just return nmHDR->iItem because when there are
1058 // hidden columns, nmHDR->iItem may be different from
1059 // nmHDR->pitem->lParam
1060
1061 if (nmHDR->pitem && nmHDR->pitem->mask & HDI_LPARAM)
1062 {
1063 idx = (unsigned int)nmHDR->pitem->lParam;
1064 return idx;
1065 }
1066
1067 HDITEM item;
1068 item.mask = HDI_LPARAM;
1069 Header_GetItem((HWND)m_hWnd, nmHDR->iItem, &item);
1070
1071 return (unsigned int)item.lParam;
1072}
1073
87f0efe2
RR
1074bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1075{
1076 NMHDR *nmhdr = (NMHDR *)lParam;
1077
1078 // is it a message from the header?
1079 if ( nmhdr->hwndFrom != (HWND)m_hWnd )
1080 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1081
1082 NMHEADER *nmHDR = (NMHEADER *)nmhdr;
1083 switch ( nmhdr->code )
1084 {
1085 case HDN_BEGINTRACK:
1086 // user has started to resize a column:
1087 // do we need to veto it?
1088 if (!GetColumn(nmHDR->iItem)->IsResizeable())
1089 {
1090 // veto it!
1091 *result = TRUE;
1092 }
1093 break;
1094
1095 case HDN_BEGINDRAG:
1096 // user has started to reorder a column
1097 break;
1098
9861f022 1099 case HDN_ITEMCHANGING:
c741d33f 1100 if (nmHDR->pitem != NULL &&
9861f022
RR
1101 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1102 {
1103 int minWidth = GetColumnFromHeader(nmHDR)->GetMinWidth();
1104 if (nmHDR->pitem->cxy < minWidth)
1105 {
c741d33f 1106 // do not allow the user to resize this column under
9861f022
RR
1107 // its minimal width:
1108 *result = TRUE;
1109 }
1110 }
1111 break;
1112
87f0efe2
RR
1113 case HDN_ITEMCHANGED: // user is resizing a column
1114 case HDN_ENDTRACK: // user has finished resizing a column
1115 case HDN_ENDDRAG: // user has finished reordering a column
1116
1117 // update the width of the modified column:
c741d33f 1118 if (nmHDR->pitem != NULL &&
9861f022
RR
1119 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1120 {
1121 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1122 unsigned int w = nmHDR->pitem->cxy;
1123 wxDataViewColumn *col = GetColumn(idx);
1124
1125 // see UpdateDisplay() for more info about COLUMN_WIDTH_OFFSET
1126 if (idx == 0 && w > COLUMN_WIDTH_OFFSET)
1127 w -= COLUMN_WIDTH_OFFSET;
1128
1129 if (w >= (unsigned)col->GetMinWidth())
1130 col->SetInternalWidth(w);
1131 }
87f0efe2
RR
1132 break;
1133
1134 case HDN_ITEMCLICK:
1135 {
9861f022 1136 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
c741d33f 1137 wxEventType evt = nmHDR->iButton == 0 ?
87f0efe2
RR
1138 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1139 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
9861f022 1140 SendEvent(evt, idx);
87f0efe2
RR
1141 }
1142 break;
1143
1144 case NM_RCLICK:
1145 {
1146 // NOTE: for some reason (i.e. for a bug in Windows)
1147 // the HDN_ITEMCLICK notification is not sent on
1148 // right clicks, so we need to handle NM_RCLICK
1149
1150 POINT ptClick;
9861f022 1151 int column = wxMSWGetColumnClicked(nmhdr, &ptClick);
87f0efe2 1152 if (column != wxNOT_FOUND)
9861f022
RR
1153 {
1154 HDITEM item;
1155 item.mask = HDI_LPARAM;
1156 Header_GetItem((HWND)m_hWnd, column, &item);
1157
1158 // 'idx' may be different from 'column' if there are
1159 // hidden columns...
1160 unsigned int idx = (unsigned int)item.lParam;
87f0efe2 1161 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
9861f022
RR
1162 idx);
1163 }
87f0efe2
RR
1164 }
1165 break;
1166
1167 case HDN_GETDISPINFOW:
1168 // see wxListCtrl::MSWOnNotify for more info!
1169 break;
1170
1171 case HDN_ITEMDBLCLICK:
1172 {
9861f022 1173 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
87f0efe2
RR
1174 int w = GetOwner()->GetBestColumnWidth(idx);
1175
1176 // update the native control:
1177 HDITEM hd;
1178 ZeroMemory(&hd, sizeof(hd));
1179 hd.mask = HDI_WIDTH;
1180 hd.cxy = w;
c741d33f 1181 Header_SetItem(GetHwnd(),
9861f022
RR
1182 nmHDR->iItem, // NOTE: we don't want 'idx' here!
1183 &hd);
87f0efe2
RR
1184
1185 // update the wxDataViewColumn class:
9861f022 1186 GetColumn(idx)->SetInternalWidth(w);
87f0efe2
RR
1187 }
1188 break;
1189
1190 default:
1191 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1192 }
1193
1194 return true;
1195}
1196
c741d33f 1197void wxDataViewHeaderWindowMSW::ScrollWindow(int WXUNUSED(dx), int WXUNUSED(dy),
87f0efe2
RR
1198 const wxRect *WXUNUSED(rect))
1199{
1200 wxSize ourSz = GetClientSize();
1201 wxSize ownerSz = m_owner->GetClientSize();
1202
1203 // where should the (logical) origin of this window be placed?
1204 int x1 = 0, y1 = 0;
1205 m_owner->CalcUnscrolledPosition(0, 0, &x1, &y1);
4ed7af08 1206
c741d33f
VZ
1207 // put this window on top of our parent and
1208 SetWindowPos((HWND)m_hWnd, HWND_TOP, -x1, 0,
1209 ownerSz.GetWidth() + x1, ourSz.GetHeight(),
87f0efe2
RR
1210 SWP_SHOWWINDOW);
1211}
1212
c741d33f
VZ
1213void wxDataViewHeaderWindowMSW::DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
1214 int WXUNUSED(w), int WXUNUSED(h),
9861f022
RR
1215 int WXUNUSED(f))
1216{
1217 // the wxDataViewCtrl's internal wxBoxSizer will call this function when
1218 // the wxDataViewCtrl window gets resized: the following dummy call
1219 // to ScrollWindow() is required in order to get this header window
1220 // correctly repainted when it's (horizontally) scrolled:
1221
1222 ScrollWindow(0, 0);
1223}
1224
87f0efe2
RR
1225#else // !defined(__WXMSW__)
1226
1227IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow)
1228BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow)
1229 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint)
1230 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse)
1231 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus)
4ed7af08
RR
1232END_EVENT_TABLE()
1233
87f0efe2 1234bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f
VZ
1235 const wxPoint &pos, const wxSize &size,
1236 const wxString &name )
4ed7af08 1237{
87f0efe2 1238 m_owner = parent;
4b3feaa7 1239
87f0efe2
RR
1240 if (!wxDataViewHeaderWindowBase::Create(parent, id, pos, size, name))
1241 return false;
f554a14b 1242
4ed7af08 1243 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
2e992e06 1244 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
4ed7af08
RR
1245 SetOwnForegroundColour( attr.colFg );
1246 SetOwnBackgroundColour( attr.colBg );
1247 if (!m_hasFont)
1248 SetOwnFont( attr.font );
4ed7af08 1249
87f0efe2
RR
1250 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1251 // a wxBoxSizer and in order to avoid super-big header windows,
1252 // we need to set our height as fixed
1253 SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1254 SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1255
1256 return true;
4ed7af08
RR
1257}
1258
87f0efe2 1259void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
4ed7af08 1260{
4b3feaa7
RR
1261 int w, h;
1262 GetClientSize( &w, &h );
1263
2e992e06
VZ
1264 wxAutoBufferedPaintDC dc( this );
1265
1266 dc.SetBackground(GetBackgroundColour());
1267 dc.Clear();
f554a14b 1268
4ed7af08
RR
1269 int xpix;
1270 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1271
1272 int x;
1273 m_owner->GetViewStart( &x, NULL );
1274
1275 // account for the horz scrollbar offset
1276 dc.SetDeviceOrigin( -x * xpix, 0 );
f554a14b 1277
4ed7af08 1278 dc.SetFont( GetFont() );
f554a14b 1279
9861f022 1280 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 1281 unsigned int i;
4b3feaa7
RR
1282 int xpos = 0;
1283 for (i = 0; i < cols; i++)
1284 {
87f0efe2
RR
1285 wxDataViewColumn *col = GetColumn( i );
1286 if (col->IsHidden())
9861f022 1287 continue; // skip it!
f554a14b 1288
87f0efe2 1289 int cw = col->GetWidth();
4b3feaa7 1290 int ch = h;
4b3feaa7 1291
c3112d56
RR
1292 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE;
1293 if (col->IsSortable())
1294 {
1295 if (col->IsSortOrderAscending())
1296 sortArrow = wxHDR_SORT_ICON_UP;
1297 else
1298 sortArrow = wxHDR_SORT_ICON_DOWN;
1299 }
1300
4b3feaa7
RR
1301 wxRendererNative::Get().DrawHeaderButton
1302 (
1303 this,
1304 dc,
72664514 1305 wxRect(xpos, 0, cw, ch-1),
4b3feaa7 1306 m_parent->IsEnabled() ? 0
c3112d56
RR
1307 : (int)wxCONTROL_DISABLED,
1308 sortArrow
4b3feaa7
RR
1309 );
1310
9861f022
RR
1311 // align as required the column title:
1312 int x = xpos;
1313 wxSize titleSz = dc.GetTextExtent(col->GetTitle());
1314 switch (col->GetAlignment())
1315 {
1316 case wxALIGN_LEFT:
1317 x += HEADER_HORIZ_BORDER;
1318 break;
1319 case wxALIGN_CENTER:
1320 case wxALIGN_CENTER_HORIZONTAL:
1321 x += (cw - titleSz.GetWidth() - 2 * HEADER_HORIZ_BORDER)/2;
1322 break;
1323 case wxALIGN_RIGHT:
1324 x += cw - titleSz.GetWidth() - HEADER_HORIZ_BORDER;
1325 break;
1326 }
1327
1328 // always center the title vertically:
1329 int y = wxMax((ch - titleSz.GetHeight()) / 2, HEADER_VERT_BORDER);
1330
c741d33f 1331 dc.SetClippingRegion( xpos+HEADER_HORIZ_BORDER,
9861f022
RR
1332 HEADER_VERT_BORDER,
1333 wxMax(cw - 2 * HEADER_HORIZ_BORDER, 1), // width
1334 wxMax(ch - 2 * HEADER_VERT_BORDER, 1)); // height
1335 dc.DrawText( col->GetTitle(), x, y );
1336 dc.DestroyClippingRegion();
f554a14b 1337
87f0efe2 1338 xpos += cw;
4b3feaa7 1339 }
4ed7af08
RR
1340}
1341
87f0efe2 1342void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
4ed7af08 1343{
87f0efe2
RR
1344 GetParent()->SetFocus();
1345 event.Skip();
4ed7af08
RR
1346}
1347
87f0efe2 1348void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent &event )
4ed7af08 1349{
87f0efe2
RR
1350 // we want to work with logical coords
1351 int x;
1352 m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
1353 int y = event.GetY();
1354
1355 if (m_isDragging)
1356 {
c741d33f 1357 // we don't draw the line beyond our window,
87f0efe2
RR
1358 // but we allow dragging it there
1359 int w = 0;
1360 GetClientSize( &w, NULL );
1361 m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
1362 w -= 6;
1363
1364 // erase the line if it was drawn
c741d33f 1365 if (m_currentX < w)
87f0efe2
RR
1366 DrawCurrent();
1367
c741d33f 1368 if (event.ButtonUp())
87f0efe2
RR
1369 {
1370 m_isDragging = false;
c741d33f 1371 if (HasCapture())
87f0efe2
RR
1372 ReleaseMouse();
1373
1374 m_dirty = true;
1375
9861f022 1376 GetColumn(m_column)->SetWidth(m_currentX - m_minX);
87f0efe2
RR
1377
1378 Refresh();
1379 GetOwner()->Refresh();
1380 }
1381 else
1382 {
1383 m_currentX = wxMax(m_minX + 7, x);
1384
1385 // draw in the new location
1386 if (m_currentX < w) DrawCurrent();
1387 }
1388
1389 }
1390 else // not dragging
1391 {
1392 m_minX = 0;
1393 m_column = wxNOT_FOUND;
1394
1395 bool hit_border = false;
1396
1397 // end of the current column
1398 int xpos = 0;
1399
1400 // find the column where this event occured
9861f022 1401 int countCol = m_owner->GetColumnCount();
c741d33f 1402 for (int column = 0; column < countCol; column++)
87f0efe2
RR
1403 {
1404 wxDataViewColumn *p = GetColumn(column);
1405
c741d33f 1406 if (p->IsHidden())
87f0efe2
RR
1407 continue; // skip if not shown
1408
1409 xpos += p->GetWidth();
1410 m_column = column;
c741d33f 1411 if ((abs(x-xpos) < 3) && (y < 22))
87f0efe2
RR
1412 {
1413 hit_border = true;
1414 break;
1415 }
1416
c741d33f 1417 if (x < xpos)
87f0efe2
RR
1418 {
1419 // inside the column
1420 break;
1421 }
1422
1423 m_minX = xpos;
1424 }
1425
1426 if (m_column == wxNOT_FOUND)
1427 return;
1428
1429 bool resizeable = GetColumn(m_column)->IsResizeable();
1430 if (event.LeftDClick() && resizeable)
1431 {
9861f022 1432 GetColumn(m_column)->SetWidth(GetOwner()->GetBestColumnWidth(m_column));
87f0efe2
RR
1433 Refresh();
1434 }
1435 else if (event.LeftDown() || event.RightUp())
1436 {
c741d33f 1437 if (hit_border && event.LeftDown() && resizeable)
87f0efe2
RR
1438 {
1439 m_isDragging = true;
1440 CaptureMouse();
1441 m_currentX = x;
1442 DrawCurrent();
1443 }
1444 else // click on a column
1445 {
c741d33f 1446 wxEventType evt = event.LeftDown() ?
87f0efe2
RR
1447 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1448 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1449 SendEvent(evt, m_column);
1450 }
1451 }
1452 else if (event.Moving())
1453 {
c741d33f 1454 if (hit_border && resizeable)
87f0efe2
RR
1455 m_currentCursor = m_resizeCursor;
1456 else
1457 m_currentCursor = wxSTANDARD_CURSOR;
1458
1459 SetCursor(*m_currentCursor);
1460 }
1461 }
1462}
1463
1464void wxGenericDataViewHeaderWindow::DrawCurrent()
1465{
1466 int x1 = m_currentX;
1467 int y1 = 0;
1468 ClientToScreen (&x1, &y1);
1469
1470 int x2 = m_currentX-1;
1471#ifdef __WXMSW__
1472 ++x2; // but why ????
1473#endif
1474 int y2 = 0;
1475 m_owner->GetClientSize( NULL, &y2 );
1476 m_owner->ClientToScreen( &x2, &y2 );
1477
1478 wxScreenDC dc;
9861f022
RR
1479 dc.SetLogicalFunction(wxINVERT);
1480 dc.SetPen(m_penCurrent);
1481 dc.SetBrush(*wxTRANSPARENT_BRUSH);
87f0efe2 1482 AdjustDC(dc);
9861f022 1483 dc.DrawLine(x1, y1, x2, y2);
87f0efe2
RR
1484}
1485
1486void wxGenericDataViewHeaderWindow::AdjustDC(wxDC& dc)
1487{
1488 int xpix, x;
1489
1490 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1491 m_owner->GetViewStart( &x, NULL );
1492
1493 // shift the DC origin to match the position of the main window horizontal
1494 // scrollbar: this allows us to always use logical coords
1495 dc.SetDeviceOrigin( -x * xpix, 0 );
4ed7af08
RR
1496}
1497
87f0efe2
RR
1498#endif // defined(__WXMSW__)
1499
0fcce6b9
RR
1500//-----------------------------------------------------------------------------
1501// wxDataViewRenameTimer
1502//-----------------------------------------------------------------------------
1503
1504wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1505{
1506 m_owner = owner;
1507}
1508
1509void wxDataViewRenameTimer::Notify()
1510{
1511 m_owner->OnRenameTimer();
1512}
1513
4ed7af08
RR
1514//-----------------------------------------------------------------------------
1515// wxDataViewMainWindow
1516//-----------------------------------------------------------------------------
1517
0a71f9e9 1518int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
cab07038
RR
1519{
1520 if (row1 > row2) return 1;
1521 if (row1 == row2) return 0;
1522 return -1;
1523}
1524
1525
45778c96 1526IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
4ed7af08
RR
1527
1528BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1529 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1530 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1531 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
cab07038
RR
1532 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1533 EVT_CHAR (wxDataViewMainWindow::OnChar)
4ed7af08
RR
1534END_EVENT_TABLE()
1535
1536wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1537 const wxPoint &pos, const wxSize &size, const wxString &name ) :
72664514 1538 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
cab07038 1539 m_selection( wxDataViewSelectionCmp )
120b9b05 1540
4ed7af08
RR
1541{
1542 SetOwner( parent );
f554a14b 1543
0fcce6b9
RR
1544 m_lastOnSame = false;
1545 m_renameTimer = new wxDataViewRenameTimer( this );
120b9b05 1546
0fcce6b9
RR
1547 // TODO: user better initial values/nothing selected
1548 m_currentCol = NULL;
1549 m_currentRow = 0;
1550
1551 // TODO: we need to calculate this smartly
c741d33f 1552 m_lineHeight =
87f0efe2
RR
1553#ifdef __WXMSW__
1554 17;
1555#else
1556 20;
1557#endif
9861f022 1558 wxASSERT(m_lineHeight > 2*PADDING_TOPBOTTOM);
e21f75bd
RR
1559
1560 m_dragCount = 0;
1561 m_dragStart = wxPoint(0,0);
0a71f9e9
RR
1562 m_lineLastClicked = (unsigned int) -1;
1563 m_lineBeforeLastClicked = (unsigned int) -1;
1564 m_lineSelectSingleOnUp = (unsigned int) -1;
120b9b05 1565
cab07038 1566 m_hasFocus = false;
f554a14b 1567
2e992e06 1568 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
72664514
RR
1569 SetBackgroundColour( *wxWHITE );
1570
9861f022
RR
1571 m_penRule = wxPen(GetRuleColour(), 1, wxSOLID);
1572
4b3feaa7 1573 UpdateDisplay();
4ed7af08
RR
1574}
1575
1576wxDataViewMainWindow::~wxDataViewMainWindow()
1577{
0fcce6b9
RR
1578 delete m_renameTimer;
1579}
1580
1581void wxDataViewMainWindow::OnRenameTimer()
1582{
1583 // We have to call this here because changes may just have
1584 // been made and no screen update taken place.
1585 if ( m_dirty )
1586 wxSafeYield();
1587
0fcce6b9 1588 int xpos = 0;
9861f022 1589 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 1590 unsigned int i;
0fcce6b9
RR
1591 for (i = 0; i < cols; i++)
1592 {
1593 wxDataViewColumn *c = GetOwner()->GetColumn( i );
9861f022
RR
1594 if (c->IsHidden())
1595 continue; // skip it!
1596
0fcce6b9
RR
1597 if (c == m_currentCol)
1598 break;
1599 xpos += c->GetWidth();
1600 }
c741d33f 1601 wxRect labelRect( xpos, m_currentRow * m_lineHeight,
87f0efe2 1602 m_currentCol->GetWidth(), m_lineHeight );
0fcce6b9 1603
1e510b1e
RR
1604 GetOwner()->CalcScrolledPosition( labelRect.x, labelRect.y,
1605 &labelRect.x, &labelRect.y);
1606
99d471a5 1607 m_currentCol->GetRenderer()->StartEditing( m_currentRow, labelRect );
4ed7af08
RR
1608}
1609
a0f3af5f
RR
1610bool wxDataViewMainWindow::RowAppended()
1611{
99d471a5
RR
1612 UpdateDisplay();
1613 return true;
a0f3af5f
RR
1614}
1615
1616bool wxDataViewMainWindow::RowPrepended()
1617{
99d471a5
RR
1618 UpdateDisplay();
1619 return true;
a0f3af5f
RR
1620}
1621
0a71f9e9 1622bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before) )
a0f3af5f 1623{
99d471a5
RR
1624 UpdateDisplay();
1625 return true;
a0f3af5f
RR
1626}
1627
0a71f9e9 1628bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row) )
a0f3af5f 1629{
99d471a5
RR
1630 UpdateDisplay();
1631 return true;
a0f3af5f
RR
1632}
1633
0a71f9e9 1634bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row) )
a0f3af5f 1635{
99d471a5
RR
1636 UpdateDisplay();
1637 return true;
a0f3af5f
RR
1638}
1639
0a71f9e9 1640bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col), unsigned int row )
a0f3af5f 1641{
9861f022
RR
1642 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
1643#define MAX_VIRTUAL_WIDTH 100000
1644
1645 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
0fdc2321
RR
1646 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
1647 Refresh( true, &rect );
1648
1649 return true;
a0f3af5f
RR
1650}
1651
0a71f9e9 1652bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order) )
a0f3af5f 1653{
99d471a5 1654 UpdateDisplay();
0fcce6b9 1655 return true;
a0f3af5f
RR
1656}
1657
1658bool wxDataViewMainWindow::Cleared()
1659{
99d471a5
RR
1660 UpdateDisplay();
1661 return true;
a0f3af5f
RR
1662}
1663
4b3feaa7
RR
1664void wxDataViewMainWindow::UpdateDisplay()
1665{
1666 m_dirty = true;
1667}
1668
1669void wxDataViewMainWindow::OnInternalIdle()
1670{
1671 wxWindow::OnInternalIdle();
f554a14b 1672
4b3feaa7
RR
1673 if (m_dirty)
1674 {
1675 RecalculateDisplay();
1676 m_dirty = false;
1677 }
1678}
1679
1680void wxDataViewMainWindow::RecalculateDisplay()
1681{
1682 wxDataViewListModel *model = GetOwner()->GetModel();
1683 if (!model)
1684 {
1685 Refresh();
1686 return;
1687 }
f554a14b 1688
9861f022
RR
1689 int width = GetEndOfLastCol();
1690 int height = model->GetRowCount() * m_lineHeight;
4b3feaa7
RR
1691
1692 SetVirtualSize( width, height );
1693 GetOwner()->SetScrollRate( 10, m_lineHeight );
f554a14b 1694
4b3feaa7
RR
1695 Refresh();
1696}
1697
1698void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1699{
1700 wxWindow::ScrollWindow( dx, dy, rect );
9861f022
RR
1701
1702 if (GetOwner()->m_headerArea)
1703 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
4b3feaa7
RR
1704}
1705
f554a14b 1706void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
4ed7af08 1707{
9861f022 1708 wxDataViewListModel *model = GetOwner()->GetModel();
2e992e06
VZ
1709 wxAutoBufferedPaintDC dc( this );
1710
9861f022 1711 // prepare the DC
2e992e06
VZ
1712 dc.SetBackground(GetBackgroundColour());
1713 dc.Clear();
4b3feaa7 1714 GetOwner()->PrepareDC( dc );
4ed7af08 1715 dc.SetFont( GetFont() );
90675b95
RR
1716
1717 wxRect update = GetUpdateRegion().GetBox();
1718 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
f554a14b 1719
9861f022 1720 // compute which items needs to be redrawn
0a71f9e9 1721 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
c741d33f 1722 unsigned int item_count =
87f0efe2 1723 wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
9861f022
RR
1724 (int)(model->GetRowCount() - item_start) );
1725 unsigned int item_last = item_start + item_count;
1726
1727 // compute which columns needs to be redrawn
1728 unsigned int cols = GetOwner()->GetColumnCount();
1729 unsigned int col_start = 0;
1730 unsigned int x_start = 0;
1731 for (x_start = 0; col_start < cols; col_start++)
1732 {
1733 wxDataViewColumn *col = GetOwner()->GetColumn(col_start);
1734 if (col->IsHidden())
1735 continue; // skip it!
1736
1737 unsigned int w = col->GetWidth();
1738 if (x_start+w >= (unsigned int)update.x)
1739 break;
1740
1741 x_start += w;
1742 }
90675b95 1743
9861f022
RR
1744 unsigned int col_last = col_start;
1745 unsigned int x_last = x_start;
1746 for (; col_last < cols; col_last++)
1747 {
1748 wxDataViewColumn *col = GetOwner()->GetColumn(col_last);
1749 if (col->IsHidden())
1750 continue; // skip it!
1751
1752 if (x_last > (unsigned int)update.GetRight())
1753 break;
1754
1755 x_last += col->GetWidth();
1756 }
1757
1758 // Draw horizontal rules if required
1759 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1760 {
1761 dc.SetPen(m_penRule);
1762 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1763
1764 for (unsigned int i = item_start; i <= item_last+1; i++)
1765 {
1766 int y = i * m_lineHeight;
1767 dc.DrawLine(x_start, y, x_last, y);
1768 }
1769 }
1770
1771 // Draw vertical rules if required
1772 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
1773 {
1774 dc.SetPen(m_penRule);
1775 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1776
1777 int x = x_start;
1778 for (unsigned int i = col_start; i < col_last; i++)
1779 {
1780 wxDataViewColumn *col = GetOwner()->GetColumn(i);
1781 if (col->IsHidden())
1782 continue; // skip it
1783
1784 dc.DrawLine(x, item_start * m_lineHeight,
1785 x, item_last * m_lineHeight);
1786
1787 x += col->GetWidth();
1788 }
1789
1790 // Draw last vertical rule
c741d33f 1791 dc.DrawLine(x, item_start * m_lineHeight,
9861f022
RR
1792 x, item_last * m_lineHeight);
1793 }
1794
1795 // redraw the background for the items which are selected/current
1796 for (unsigned int item = item_start; item < item_last; item++)
cab07038 1797 {
87f0efe2
RR
1798 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1799 if (selected || item == m_currentRow)
cab07038 1800 {
5d3f234b 1801 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
daebb44c
RR
1802 if (item == m_currentRow)
1803 flags |= wxCONTROL_CURRENT;
1804 if (m_hasFocus)
1805 flags |= wxCONTROL_FOCUSED;
9861f022
RR
1806
1807 wxRect rect( x_start, item*m_lineHeight, x_last, m_lineHeight );
daebb44c
RR
1808 wxRendererNative::Get().DrawItemSelectionRect
1809 (
1810 this,
1811 dc,
1812 rect,
1813 flags
1814 );
1815 }
cab07038 1816 }
120b9b05 1817
9861f022 1818 // redraw all cells for all rows which must be repainted and for all columns
90675b95 1819 wxRect cell_rect;
9861f022
RR
1820 cell_rect.x = x_start;
1821 cell_rect.height = m_lineHeight; // -1 is for the horizontal rules
1822 for (unsigned int i = col_start; i < col_last; i++)
90675b95
RR
1823 {
1824 wxDataViewColumn *col = GetOwner()->GetColumn( i );
baa9ebc4 1825 wxDataViewRenderer *cell = col->GetRenderer();
90675b95 1826 cell_rect.width = col->GetWidth();
f554a14b 1827
87f0efe2
RR
1828 if (col->IsHidden())
1829 continue; // skipt it!
1830
9861f022 1831 for (unsigned int item = item_start; item < item_last; item++)
90675b95 1832 {
87f0efe2 1833 // get the cell value and set it into the renderer
90675b95
RR
1834 wxVariant value;
1835 model->GetValue( value, col->GetModelColumn(), item );
1836 cell->SetValue( value );
87f0efe2
RR
1837
1838 // update the y offset
9861f022 1839 cell_rect.y = item * m_lineHeight;
87f0efe2 1840
4064f7de 1841 // cannot be bigger than allocated space
87f0efe2 1842 wxSize size = cell->GetSize();
9861f022
RR
1843 size.x = wxMin( size.x + 2*PADDING_RIGHTLEFT, cell_rect.width );
1844 size.y = wxMin( size.y + 2*PADDING_TOPBOTTOM, cell_rect.height );
87f0efe2
RR
1845
1846 wxRect item_rect(cell_rect.GetTopLeft(), size);
9861f022 1847 int align = cell->GetAlignment();
87f0efe2
RR
1848
1849 // horizontal alignment:
9861f022
RR
1850 item_rect.x = cell_rect.x;
1851 if (align & wxALIGN_CENTER_HORIZONTAL)
1852 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
1853 else if (align & wxALIGN_RIGHT)
87f0efe2
RR
1854 item_rect.x = cell_rect.x + cell_rect.width - size.x;
1855 //else: wxALIGN_LEFT is the default
1856
1857 // vertical alignment:
9861f022
RR
1858 item_rect.y = cell_rect.y;
1859 if (align & wxALIGN_CENTER_VERTICAL)
87f0efe2 1860 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
9861f022 1861 else if (align & wxALIGN_BOTTOM)
87f0efe2
RR
1862 item_rect.y = cell_rect.y + cell_rect.height - size.y;
1863 //else: wxALIGN_TOP is the default
1864
9861f022
RR
1865 // add padding
1866 item_rect.x += PADDING_RIGHTLEFT;
1867 item_rect.y += PADDING_TOPBOTTOM;
1868 item_rect.width = size.x - 2 * PADDING_RIGHTLEFT;
1869 item_rect.height = size.y - 2 * PADDING_TOPBOTTOM;
64b3c262
VZ
1870
1871 int state = 0;
87f0efe2 1872 if (m_selection.Index(item) != wxNOT_FOUND)
64b3c262 1873 state |= wxDATAVIEW_CELL_SELECTED;
87f0efe2 1874
9861f022
RR
1875 // TODO: it would be much more efficient to create a clipping
1876 // region for the entire column being rendered (in the OnPaint
1877 // of wxDataViewMainWindow) instead of a single clip region for
1878 // each cell. However it would mean that each renderer should
1879 // respect the given wxRect's top & bottom coords, eventually
1880 // violating only the left & right coords - however the user can
1881 // make its own renderer and thus we cannot be sure of that.
1882 dc.SetClippingRegion( item_rect );
64b3c262 1883 cell->Render( item_rect, &dc, state );
9861f022 1884 dc.DestroyClippingRegion();
90675b95 1885 }
f554a14b 1886
90675b95
RR
1887 cell_rect.x += cell_rect.width;
1888 }
4ed7af08
RR
1889}
1890
9861f022 1891int wxDataViewMainWindow::GetCountPerPage() const
cab07038
RR
1892{
1893 wxSize size = GetClientSize();
1894 return size.y / m_lineHeight;
1895}
1896
9861f022 1897int wxDataViewMainWindow::GetEndOfLastCol() const
e21f75bd
RR
1898{
1899 int width = 0;
0a71f9e9 1900 unsigned int i;
9861f022 1901 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
e21f75bd 1902 {
c741d33f 1903 const wxDataViewColumn *c =
9861f022
RR
1904 wx_const_cast(wxDataViewCtrl*, GetOwner())->GetColumn( i );
1905
1906 if (!c->IsHidden())
1907 width += c->GetWidth();
e21f75bd
RR
1908 }
1909 return width;
1910}
1911
9861f022 1912unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
72664514
RR
1913{
1914 int x = 0;
1915 int y = 0;
1916 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
120b9b05 1917
72664514
RR
1918 return y / m_lineHeight;
1919}
1920
9861f022 1921unsigned int wxDataViewMainWindow::GetLastVisibleRow() const
72664514
RR
1922{
1923 wxSize client_size = GetClientSize();
c741d33f 1924 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
87f0efe2 1925 &client_size.x, &client_size.y );
72664514 1926
5637e131 1927 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
72664514
RR
1928}
1929
9861f022 1930unsigned int wxDataViewMainWindow::GetRowCount() const
cab07038 1931{
9861f022 1932 return wx_const_cast(wxDataViewCtrl*, GetOwner())->GetModel()->GetRowCount();
cab07038
RR
1933}
1934
0a71f9e9 1935void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
e21f75bd
RR
1936{
1937 m_currentRow = row;
120b9b05 1938
e21f75bd
RR
1939 // send event
1940}
1941
cab07038
RR
1942void wxDataViewMainWindow::SelectAllRows( bool on )
1943{
4a851b11
VZ
1944 if (IsEmpty())
1945 return;
120b9b05 1946
cab07038
RR
1947 if (on)
1948 {
72664514 1949 m_selection.Clear();
0a71f9e9 1950 for (unsigned int i = 0; i < GetRowCount(); i++)
cab07038 1951 m_selection.Add( i );
72664514
RR
1952 Refresh();
1953 }
1954 else
1955 {
0a71f9e9
RR
1956 unsigned int first_visible = GetFirstVisibleRow();
1957 unsigned int last_visible = GetLastVisibleRow();
1958 unsigned int i;
72664514 1959 for (i = 0; i < m_selection.GetCount(); i++)
120b9b05 1960 {
0a71f9e9 1961 unsigned int row = m_selection[i];
72664514
RR
1962 if ((row >= first_visible) && (row <= last_visible))
1963 RefreshRow( row );
1964 }
1965 m_selection.Clear();
cab07038 1966 }
cab07038
RR
1967}
1968
0a71f9e9 1969void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
cab07038
RR
1970{
1971 if (m_selection.Index( row ) == wxNOT_FOUND)
1972 {
1973 if (on)
1974 {
1975 m_selection.Add( row );
1976 RefreshRow( row );
1977 }
1978 }
1979 else
1980 {
1981 if (!on)
1982 {
1983 m_selection.Remove( row );
1984 RefreshRow( row );
1985 }
1986 }
1987}
1988
0a71f9e9 1989void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
cab07038
RR
1990{
1991 if (from > to)
1992 {
0a71f9e9 1993 unsigned int tmp = from;
cab07038
RR
1994 from = to;
1995 to = tmp;
1996 }
1997
0a71f9e9 1998 unsigned int i;
cab07038
RR
1999 for (i = from; i <= to; i++)
2000 {
2001 if (m_selection.Index( i ) == wxNOT_FOUND)
2002 {
2003 if (on)
2004 m_selection.Add( i );
2005 }
2006 else
2007 {
2008 if (!on)
2009 m_selection.Remove( i );
2010 }
2011 }
2012 RefreshRows( from, to );
2013}
2014
87f0efe2
RR
2015void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2016{
2017 for (size_t i=0; i < aSelections.GetCount(); i++)
2018 {
2019 int n = aSelections[i];
2020
2021 m_selection.Add( n );
2022 RefreshRow( n );
2023 }
2024}
2025
0a71f9e9 2026void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
cab07038
RR
2027{
2028 if (m_selection.Index( row ) == wxNOT_FOUND)
2029 m_selection.Add( row );
2030 else
2031 m_selection.Remove( row );
120b9b05 2032 RefreshRow( row );
cab07038
RR
2033}
2034
0a71f9e9 2035bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
cab07038
RR
2036{
2037 return (m_selection.Index( row ) != wxNOT_FOUND);
2038}
2039
0a71f9e9 2040void wxDataViewMainWindow::RefreshRow( unsigned int row )
cab07038 2041{
e21f75bd 2042 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
cab07038 2043 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2044
cab07038
RR
2045 wxSize client_size = GetClientSize();
2046 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2047 wxRect intersect_rect = client_rect.Intersect( rect );
2048 if (intersect_rect.width > 0)
2049 Refresh( true, &intersect_rect );
2050}
2051
0a71f9e9 2052void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
cab07038
RR
2053{
2054 if (from > to)
2055 {
0a71f9e9 2056 unsigned int tmp = to;
cab07038
RR
2057 to = from;
2058 from = tmp;
2059 }
2060
e21f75bd 2061 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
cab07038 2062 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2063
cab07038
RR
2064 wxSize client_size = GetClientSize();
2065 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2066 wxRect intersect_rect = client_rect.Intersect( rect );
2067 if (intersect_rect.width > 0)
2068 Refresh( true, &intersect_rect );
2069}
2070
0a71f9e9 2071void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
cab07038 2072{
0a71f9e9 2073 unsigned int count = GetRowCount();
4a851b11
VZ
2074 if (firstRow > count)
2075 return;
120b9b05 2076
e21f75bd 2077 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
cab07038 2078 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2079
cab07038
RR
2080 wxSize client_size = GetClientSize();
2081 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2082 wxRect intersect_rect = client_rect.Intersect( rect );
2083 if (intersect_rect.width > 0)
2084 Refresh( true, &intersect_rect );
2085}
2086
0a71f9e9 2087void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
cab07038 2088{
4a851b11 2089 wxCHECK_RET( newCurrent < GetRowCount(),
cab07038
RR
2090 _T("invalid item index in OnArrowChar()") );
2091
2092 // if there is no selection, we cannot move it anywhere
2093 if (!HasCurrentRow())
2094 return;
2095
0a71f9e9 2096 unsigned int oldCurrent = m_currentRow;
cab07038
RR
2097
2098 // in single selection we just ignore Shift as we can't select several
2099 // items anyhow
e21f75bd 2100 if ( event.ShiftDown() && !IsSingleSel() )
cab07038
RR
2101 {
2102 RefreshRow( oldCurrent );
120b9b05 2103
e21f75bd 2104 ChangeCurrentRow( newCurrent );
cab07038
RR
2105
2106 // select all the items between the old and the new one
2107 if ( oldCurrent > newCurrent )
2108 {
2109 newCurrent = oldCurrent;
2110 oldCurrent = m_currentRow;
2111 }
2112
2113 SelectRows( oldCurrent, newCurrent, true );
2114 }
2115 else // !shift
2116 {
72664514 2117 RefreshRow( oldCurrent );
120b9b05 2118
cab07038
RR
2119 // all previously selected items are unselected unless ctrl is held
2120 if ( !event.ControlDown() )
2121 SelectAllRows(false);
2122
e21f75bd 2123 ChangeCurrentRow( newCurrent );
cab07038
RR
2124
2125 if ( !event.ControlDown() )
2126 SelectRow( m_currentRow, true );
72664514
RR
2127 else
2128 RefreshRow( m_currentRow );
cab07038
RR
2129 }
2130
9861f022
RR
2131 //EnsureVisible( m_currentRow );
2132}
2133
2134wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2135{
2136 wxRect rect;
2137 rect.x = 0;
2138 rect.y = m_lineHeight * row;
2139 rect.width = GetEndOfLastCol();
2140 rect.height = m_lineHeight;
2141
2142 return rect;
cab07038
RR
2143}
2144
2145void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
2146{
2147 if (event.GetKeyCode() == WXK_TAB)
2148 {
2149 wxNavigationKeyEvent nevent;
2150 nevent.SetWindowChange( event.ControlDown() );
2151 nevent.SetDirection( !event.ShiftDown() );
2152 nevent.SetEventObject( GetParent()->GetParent() );
2153 nevent.SetCurrentFocus( m_parent );
2154 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
2155 return;
2156 }
2157
2158 // no item -> nothing to do
2159 if (!HasCurrentRow())
2160 {
2161 event.Skip();
2162 return;
2163 }
120b9b05 2164
cab07038
RR
2165 // don't use m_linesPerPage directly as it might not be computed yet
2166 const int pageSize = GetCountPerPage();
2167 wxCHECK_RET( pageSize, _T("should have non zero page size") );
2168
2169 switch ( event.GetKeyCode() )
2170 {
2171 case WXK_UP:
2172 if ( m_currentRow > 0 )
2173 OnArrowChar( m_currentRow - 1, event );
2174 break;
2175
2176 case WXK_DOWN:
4a851b11 2177 if ( m_currentRow < GetRowCount() - 1 )
cab07038
RR
2178 OnArrowChar( m_currentRow + 1, event );
2179 break;
2180
2181 case WXK_END:
2182 if (!IsEmpty())
2183 OnArrowChar( GetRowCount() - 1, event );
2184 break;
2185
2186 case WXK_HOME:
2187 if (!IsEmpty())
2188 OnArrowChar( 0, event );
2189 break;
2190
2191 case WXK_PAGEUP:
2192 {
2193 int steps = pageSize - 1;
2194 int index = m_currentRow - steps;
2195 if (index < 0)
2196 index = 0;
2197
2198 OnArrowChar( index, event );
2199 }
2200 break;
2201
2202 case WXK_PAGEDOWN:
2203 {
2204 int steps = pageSize - 1;
0a71f9e9
RR
2205 unsigned int index = m_currentRow + steps;
2206 unsigned int count = GetRowCount();
cab07038
RR
2207 if ( index >= count )
2208 index = count - 1;
2209
2210 OnArrowChar( index, event );
2211 }
2212 break;
2213
2214 default:
2215 event.Skip();
2216 }
2217}
2218
4ed7af08
RR
2219void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
2220{
e21f75bd
RR
2221 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
2222 {
2223 // let the base handle mouse wheel events.
2224 event.Skip();
2225 return;
2226 }
2227
0fdc2321
RR
2228 int x = event.GetX();
2229 int y = event.GetY();
2230 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
2231
2232 wxDataViewColumn *col = NULL;
2233
2234 int xpos = 0;
9861f022 2235 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 2236 unsigned int i;
0fdc2321
RR
2237 for (i = 0; i < cols; i++)
2238 {
2239 wxDataViewColumn *c = GetOwner()->GetColumn( i );
9861f022
RR
2240 if (c->IsHidden())
2241 continue; // skip it!
2242
0fdc2321
RR
2243 if (x < xpos + c->GetWidth())
2244 {
2245 col = c;
2246 break;
2247 }
2248 xpos += c->GetWidth();
2249 }
f554a14b 2250 if (!col)
0fdc2321 2251 return;
baa9ebc4 2252 wxDataViewRenderer *cell = col->GetRenderer();
f554a14b 2253
0a71f9e9 2254 unsigned int current = y / m_lineHeight;
120b9b05 2255
e21f75bd
RR
2256 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
2257 {
2258 // Unselect all if below the last row ?
2259 return;
2260 }
f554a14b 2261
0fdc2321
RR
2262 wxDataViewListModel *model = GetOwner()->GetModel();
2263
e21f75bd
RR
2264 if (event.Dragging())
2265 {
2266 if (m_dragCount == 0)
2267 {
2268 // we have to report the raw, physical coords as we want to be
2269 // able to call HitTest(event.m_pointDrag) from the user code to
2270 // get the item being dragged
2271 m_dragStart = event.GetPosition();
2272 }
2273
2274 m_dragCount++;
2275
2276 if (m_dragCount != 3)
2277 return;
2278
2279 if (event.LeftIsDown())
2280 {
2281 // Notify cell about drag
2282 }
2283 return;
2284 }
2285 else
2286 {
2287 m_dragCount = 0;
2288 }
2289
2290 bool forceClick = false;
2291
0fcce6b9
RR
2292 if (event.ButtonDClick())
2293 {
2294 m_renameTimer->Stop();
2295 m_lastOnSame = false;
2296 }
2297
0fdc2321
RR
2298 if (event.LeftDClick())
2299 {
e21f75bd 2300 if ( current == m_lineLastClicked )
0fdc2321 2301 {
e21f75bd
RR
2302 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
2303 {
2304 wxVariant value;
2305 model->GetValue( value, col->GetModelColumn(), current );
2306 cell->SetValue( value );
c741d33f 2307 wxRect cell_rect( xpos, current * m_lineHeight,
87f0efe2 2308 col->GetWidth(), m_lineHeight );
e21f75bd
RR
2309 cell->Activate( cell_rect, model, col->GetModelColumn(), current );
2310 }
2311 return;
2312 }
2313 else
2314 {
2315 // The first click was on another item, so don't interpret this as
2316 // a double click, but as a simple click instead
2317 forceClick = true;
0fdc2321 2318 }
120b9b05 2319 }
f554a14b 2320
0fcce6b9
RR
2321 if (event.LeftUp())
2322 {
0a71f9e9 2323 if (m_lineSelectSingleOnUp != (unsigned int)-1)
e21f75bd
RR
2324 {
2325 // select single line
2326 SelectAllRows( false );
2327 SelectRow( m_lineSelectSingleOnUp, true );
2328 }
120b9b05 2329
0fcce6b9
RR
2330 if (m_lastOnSame)
2331 {
a8461d31 2332 if ((col == m_currentCol) && (current == m_currentRow) &&
0fcce6b9
RR
2333 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
2334 {
2335 m_renameTimer->Start( 100, true );
2336 }
2337 }
2338
2339 m_lastOnSame = false;
0a71f9e9 2340 m_lineSelectSingleOnUp = (unsigned int)-1;
120b9b05 2341 }
e21f75bd
RR
2342 else
2343 {
2344 // This is necessary, because after a DnD operation in
2345 // from and to ourself, the up event is swallowed by the
2346 // DnD code. So on next non-up event (which means here and
2347 // now) m_lineSelectSingleOnUp should be reset.
0a71f9e9 2348 m_lineSelectSingleOnUp = (unsigned int)-1;
e21f75bd
RR
2349 }
2350
2351 if (event.RightDown())
2352 {
2353 m_lineBeforeLastClicked = m_lineLastClicked;
2354 m_lineLastClicked = current;
2355
2356 // If the item is already selected, do not update the selection.
2357 // Multi-selections should not be cleared if a selected item is clicked.
2358 if (!IsRowSelected(current))
2359 {
2360 SelectAllRows(false);
2361 ChangeCurrentRow(current);
2362 SelectRow(m_currentRow,true);
2363 }
2364
2365 // notify cell about right click
2366 // cell->...
120b9b05 2367
e21f75bd
RR
2368 // Allow generation of context menu event
2369 event.Skip();
2370 }
2371 else if (event.MiddleDown())
2372 {
2373 // notify cell about middle click
2374 // cell->...
2375 }
2376 if (event.LeftDown() || forceClick)
0fcce6b9 2377 {
72664514
RR
2378#ifdef __WXMSW__
2379 SetFocus();
2380#endif
120b9b05 2381
e21f75bd
RR
2382 m_lineBeforeLastClicked = m_lineLastClicked;
2383 m_lineLastClicked = current;
2384
0a71f9e9 2385 unsigned int oldCurrentRow = m_currentRow;
e21f75bd
RR
2386 bool oldWasSelected = IsRowSelected(m_currentRow);
2387
2388 bool cmdModifierDown = event.CmdDown();
2389 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
2390 {
2391 if ( IsSingleSel() || !IsRowSelected(current) )
2392 {
2393 SelectAllRows( false );
2394
2395 ChangeCurrentRow(current);
2396
2397 SelectRow(m_currentRow,true);
2398 }
2399 else // multi sel & current is highlighted & no mod keys
2400 {
2401 m_lineSelectSingleOnUp = current;
2402 ChangeCurrentRow(current); // change focus
2403 }
2404 }
2405 else // multi sel & either ctrl or shift is down
2406 {
2407 if (cmdModifierDown)
2408 {
2409 ChangeCurrentRow(current);
2410
2411 ReverseRowSelection(m_currentRow);
2412 }
2413 else if (event.ShiftDown())
2414 {
2415 ChangeCurrentRow(current);
2416
0a71f9e9 2417 unsigned int lineFrom = oldCurrentRow,
e21f75bd
RR
2418 lineTo = current;
2419
2420 if ( lineTo < lineFrom )
2421 {
2422 lineTo = lineFrom;
2423 lineFrom = m_currentRow;
2424 }
2425
2426 SelectRows(lineFrom, lineTo, true);
2427 }
2428 else // !ctrl, !shift
2429 {
2430 // test in the enclosing if should make it impossible
2431 wxFAIL_MSG( _T("how did we get here?") );
2432 }
2433 }
2434
72664514
RR
2435 if (m_currentRow != oldCurrentRow)
2436 RefreshRow( oldCurrentRow );
e21f75bd 2437
0fcce6b9 2438 wxDataViewColumn *oldCurrentCol = m_currentCol;
120b9b05 2439
0fcce6b9
RR
2440 // Update selection here...
2441 m_currentCol = col;
120b9b05 2442
c741d33f 2443 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
87f0efe2 2444 (current == oldCurrentRow)) && oldWasSelected;
0fdc2321 2445 }
4ed7af08
RR
2446}
2447
2448void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
2449{
cab07038 2450 m_hasFocus = true;
120b9b05 2451
cab07038
RR
2452 if (HasCurrentRow())
2453 Refresh();
120b9b05 2454
cab07038
RR
2455 event.Skip();
2456}
2457
2458void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
2459{
2460 m_hasFocus = false;
120b9b05 2461
cab07038
RR
2462 if (HasCurrentRow())
2463 Refresh();
120b9b05 2464
4ed7af08
RR
2465 event.Skip();
2466}
2467
2468//-----------------------------------------------------------------------------
2469// wxDataViewCtrl
2470//-----------------------------------------------------------------------------
2471
2472IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
2473
4b3feaa7
RR
2474BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
2475 EVT_SIZE(wxDataViewCtrl::OnSize)
2476END_EVENT_TABLE()
2477
4ed7af08
RR
2478wxDataViewCtrl::~wxDataViewCtrl()
2479{
2480 if (m_notifier)
2481 GetModel()->RemoveNotifier( m_notifier );
2482}
2483
2484void wxDataViewCtrl::Init()
2485{
2486 m_notifier = NULL;
2487}
2488
2489bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
f554a14b 2490 const wxPoint& pos, const wxSize& size,
4ed7af08
RR
2491 long style, const wxValidator& validator )
2492{
c741d33f 2493 if (!wxControl::Create( parent, id, pos, size,
87f0efe2 2494 style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
4b3feaa7
RR
2495 return false;
2496
4ed7af08 2497 Init();
f554a14b 2498
4ed7af08
RR
2499#ifdef __WXMAC__
2500 MacSetClipChildren( true ) ;
2501#endif
2502
f554a14b 2503 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
9861f022
RR
2504
2505 if (HasFlag(wxDV_NO_HEADER))
2506 m_headerArea = NULL;
2507 else
2508 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY );
f554a14b 2509
4ed7af08 2510 SetTargetWindow( m_clientArea );
f554a14b 2511
4ed7af08 2512 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
9861f022
RR
2513 if (m_headerArea)
2514 sizer->Add( m_headerArea, 0, wxGROW );
4ed7af08
RR
2515 sizer->Add( m_clientArea, 1, wxGROW );
2516 SetSizer( sizer );
2517
2518 return true;
2519}
2520
2521#ifdef __WXMSW__
2522WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
2523 WXWPARAM wParam,
2524 WXLPARAM lParam)
2525{
b910a8ad 2526 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
4ed7af08
RR
2527
2528#ifndef __WXWINCE__
2529 // we need to process arrows ourselves for scrolling
2530 if ( nMsg == WM_GETDLGCODE )
2531 {
2532 rc |= DLGC_WANTARROWS;
2533 }
2534#endif
2535
2536 return rc;
2537}
2538#endif
2539
f554a14b 2540void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
4ed7af08 2541{
4b3feaa7
RR
2542 // We need to override OnSize so that our scrolled
2543 // window a) does call Layout() to use sizers for
2544 // positioning the controls but b) does not query
2545 // the sizer for their size and use that for setting
2546 // the scrollable area as set that ourselves by
2547 // calling SetScrollbar() further down.
2548
2549 Layout();
2550
2551 AdjustScrollbars();
4ed7af08
RR
2552}
2553
2554bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
2555{
2556 if (!wxDataViewCtrlBase::AssociateModel( model ))
2557 return false;
2558
a0f3af5f 2559 m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
4ed7af08 2560
f554a14b 2561 model->AddNotifier( m_notifier );
4ed7af08 2562
4b3feaa7 2563 m_clientArea->UpdateDisplay();
f554a14b 2564
4ed7af08
RR
2565 return true;
2566}
2567
2568bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
2569{
2570 if (!wxDataViewCtrlBase::AppendColumn(col))
2571 return false;
f554a14b 2572
9861f022 2573 OnColumnChange();
4ed7af08
RR
2574 return true;
2575}
2576
9861f022
RR
2577void wxDataViewCtrl::OnColumnChange()
2578{
2579 if (m_headerArea)
2580 m_headerArea->UpdateDisplay();
2581
2582 m_clientArea->UpdateDisplay();
2583}
2584
87f0efe2 2585void wxDataViewCtrl::SetSelection( int row )
6ff7eee7 2586{
87f0efe2 2587 m_clientArea->SelectRow(row, true);
6ff7eee7
RR
2588}
2589
87f0efe2 2590void wxDataViewCtrl::SetSelectionRange( unsigned int from, unsigned int to )
6ff7eee7 2591{
87f0efe2 2592 m_clientArea->SelectRows(from, to, true);
6ff7eee7
RR
2593}
2594
87f0efe2 2595void wxDataViewCtrl::SetSelections( const wxArrayInt& aSelections )
6ff7eee7 2596{
87f0efe2 2597 m_clientArea->Select(aSelections);
6ff7eee7 2598}
df387169
WS
2599
2600void wxDataViewCtrl::Unselect( unsigned int WXUNUSED(row) )
fc211fe5 2601{
df387169 2602 // FIXME - TODO
fc211fe5
RR
2603}
2604
df387169 2605bool wxDataViewCtrl::IsSelected( unsigned int WXUNUSED(row) ) const
6ff7eee7 2606{
df387169
WS
2607 // FIXME - TODO
2608
6ff7eee7
RR
2609 return false;
2610}
2611
2612int wxDataViewCtrl::GetSelection() const
2613{
df387169
WS
2614 // FIXME - TODO
2615
6ff7eee7
RR
2616 return -1;
2617}
2618
df387169 2619int wxDataViewCtrl::GetSelections(wxArrayInt& WXUNUSED(aSelections) ) const
6ff7eee7 2620{
df387169
WS
2621 // FIXME - TODO
2622
6ff7eee7
RR
2623 return 0;
2624}
2625
f554a14b 2626#endif
4ed7af08
RR
2627 // !wxUSE_GENERICDATAVIEWCTRL
2628
f554a14b 2629#endif
4ed7af08 2630 // wxUSE_DATAVIEWCTRL