]> git.saurik.com Git - wxWidgets.git/blame - src/generic/datavgen.cpp
made UTF-8 tables const
[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"
351461fc
RR
45#include "wx/list.h"
46#include "wx/listimpl.cpp"
4ed7af08 47
4ed7af08
RR
48//-----------------------------------------------------------------------------
49// classes
50//-----------------------------------------------------------------------------
51
52class wxDataViewCtrl;
53
9861f022
RR
54static const int SCROLL_UNIT_X = 15;
55
56// the cell padding on the left/right
57static const int PADDING_RIGHTLEFT = 3;
58
59// the cell padding on the top/bottom
60static const int PADDING_TOPBOTTOM = 1;
61
3b6280be
RR
62// the expander space margin
63static const int EXPANDER_MARGIN = 4;
9861f022 64
a0f3af5f
RR
65//-----------------------------------------------------------------------------
66// wxDataViewHeaderWindow
67//-----------------------------------------------------------------------------
4ed7af08 68
b7e9f8b1 69#define USE_NATIVE_HEADER_WINDOW 0
87f0efe2 70
c741d33f
VZ
71// NB: for some reason, this class must be dllexport'ed or we get warnings from
72// MSVC in DLL build
73class WXDLLIMPEXP_ADV wxDataViewHeaderWindowBase : public wxControl
87f0efe2
RR
74{
75public:
76 wxDataViewHeaderWindowBase()
77 { m_owner = NULL; }
78
79 bool Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f 80 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
81 const wxString &name)
82 {
83 return wxWindow::Create(parent, id, pos, size, wxNO_BORDER, name);
84 }
85
86 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
87 wxDataViewCtrl *GetOwner() { return m_owner; }
88
89 // called on column addition/removal
90 virtual void UpdateDisplay() { /* by default, do nothing */ }
91
87f0efe2 92 // returns the n-th column
9861f022 93 virtual wxDataViewColumn *GetColumn(unsigned int n)
87f0efe2
RR
94 {
95 wxASSERT(m_owner);
c741d33f 96 wxDataViewColumn *ret = m_owner->GetColumn(n);
87f0efe2
RR
97 wxASSERT(ret);
98
c741d33f 99 return ret;
87f0efe2
RR
100 }
101
102protected:
103 wxDataViewCtrl *m_owner;
104
105 // sends an event generated from the n-th wxDataViewColumn
106 void SendEvent(wxEventType type, unsigned int n);
107};
108
109// on wxMSW the header window (only that part however) can be made native!
110#if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
111
9861f022 112#define COLUMN_WIDTH_OFFSET 2
87f0efe2
RR
113#define wxDataViewHeaderWindowMSW wxDataViewHeaderWindow
114
115class wxDataViewHeaderWindowMSW : public wxDataViewHeaderWindowBase
4ed7af08
RR
116{
117public:
87f0efe2
RR
118
119 wxDataViewHeaderWindowMSW( wxDataViewCtrl *parent,
9861f022
RR
120 wxWindowID id,
121 const wxPoint &pos = wxDefaultPosition,
122 const wxSize &size = wxDefaultSize,
123 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
c741d33f 124 {
87f0efe2
RR
125 Create(parent, id, pos, size, name);
126 }
4ed7af08 127
87f0efe2 128 bool Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f 129 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
130 const wxString &name);
131
132 ~wxDataViewHeaderWindowMSW();
133
9861f022
RR
134 // called when any column setting is changed and/or changed
135 // the column count
87f0efe2
RR
136 virtual void UpdateDisplay();
137
138 // called when the main window gets scrolled
139 virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL);
140
141protected:
142 virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result);
9861f022
RR
143 virtual void DoSetSize(int x, int y, int width, int height, int sizeFlags);
144
145 unsigned int GetColumnIdxFromHeader(NMHEADER *nmHDR);
146
147 wxDataViewColumn *GetColumnFromHeader(NMHEADER *nmHDR)
148 { return GetColumn(GetColumnIdxFromHeader(nmHDR)); }
149
87f0efe2
RR
150private:
151 DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindowMSW)
152};
153
154#else // !defined(__WXMSW__)
155
156#define HEADER_WINDOW_HEIGHT 25
9861f022
RR
157#define HEADER_HORIZ_BORDER 5
158#define HEADER_VERT_BORDER 3
87f0efe2
RR
159#define wxGenericDataViewHeaderWindow wxDataViewHeaderWindow
160
161class wxGenericDataViewHeaderWindow : public wxDataViewHeaderWindowBase
162{
163public:
164 wxGenericDataViewHeaderWindow( wxDataViewCtrl *parent,
165 wxWindowID id,
166 const wxPoint &pos = wxDefaultPosition,
167 const wxSize &size = wxDefaultSize,
168 const wxString &name = wxT("wxdataviewctrlheaderwindow") )
c741d33f 169 {
87f0efe2
RR
170 Init();
171 Create(parent, id, pos, size, name);
172 }
173
174 bool Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f 175 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
176 const wxString &name);
177
178 ~wxGenericDataViewHeaderWindow()
179 {
180 delete m_resizeCursor;
181 }
182
c3112d56 183 virtual void UpdateDisplay() { Refresh(); }
442c56e6 184
87f0efe2 185 // event handlers:
4ed7af08 186
a0f3af5f
RR
187 void OnPaint( wxPaintEvent &event );
188 void OnMouse( wxMouseEvent &event );
189 void OnSetFocus( wxFocusEvent &event );
f554a14b 190
87f0efe2
RR
191
192protected:
193
194 // vars used for column resizing:
195
a0f3af5f 196 wxCursor *m_resizeCursor;
87f0efe2
RR
197 const wxCursor *m_currentCursor;
198 bool m_isDragging;
199
200 bool m_dirty; // needs refresh?
201 int m_column; // index of the column being resized
202 int m_currentX; // divider line position in logical (unscrolled) coords
c741d33f 203 int m_minX; // minimal position beyond which the divider line
87f0efe2
RR
204 // can't be dragged in logical coords
205
9861f022
RR
206 // the pen used to draw the current column width drag line
207 // when resizing the columsn
208 wxPen m_penCurrent;
209
210
87f0efe2
RR
211 // internal utilities:
212
213 void Init()
214 {
215 m_currentCursor = (wxCursor *) NULL;
216 m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
217
218 m_isDragging = false;
219 m_dirty = false;
220
221 m_column = wxNOT_FOUND;
222 m_currentX = 0;
223 m_minX = 0;
9861f022
RR
224
225 wxColour col = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
226 m_penCurrent = wxPen(col, 1, wxSOLID);
87f0efe2
RR
227 }
228
229 void DrawCurrent();
230 void AdjustDC(wxDC& dc);
f554a14b 231
a0f3af5f 232private:
87f0efe2 233 DECLARE_DYNAMIC_CLASS(wxGenericDataViewHeaderWindow)
a0f3af5f
RR
234 DECLARE_EVENT_TABLE()
235};
4ed7af08 236
87f0efe2
RR
237#endif // defined(__WXMSW__)
238
0fcce6b9
RR
239//-----------------------------------------------------------------------------
240// wxDataViewRenameTimer
241//-----------------------------------------------------------------------------
242
243class wxDataViewRenameTimer: public wxTimer
244{
245private:
246 wxDataViewMainWindow *m_owner;
247
248public:
249 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
250 void Notify();
251};
252
aba9bfd0
RR
253//-----------------------------------------------------------------------------
254// wxDataViewTreeNode
255//-----------------------------------------------------------------------------
442c56e6 256class wxDataViewTreeNode;
d47db7e0
RR
257WX_DEFINE_SORTED_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
258WX_DEFINE_SORTED_ARRAY( void* , wxDataViewTreeLeaves);
259
260int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode * node1, wxDataViewTreeNode * node2);
261int LINKAGEMODE wxGenericTreeModelItemCmp( void * id1, void * id2);
aba9bfd0
RR
262
263class wxDataViewTreeNode
264{
265public:
b7e9f8b1 266 wxDataViewTreeNode( wxDataViewTreeNode * parent = NULL )
d47db7e0
RR
267 :leaves( wxGenericTreeModelItemCmp ),
268 nodes(wxGenericTreeModelNodeCmp)
b7e9f8b1 269 { this->parent = parent;
aba9bfd0
RR
270 if( parent == NULL )
271 open = true;
b7e9f8b1
RR
272 else
273 open = false;
704c3490 274 hasChildren = false;
d47db7e0 275 subTreeCount = 0;
b7e9f8b1 276 }
aba9bfd0
RR
277 //I don't know what I need to do in the destructure
278 ~wxDataViewTreeNode()
d47db7e0 279 {
442c56e6 280
d47db7e0 281 }
aba9bfd0
RR
282
283 wxDataViewTreeNode * GetParent() { return parent; }
284 void SetParent( wxDataViewTreeNode * parent ) { this->parent = parent; }
d47db7e0
RR
285 wxDataViewTreeNodes & GetNodes() { return nodes; }
286 wxDataViewTreeLeaves & GetChildren() { return leaves; }
442c56e6
VZ
287
288 void AddNode( wxDataViewTreeNode * node )
d47db7e0
RR
289 {
290 nodes.Add( node );
291 leaves.Add( node->GetItem().GetID() );
292 }
293 void AddLeaf( void * leaf ) { leaves.Add( leaf ); }
aba9bfd0
RR
294
295 wxDataViewItem & GetItem() { return item; }
351461fc 296 void SetItem( const wxDataViewItem & item ) { this->item = item; }
aba9bfd0 297
d47db7e0
RR
298 unsigned int GetChildrenNumber() { return leaves.GetCount(); }
299 unsigned int GetNodeNumber() { return nodes.GetCount(); }
3b6280be
RR
300 int GetIndentLevel()
301 {
302 int ret = 0 ;
b7e9f8b1
RR
303 wxDataViewTreeNode * node = this;
304 while( node->GetParent()->GetParent() != NULL )
305 {
306 node = node->GetParent();
307 ret ++;
308 }
309 return ret;
3b6280be 310 }
aba9bfd0 311
442c56e6
VZ
312 bool IsOpen()
313 {
314 return open ;
315 }
d47db7e0
RR
316
317 void ToggleOpen()
442c56e6 318 {
d47db7e0
RR
319 int len = nodes.GetCount();
320 int sum = 0;
321 for ( int i = 0 ;i < len ; i ++)
322 sum += nodes[i]->GetSubTreeCount();
323
324 sum += leaves.GetCount();
325 if( open )
326 {
327 ChangeSubTreeCount(-sum);
328 open = !open;
329 }
330 else
331 {
332 open = !open;
333 ChangeSubTreeCount(sum);
334 }
335 }
704c3490
RR
336 bool HasChildren() { return hasChildren; }
337 void SetHasChildren( bool has ){ hasChildren = has; }
d47db7e0
RR
338
339 void SetSubTreeCount( int num ) { subTreeCount = num; }
340 int GetSubTreeCount() { return subTreeCount; }
442c56e6 341 void ChangeSubTreeCount( int num )
d47db7e0
RR
342 {
343 if( !open )
344 return ;
345 subTreeCount += num;
346 if( parent )
347 parent->ChangeSubTreeCount(num);
348 }
349
d3f00f59
VZ
350 void Resort()
351 {
352 wxDataViewTreeNodes nds = nodes;
353 wxDataViewTreeLeaves lvs = leaves;
354 nodes.Empty();
355 leaves.Empty();
a8505db0 356
d3f00f59
VZ
357 int len = nds.GetCount();
358 if(len > 0)
359 {
b7e9f8b1 360 int i;
a8505db0 361 for(i = 0; i < len; i ++)
d3f00f59 362 nodes.Add(nds[i]);
a8505db0 363 for(i = 0; i < len; i ++)
d3f00f59
VZ
364 nodes[i]->Resort();
365 }
366
367 len = lvs.GetCount();
368 if(len > 0)
369 {
370 for(int i = 0; i < len; i++)
371 leaves.Add(lvs[i]);
372 }
373 }
374
aba9bfd0
RR
375private:
376 wxDataViewTreeNode * parent;
d47db7e0
RR
377 wxDataViewTreeNodes nodes;
378 wxDataViewTreeLeaves leaves;
442c56e6 379 wxDataViewItem item;
aba9bfd0 380 bool open;
704c3490 381 bool hasChildren;
d47db7e0 382 int subTreeCount;
aba9bfd0
RR
383};
384
d47db7e0
RR
385//Below is the compare stuff
386//For the generic implements, both the leaf nodes and the nodes are sorted for fast search when needed
387static wxDataViewModel * g_model;
66e09788
RR
388static unsigned int g_column;
389static bool g_asending;
d47db7e0
RR
390
391int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode * node1, wxDataViewTreeNode * node2)
392{
66e09788 393 return g_model->Compare( node1->GetItem(), node2->GetItem(), g_column, g_asending );
d47db7e0
RR
394}
395
396int LINKAGEMODE wxGenericTreeModelItemCmp( void * id1, void * id2)
397{
66e09788 398 return g_model->Compare( id1, id2, g_column, g_asending );
d47db7e0
RR
399}
400
401
402
a0f3af5f
RR
403//-----------------------------------------------------------------------------
404// wxDataViewMainWindow
405//-----------------------------------------------------------------------------
4ed7af08 406
c741d33f 407WX_DEFINE_SORTED_USER_EXPORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection,
87f0efe2 408 WXDLLIMPEXP_ADV);
351461fc
RR
409WX_DECLARE_LIST(wxDataViewItem, ItemList);
410WX_DEFINE_LIST(ItemList);
cab07038 411
a0f3af5f 412class wxDataViewMainWindow: public wxWindow
4ed7af08 413{
a0f3af5f
RR
414public:
415 wxDataViewMainWindow( wxDataViewCtrl *parent,
416 wxWindowID id,
417 const wxPoint &pos = wxDefaultPosition,
418 const wxSize &size = wxDefaultSize,
419 const wxString &name = wxT("wxdataviewctrlmainwindow") );
d3c7fc99 420 virtual ~wxDataViewMainWindow();
4ed7af08 421
aba9bfd0 422 // notifications from wxDataViewModel
b7e9f8b1 423 void SendModelEvent( wxEventType type, const wxDataViewItem & item);
aba9bfd0 424 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
32143117 425 bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item );
aba9bfd0
RR
426 bool ItemChanged( const wxDataViewItem &item );
427 bool ValueChanged( const wxDataViewItem &item, unsigned int col );
a0f3af5f 428 bool Cleared();
d3f00f59 429 void Resort()
66e09788
RR
430 {
431 SortPrepare();
432 m_root->Resort();
433 UpdateDisplay();
434 }
4ed7af08 435
66e09788
RR
436 void SortPrepare()
437 {
438 g_model = GetOwner()->GetModel();
439 g_column = GetOwner()->GetSortingColumn();
440 wxDataViewColumn * col = GetOwner()->GetColumn(g_column);
441 if( !col )
442 {
443 g_asending = true;
444 return;
445 }
446 g_asending = col->IsSortOrderAscending();
447 }
448
a0f3af5f
RR
449 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
450 wxDataViewCtrl *GetOwner() { return m_owner; }
9861f022 451 const wxDataViewCtrl *GetOwner() const { return m_owner; }
4ed7af08 452
a0f3af5f 453 void OnPaint( wxPaintEvent &event );
0a71f9e9 454 void OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event);
cab07038 455 void OnChar( wxKeyEvent &event );
a0f3af5f
RR
456 void OnMouse( wxMouseEvent &event );
457 void OnSetFocus( wxFocusEvent &event );
cab07038 458 void OnKillFocus( wxFocusEvent &event );
f554a14b 459
a0f3af5f
RR
460 void UpdateDisplay();
461 void RecalculateDisplay();
462 void OnInternalIdle();
f554a14b 463
0fcce6b9 464 void OnRenameTimer();
0fcce6b9 465
9861f022 466 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
b7e9f8b1 467 void ScrollTo( int rows );
120b9b05 468
0a71f9e9
RR
469 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
470 void ChangeCurrentRow( unsigned int row );
120b9b05 471
47b378bd 472 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
cab07038 473 bool IsEmpty() { return GetRowCount() == 0; }
120b9b05 474
9861f022
RR
475 int GetCountPerPage() const;
476 int GetEndOfLastCol() const;
477 unsigned int GetFirstVisibleRow() const;
3b6280be
RR
478 //I change this method to un const because in the tree view, the displaying number of the tree are changing along with the expanding/collapsing of the tree nodes
479 unsigned int GetLastVisibleRow();
480 unsigned int GetRowCount() ;
120b9b05 481
704c3490 482 wxDataViewItem GetSelection();
b7e9f8b1
RR
483 wxDataViewSelection GetSelections(){ return m_selection; }
484 void SetSelections( const wxDataViewSelection & sel ) { m_selection = sel; UpdateDisplay(); }
87f0efe2 485 void Select( const wxArrayInt& aSelections );
cab07038 486 void SelectAllRows( bool on );
0a71f9e9
RR
487 void SelectRow( unsigned int row, bool on );
488 void SelectRows( unsigned int from, unsigned int to, bool on );
489 void ReverseRowSelection( unsigned int row );
490 bool IsRowSelected( unsigned int row );
120b9b05 491
0a71f9e9
RR
492 void RefreshRow( unsigned int row );
493 void RefreshRows( unsigned int from, unsigned int to );
494 void RefreshRowsAfter( unsigned int firstRow );
120b9b05 495
9861f022
RR
496 // returns the colour to be used for drawing the rules
497 wxColour GetRuleColour() const
498 {
499 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
500 }
501
502 //void EnsureVisible( unsigned int row );
503 wxRect GetLineRect( unsigned int row ) const;
504
aba9bfd0
RR
505 //Some useful functions for row and item mapping
506 wxDataViewItem GetItemByRow( unsigned int row );
b7e9f8b1 507 int GetRowByItem( const wxDataViewItem & item );
aba9bfd0
RR
508
509 //Methods for building the mapping tree
510 void BuildTree( wxDataViewModel * model );
511 void DestroyTree();
66e09788
RR
512 void HitTest( const wxPoint & point, wxDataViewItem & item, unsigned int & column );
513 wxRect GetItemRect( const wxDataViewItem & item, unsigned int column );
3b6280be
RR
514private:
515 wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row );
704c3490
RR
516 //We did not need this temporarily
517 //wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
3b6280be
RR
518
519 int RecalculateCount() ;
520
66e09788 521 wxDataViewEvent SendExpanderEvent( wxEventType type, const wxDataViewItem & item );
3b6280be
RR
522 void OnExpanding( unsigned int row );
523 void OnCollapsing( unsigned int row );
aba9bfd0 524
442c56e6 525 wxDataViewTreeNode * FindNode( const wxDataViewItem & item );
351461fc 526
a0f3af5f 527private:
0fcce6b9
RR
528 wxDataViewCtrl *m_owner;
529 int m_lineHeight;
530 bool m_dirty;
120b9b05 531
0fcce6b9 532 wxDataViewColumn *m_currentCol;
99d471a5 533 unsigned int m_currentRow;
cab07038 534 wxDataViewSelection m_selection;
120b9b05 535
0fcce6b9 536 wxDataViewRenameTimer *m_renameTimer;
0fcce6b9 537 bool m_lastOnSame;
f554a14b 538
cab07038
RR
539 bool m_hasFocus;
540
e21f75bd
RR
541 int m_dragCount;
542 wxPoint m_dragStart;
543
544 // for double click logic
0a71f9e9 545 unsigned int m_lineLastClicked,
e21f75bd
RR
546 m_lineBeforeLastClicked,
547 m_lineSelectSingleOnUp;
cab07038 548
9861f022
RR
549 // the pen used to draw horiz/vertical rules
550 wxPen m_penRule;
551
3b6280be
RR
552 // the pen used to draw the expander and the lines
553 wxPen m_penExpander;
554
aba9bfd0 555 //This is the tree structure of the model
442c56e6 556 wxDataViewTreeNode * m_root;
3b6280be 557 int m_count;
a0f3af5f
RR
558private:
559 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
560 DECLARE_EVENT_TABLE()
561};
4ed7af08 562
f554a14b 563// ---------------------------------------------------------
aba9bfd0 564// wxGenericDataViewModelNotifier
f554a14b 565// ---------------------------------------------------------
a0f3af5f 566
aba9bfd0 567class wxGenericDataViewModelNotifier: public wxDataViewModelNotifier
4ed7af08 568{
a0f3af5f 569public:
aba9bfd0 570 wxGenericDataViewModelNotifier( wxDataViewMainWindow *mainWindow )
a0f3af5f 571 { m_mainWindow = mainWindow; }
f554a14b 572
aba9bfd0
RR
573 virtual bool ItemAdded( const wxDataViewItem & parent, const wxDataViewItem & item )
574 { return m_mainWindow->ItemAdded( parent , item ); }
442c56e6 575 virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
071691a0 576 { return m_mainWindow->ItemDeleted( parent, item ); }
aba9bfd0
RR
577 virtual bool ItemChanged( const wxDataViewItem & item )
578 { return m_mainWindow->ItemChanged(item); }
579 virtual bool ValueChanged( const wxDataViewItem & item , unsigned int col )
580 { return m_mainWindow->ValueChanged( item, col ); }
a0f3af5f
RR
581 virtual bool Cleared()
582 { return m_mainWindow->Cleared(); }
d3f00f59 583 virtual void Resort()
b7e9f8b1 584 { m_mainWindow->Resort(); }
f554a14b 585
a0f3af5f
RR
586 wxDataViewMainWindow *m_mainWindow;
587};
4ed7af08 588
f554a14b 589// ---------------------------------------------------------
baa9ebc4 590// wxDataViewRenderer
f554a14b 591// ---------------------------------------------------------
4ed7af08 592
baa9ebc4 593IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
4ed7af08 594
c741d33f 595wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
9861f022
RR
596 wxDataViewCellMode mode,
597 int align) :
598 wxDataViewRendererBase( varianttype, mode, align )
4ed7af08 599{
3d9d7cc4 600 m_dc = NULL;
9861f022
RR
601 m_align = align;
602 m_mode = mode;
4ed7af08
RR
603}
604
baa9ebc4 605wxDataViewRenderer::~wxDataViewRenderer()
3d9d7cc4
RR
606{
607 if (m_dc)
608 delete m_dc;
609}
610
baa9ebc4 611wxDC *wxDataViewRenderer::GetDC()
3d9d7cc4
RR
612{
613 if (m_dc == NULL)
614 {
615 if (GetOwner() == NULL)
616 return NULL;
617 if (GetOwner()->GetOwner() == NULL)
618 return NULL;
619 m_dc = new wxClientDC( GetOwner()->GetOwner() );
620 }
f554a14b 621
3d9d7cc4
RR
622 return m_dc;
623}
624
f554a14b 625// ---------------------------------------------------------
baa9ebc4 626// wxDataViewCustomRenderer
f554a14b 627// ---------------------------------------------------------
3d9d7cc4 628
baa9ebc4 629IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
3d9d7cc4 630
baa9ebc4 631wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
9861f022
RR
632 wxDataViewCellMode mode, int align ) :
633 wxDataViewRenderer( varianttype, mode, align )
3d9d7cc4
RR
634{
635}
636
f554a14b 637// ---------------------------------------------------------
baa9ebc4 638// wxDataViewTextRenderer
f554a14b 639// ---------------------------------------------------------
4ed7af08 640
baa9ebc4 641IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewCustomRenderer)
4ed7af08 642
c741d33f 643wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
9861f022
RR
644 wxDataViewCellMode mode, int align ) :
645 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08
RR
646{
647}
648
baa9ebc4 649bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
4ed7af08 650{
90675b95 651 m_text = value.GetString();
f554a14b 652
90675b95 653 return true;
4ed7af08
RR
654}
655
9861f022 656bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
4ed7af08
RR
657{
658 return false;
659}
660
99d471a5 661bool wxDataViewTextRenderer::HasEditorCtrl()
442c56e6 662{
99d471a5
RR
663 return true;
664}
665
666wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
667 wxRect labelRect, const wxVariant &value )
668{
442c56e6 669 return new wxTextCtrl( parent, wxID_ANY, value,
99d471a5
RR
670 wxPoint(labelRect.x,labelRect.y),
671 wxSize(labelRect.width,labelRect.height) );
672}
673
674bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value )
675{
676 wxTextCtrl *text = (wxTextCtrl*) editor;
677 value = text->GetValue();
678 return true;
679}
680
87f0efe2 681bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state )
3d9d7cc4 682{
87f0efe2
RR
683 wxDataViewCtrl *view = GetOwner()->GetOwner();
684 wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ?
685 wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) :
686 view->GetForegroundColour();
687
688 dc->SetTextForeground(col);
90675b95
RR
689 dc->DrawText( m_text, cell.x, cell.y );
690
691 return true;
3d9d7cc4
RR
692}
693
9861f022 694wxSize wxDataViewTextRenderer::GetSize() const
3d9d7cc4 695{
9861f022 696 const wxDataViewCtrl *view = GetView();
87f0efe2
RR
697 if (!m_text.empty())
698 {
699 int x,y;
700 view->GetTextExtent( m_text, &x, &y );
701 return wxSize( x, y );
702 }
3d9d7cc4
RR
703 return wxSize(80,20);
704}
705
2586d4a1 706// ---------------------------------------------------------
baa9ebc4 707// wxDataViewBitmapRenderer
2586d4a1
RR
708// ---------------------------------------------------------
709
baa9ebc4 710IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewCustomRenderer)
2586d4a1 711
c741d33f 712wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
9861f022
RR
713 wxDataViewCellMode mode, int align ) :
714 wxDataViewCustomRenderer( varianttype, mode, align )
2586d4a1
RR
715{
716}
717
baa9ebc4 718bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
2586d4a1
RR
719{
720 if (value.GetType() == wxT("wxBitmap"))
721 m_bitmap << value;
722 if (value.GetType() == wxT("wxIcon"))
723 m_icon << value;
724
725 return true;
726}
727
9861f022 728bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
2586d4a1
RR
729{
730 return false;
731}
732
baa9ebc4 733bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
2586d4a1
RR
734{
735 if (m_bitmap.Ok())
736 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
737 else if (m_icon.Ok())
738 dc->DrawIcon( m_icon, cell.x, cell.y );
739
740 return true;
741}
742
9861f022 743wxSize wxDataViewBitmapRenderer::GetSize() const
2586d4a1
RR
744{
745 if (m_bitmap.Ok())
746 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
747 else if (m_icon.Ok())
748 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
749
750 return wxSize(16,16);
751}
752
f554a14b 753// ---------------------------------------------------------
baa9ebc4 754// wxDataViewToggleRenderer
f554a14b 755// ---------------------------------------------------------
4ed7af08 756
baa9ebc4 757IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewCustomRenderer)
4ed7af08 758
baa9ebc4 759wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
9861f022
RR
760 wxDataViewCellMode mode, int align ) :
761 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08 762{
90675b95 763 m_toggle = false;
4ed7af08
RR
764}
765
baa9ebc4 766bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
4ed7af08 767{
90675b95 768 m_toggle = value.GetBool();
f554a14b 769
a8461d31 770 return true;
4ed7af08
RR
771}
772
9861f022 773bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
4ed7af08
RR
774{
775 return false;
776}
f554a14b 777
baa9ebc4 778bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
4ed7af08 779{
90675b95 780 // User wxRenderer here
f554a14b 781
90675b95
RR
782 wxRect rect;
783 rect.x = cell.x + cell.width/2 - 10;
784 rect.width = 20;
785 rect.y = cell.y + cell.height/2 - 10;
786 rect.height = 20;
120b9b05 787
862d8041 788 int flags = 0;
90675b95 789 if (m_toggle)
862d8041
RR
790 flags |= wxCONTROL_CHECKED;
791 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
792 flags |= wxCONTROL_DISABLED;
793
90b903c2 794 wxRendererNative::Get().DrawCheckBox(
862d8041
RR
795 GetOwner()->GetOwner(),
796 *dc,
797 rect,
798 flags );
f554a14b 799
90675b95 800 return true;
4ed7af08
RR
801}
802
c741d33f 803bool wxDataViewToggleRenderer::Activate( wxRect WXUNUSED(cell),
aba9bfd0
RR
804 wxDataViewModel *model,
805 const wxDataViewItem & item, unsigned int col)
0fdc2321
RR
806{
807 bool value = !m_toggle;
808 wxVariant variant = value;
aba9bfd0
RR
809 model->SetValue( variant, item, col);
810 model->ValueChanged( item, col );
0fdc2321
RR
811 return true;
812}
813
9861f022 814wxSize wxDataViewToggleRenderer::GetSize() const
4ed7af08 815{
3d9d7cc4 816 return wxSize(20,20);
4ed7af08
RR
817}
818
f554a14b 819// ---------------------------------------------------------
baa9ebc4 820// wxDataViewProgressRenderer
f554a14b 821// ---------------------------------------------------------
4ed7af08 822
baa9ebc4 823IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
4ed7af08 824
baa9ebc4 825wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
9861f022
RR
826 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
827 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08
RR
828{
829 m_label = label;
830 m_value = 0;
831}
832
baa9ebc4 833wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
4ed7af08
RR
834{
835}
836
baa9ebc4 837bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
4ed7af08
RR
838{
839 m_value = (long) value;
f554a14b 840
4ed7af08
RR
841 if (m_value < 0) m_value = 0;
842 if (m_value > 100) m_value = 100;
f554a14b 843
4ed7af08
RR
844 return true;
845}
f554a14b 846
9861f022
RR
847bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
848{
849 value = (long) m_value;
850 return true;
851}
852
baa9ebc4 853bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
4ed7af08
RR
854{
855 double pct = (double)m_value / 100.0;
856 wxRect bar = cell;
857 bar.width = (int)(cell.width * pct);
858 dc->SetPen( *wxTRANSPARENT_PEN );
859 dc->SetBrush( *wxBLUE_BRUSH );
860 dc->DrawRectangle( bar );
861
862 dc->SetBrush( *wxTRANSPARENT_BRUSH );
863 dc->SetPen( *wxBLACK_PEN );
864 dc->DrawRectangle( cell );
f554a14b 865
4ed7af08
RR
866 return true;
867}
868
9861f022 869wxSize wxDataViewProgressRenderer::GetSize() const
4ed7af08
RR
870{
871 return wxSize(40,12);
872}
f554a14b
WS
873
874// ---------------------------------------------------------
baa9ebc4 875// wxDataViewDateRenderer
f554a14b 876// ---------------------------------------------------------
4ed7af08 877
21ead767
VZ
878#define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
879
880#if wxUSE_DATE_RENDERER_POPUP
8d0ca292 881
baa9ebc4 882class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
4ed7af08 883{
f554a14b 884public:
baa9ebc4 885 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
aba9bfd0
RR
886 wxDataViewModel *model, const wxDataViewItem & item, unsigned int col) :
887 wxPopupTransientWindow( parent, wxBORDER_SIMPLE ),
888 m_item( item )
4ed7af08
RR
889 {
890 m_model = model;
891 m_col = col;
f554a14b 892 m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
4ed7af08
RR
893 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
894 sizer->Add( m_cal, 1, wxGROW );
895 SetSizer( sizer );
896 sizer->Fit( this );
897 }
f554a14b 898
4ed7af08 899 void OnCalendar( wxCalendarEvent &event );
f554a14b 900
4ed7af08 901 wxCalendarCtrl *m_cal;
aba9bfd0 902 wxDataViewModel *m_model;
0a71f9e9 903 unsigned int m_col;
aba9bfd0 904 const wxDataViewItem & m_item;
f554a14b 905
a8461d31
PC
906protected:
907 virtual void OnDismiss()
908 {
909 }
910
4ed7af08
RR
911private:
912 DECLARE_EVENT_TABLE()
913};
914
baa9ebc4
RR
915BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
916 EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
4ed7af08
RR
917END_EVENT_TABLE()
918
baa9ebc4 919void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
4ed7af08
RR
920{
921 wxDateTime date = event.GetDate();
922 wxVariant value = date;
aba9bfd0
RR
923 m_model->SetValue( value, m_item, m_col );
924 m_model->ValueChanged( m_item, m_col );
4ed7af08
RR
925 DismissAndNotify();
926}
927
21ead767 928#endif // wxUSE_DATE_RENDERER_POPUP
8d0ca292 929
baa9ebc4 930IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
4ed7af08 931
baa9ebc4 932wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
9861f022
RR
933 wxDataViewCellMode mode, int align ) :
934 wxDataViewCustomRenderer( varianttype, mode, align )
4ed7af08
RR
935{
936}
f554a14b 937
baa9ebc4 938bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
4ed7af08
RR
939{
940 m_date = value.GetDateTime();
f554a14b 941
4ed7af08
RR
942 return true;
943}
944
9861f022
RR
945bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
946{
947 value = m_date;
948 return true;
949}
950
baa9ebc4 951bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
4ed7af08
RR
952{
953 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
954 wxString tmp = m_date.FormatDate();
955 dc->DrawText( tmp, cell.x, cell.y );
956
957 return true;
958}
959
9861f022 960wxSize wxDataViewDateRenderer::GetSize() const
4ed7af08 961{
9861f022 962 const wxDataViewCtrl* view = GetView();
4ed7af08
RR
963 wxString tmp = m_date.FormatDate();
964 wxCoord x,y,d;
965 view->GetTextExtent( tmp, &x, &y, &d );
966 return wxSize(x,y+d);
967}
968
aba9bfd0
RR
969bool wxDataViewDateRenderer::Activate( wxRect WXUNUSED(cell), wxDataViewModel *model,
970 const wxDataViewItem & item, unsigned int col )
4ed7af08
RR
971{
972 wxVariant variant;
aba9bfd0 973 model->GetValue( variant, item, col );
4ed7af08
RR
974 wxDateTime value = variant.GetDateTime();
975
21ead767 976#if wxUSE_DATE_RENDERER_POPUP
baa9ebc4 977 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
aba9bfd0 978 GetOwner()->GetOwner()->GetParent(), &value, model, item, col);
4ed7af08
RR
979 wxPoint pos = wxGetMousePosition();
980 popup->Move( pos );
981 popup->Layout();
982 popup->Popup( popup->m_cal );
21ead767 983#else // !wxUSE_DATE_RENDERER_POPUP
8d0ca292 984 wxMessageBox(value.Format());
21ead767 985#endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
4ed7af08
RR
986 return true;
987}
988
f554a14b 989// ---------------------------------------------------------
4ed7af08 990// wxDataViewColumn
f554a14b 991// ---------------------------------------------------------
4ed7af08
RR
992
993IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
994
c741d33f 995wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
87f0efe2
RR
996 unsigned int model_column,
997 int width, wxAlignment align, int flags ) :
998 wxDataViewColumnBase( title, cell, model_column, width, align, flags )
4ed7af08 999{
9861f022
RR
1000 SetAlignment(align);
1001 SetTitle(title);
1002 SetFlags(flags);
1003
1004 Init(width < 0 ? wxDVC_DEFAULT_WIDTH : width);
4ed7af08
RR
1005}
1006
c741d33f 1007wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
87f0efe2
RR
1008 unsigned int model_column,
1009 int width, wxAlignment align, int flags ) :
1010 wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags )
07a84e7b 1011{
9861f022
RR
1012 SetAlignment(align);
1013 SetFlags(flags);
1014
1015 Init(width < 0 ? wxDVC_TOGGLE_DEFAULT_WIDTH : width);
07a84e7b
RR
1016}
1017
9861f022
RR
1018wxDataViewColumn::~wxDataViewColumn()
1019{
1020}
1021
1022void wxDataViewColumn::Init( int width )
47cef10f 1023{
87f0efe2 1024 m_width = width;
9861f022 1025 m_minWidth = wxDVC_DEFAULT_MINWIDTH;
c3112d56 1026 m_ascending = true;
47cef10f
RR
1027}
1028
9861f022 1029void wxDataViewColumn::SetResizeable( bool resizeable )
31fb32e1 1030{
9861f022
RR
1031 if (resizeable)
1032 m_flags |= wxDATAVIEW_COL_RESIZABLE;
1033 else
1034 m_flags &= ~wxDATAVIEW_COL_RESIZABLE;
1035}
1036
1037void wxDataViewColumn::SetHidden( bool hidden )
1038{
1039 if (hidden)
1040 m_flags |= wxDATAVIEW_COL_HIDDEN;
1041 else
1042 m_flags &= ~wxDATAVIEW_COL_HIDDEN;
1043
1044 // tell our owner to e.g. update its scrollbars:
1045 if (GetOwner())
1046 GetOwner()->OnColumnChange();
1047}
1048
1049void wxDataViewColumn::SetSortable( bool sortable )
1050{
1051 if (sortable)
1052 m_flags |= wxDATAVIEW_COL_SORTABLE;
1053 else
1054 m_flags &= ~wxDATAVIEW_COL_SORTABLE;
442c56e6 1055
c3112d56
RR
1056 // Update header button
1057 if (GetOwner())
1058 GetOwner()->OnColumnChange();
31fb32e1
RR
1059}
1060
c3112d56 1061void wxDataViewColumn::SetSortOrder( bool ascending )
47cef10f 1062{
c3112d56 1063 m_ascending = ascending;
442c56e6 1064
c3112d56
RR
1065 // Update header button
1066 if (GetOwner())
1067 GetOwner()->OnColumnChange();
47cef10f
RR
1068}
1069
87f0efe2 1070bool wxDataViewColumn::IsSortOrderAscending() const
31fb32e1 1071{
c3112d56 1072 return m_ascending;
31fb32e1
RR
1073}
1074
9861f022 1075void wxDataViewColumn::SetInternalWidth( int width )
4ed7af08 1076{
9861f022 1077 m_width = width;
f554a14b 1078
9861f022
RR
1079 // the scrollbars of the wxDataViewCtrl needs to be recalculated!
1080 if (m_owner && m_owner->m_clientArea)
1081 m_owner->m_clientArea->RecalculateDisplay();
4ed7af08
RR
1082}
1083
9861f022 1084void wxDataViewColumn::SetWidth( int width )
47cef10f 1085{
9861f022 1086 m_owner->m_headerArea->UpdateDisplay();
47cef10f 1087
9861f022 1088 SetInternalWidth(width);
47cef10f
RR
1089}
1090
533544f2 1091
4ed7af08 1092//-----------------------------------------------------------------------------
87f0efe2 1093// wxDataViewHeaderWindowBase
4ed7af08
RR
1094//-----------------------------------------------------------------------------
1095
87f0efe2
RR
1096void wxDataViewHeaderWindowBase::SendEvent(wxEventType type, unsigned int n)
1097{
1098 wxWindow *parent = GetParent();
1099 wxDataViewEvent le(type, parent->GetId());
1100
1101 le.SetEventObject(parent);
1102 le.SetColumn(n);
1103 le.SetDataViewColumn(GetColumn(n));
1104 le.SetModel(GetOwner()->GetModel());
1105
1106 // for events created by wxDataViewHeaderWindow the
1107 // row / value fields are not valid
1108
1109 parent->GetEventHandler()->ProcessEvent(le);
1110}
1111
1112#if defined(__WXMSW__) && USE_NATIVE_HEADER_WINDOW
1113
87f0efe2 1114// implemented in msw/listctrl.cpp:
a68962fc 1115int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
87f0efe2 1116
5d3f234b 1117IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindowMSW, wxWindow)
87f0efe2
RR
1118
1119bool wxDataViewHeaderWindowMSW::Create( wxDataViewCtrl *parent, wxWindowID id,
c741d33f 1120 const wxPoint &pos, const wxSize &size,
87f0efe2
RR
1121 const wxString &name )
1122{
1123 m_owner = parent;
1124
1125 if ( !CreateControl(parent, id, pos, size, 0, wxDefaultValidator, name) )
1126 return false;
1127
1128 int x = pos.x == wxDefaultCoord ? 0 : pos.x,
1129 y = pos.y == wxDefaultCoord ? 0 : pos.y,
1130 w = size.x == wxDefaultCoord ? 1 : size.x,
1131 h = size.y == wxDefaultCoord ? 22 : size.y;
1132
1133 // create the native WC_HEADER window:
1134 WXHWND hwndParent = (HWND)parent->GetHandle();
1135 WXDWORD msStyle = WS_CHILD | HDS_BUTTONS | HDS_HORZ | HDS_HOTTRACK | HDS_FULLDRAG;
c741d33f
VZ
1136 m_hWnd = CreateWindowEx(0,
1137 WC_HEADER,
1138 (LPCTSTR) NULL,
87f0efe2
RR
1139 msStyle,
1140 x, y, w, h,
c741d33f
VZ
1141 (HWND)hwndParent,
1142 (HMENU)-1,
1143 wxGetInstance(),
87f0efe2 1144 (LPVOID) NULL);
c741d33f 1145 if (m_hWnd == NULL)
87f0efe2
RR
1146 {
1147 wxLogLastError(_T("CreateWindowEx"));
1148 return false;
1149 }
1150
5d3f234b 1151 // we need to subclass the m_hWnd to force wxWindow::HandleNotify
87f0efe2 1152 // to call wxDataViewHeaderWindow::MSWOnNotify
5d3f234b 1153 SubclassWin(m_hWnd);
87f0efe2 1154
c741d33f 1155 // the following is required to get the default win's font for
9861f022
RR
1156 // header windows and must be done befor sending the HDM_LAYOUT msg
1157 SetFont(GetFont());
87f0efe2 1158
9861f022
RR
1159 RECT rcParent;
1160 HDLAYOUT hdl;
1161 WINDOWPOS wp;
87f0efe2 1162
c741d33f
VZ
1163 // Retrieve the bounding rectangle of the parent window's
1164 // client area, and then request size and position values
1165 // from the header control.
1166 ::GetClientRect((HWND)hwndParent, &rcParent);
1167
1168 hdl.prc = &rcParent;
1169 hdl.pwpos = &wp;
1170 if (!SendMessage((HWND)m_hWnd, HDM_LAYOUT, 0, (LPARAM) &hdl))
87f0efe2
RR
1171 {
1172 wxLogLastError(_T("SendMessage"));
1173 return false;
1174 }
c741d33f
VZ
1175
1176 // Set the size, position, and visibility of the header control.
1177 SetWindowPos((HWND)m_hWnd,
1178 wp.hwndInsertAfter,
1179 wp.x, wp.y,
1180 wp.cx, wp.cy,
87f0efe2
RR
1181 wp.flags | SWP_SHOWWINDOW);
1182
1183 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1184 // a wxBoxSizer and in order to avoid super-big header windows,
1185 // we need to set our height as fixed
1186 SetMinSize(wxSize(-1, wp.cy));
1187 SetMaxSize(wxSize(-1, wp.cy));
1188
87f0efe2
RR
1189 return true;
1190}
1191
1192wxDataViewHeaderWindowMSW::~wxDataViewHeaderWindow()
1193{
5d3f234b 1194 UnsubclassWin();
87f0efe2
RR
1195}
1196
1197void wxDataViewHeaderWindowMSW::UpdateDisplay()
1198{
1199 // remove old columns
914e6945 1200 for (int j=0, max=Header_GetItemCount((HWND)m_hWnd); j < max; j++)
87f0efe2 1201 Header_DeleteItem((HWND)m_hWnd, 0);
c741d33f 1202
87f0efe2 1203 // add the updated array of columns to the header control
9861f022
RR
1204 unsigned int cols = GetOwner()->GetColumnCount();
1205 unsigned int added = 0;
d3f00f59 1206 wxDataViewModel * model = GetOwner()->GetModel();
87f0efe2
RR
1207 for (unsigned int i = 0; i < cols; i++)
1208 {
1209 wxDataViewColumn *col = GetColumn( i );
1210 if (col->IsHidden())
1211 continue; // don't add it!
1212
1213 HDITEM hdi;
1214 hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
fab3f50e 1215 hdi.pszText = (wxChar *) col->GetTitle().wx_str();
87f0efe2
RR
1216 hdi.cxy = col->GetWidth();
1217 hdi.cchTextMax = sizeof(hdi.pszText)/sizeof(hdi.pszText[0]);
1218 hdi.fmt = HDF_LEFT | HDF_STRING;
b7e9f8b1
RR
1219 //hdi.fmt &= ~(HDF_SORTDOWN|HDF_SORTUP);
1220
1221 //sorting support
66e09788 1222 if(model && m_owner->GetSortingColumn() == i)
b7e9f8b1
RR
1223 {
1224 //The Microsoft Comctrl32.dll 6.0 support SORTUP/SORTDOWN, but they are not default
1225 //see http://msdn2.microsoft.com/en-us/library/ms649534.aspx for more detail
1226 //hdi.fmt |= model->GetSortOrderAscending()? HDF_SORTUP:HDF_SORTDOWN;
1227 ;
1228 }
9861f022
RR
1229
1230 // lParam is reserved for application's use:
1231 // we store there the column index to use it later in MSWOnNotify
1232 // (since columns may have been hidden)
1233 hdi.lParam = (LPARAM)i;
1234
1235 // the native wxMSW implementation of the header window
c741d33f 1236 // draws the column separator COLUMN_WIDTH_OFFSET pixels
9861f022
RR
1237 // on the right: to correct this effect we make the column
1238 // exactly COLUMN_WIDTH_OFFSET wider (for the first column):
1239 if (i == 0)
1240 hdi.cxy += COLUMN_WIDTH_OFFSET;
1241
1242 switch (col->GetAlignment())
1243 {
1244 case wxALIGN_LEFT:
1245 hdi.fmt |= HDF_LEFT;
1246 break;
1247 case wxALIGN_CENTER:
1248 case wxALIGN_CENTER_HORIZONTAL:
1249 hdi.fmt |= HDF_CENTER;
1250 break;
1251 case wxALIGN_RIGHT:
1252 hdi.fmt |= HDF_RIGHT;
1253 break;
5d3f234b
RR
1254
1255 default:
1256 // such alignment is not allowed for the column header!
1257 wxFAIL;
9861f022 1258 }
c741d33f
VZ
1259
1260 SendMessage((HWND)m_hWnd, HDM_INSERTITEM,
1261 (WPARAM)added, (LPARAM)&hdi);
9861f022 1262 added++;
87f0efe2
RR
1263 }
1264}
1265
9861f022
RR
1266unsigned int wxDataViewHeaderWindowMSW::GetColumnIdxFromHeader(NMHEADER *nmHDR)
1267{
1268 unsigned int idx;
1269
1270 // NOTE: we don't just return nmHDR->iItem because when there are
1271 // hidden columns, nmHDR->iItem may be different from
1272 // nmHDR->pitem->lParam
1273
1274 if (nmHDR->pitem && nmHDR->pitem->mask & HDI_LPARAM)
1275 {
1276 idx = (unsigned int)nmHDR->pitem->lParam;
1277 return idx;
1278 }
1279
1280 HDITEM item;
1281 item.mask = HDI_LPARAM;
1282 Header_GetItem((HWND)m_hWnd, nmHDR->iItem, &item);
1283
1284 return (unsigned int)item.lParam;
1285}
1286
87f0efe2
RR
1287bool wxDataViewHeaderWindowMSW::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1288{
1289 NMHDR *nmhdr = (NMHDR *)lParam;
1290
1291 // is it a message from the header?
1292 if ( nmhdr->hwndFrom != (HWND)m_hWnd )
1293 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1294
1295 NMHEADER *nmHDR = (NMHEADER *)nmhdr;
1296 switch ( nmhdr->code )
1297 {
1298 case HDN_BEGINTRACK:
1299 // user has started to resize a column:
1300 // do we need to veto it?
1301 if (!GetColumn(nmHDR->iItem)->IsResizeable())
1302 {
1303 // veto it!
1304 *result = TRUE;
1305 }
1306 break;
1307
1308 case HDN_BEGINDRAG:
1309 // user has started to reorder a column
1310 break;
1311
9861f022 1312 case HDN_ITEMCHANGING:
c741d33f 1313 if (nmHDR->pitem != NULL &&
9861f022
RR
1314 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1315 {
1316 int minWidth = GetColumnFromHeader(nmHDR)->GetMinWidth();
1317 if (nmHDR->pitem->cxy < minWidth)
1318 {
c741d33f 1319 // do not allow the user to resize this column under
9861f022
RR
1320 // its minimal width:
1321 *result = TRUE;
1322 }
1323 }
1324 break;
1325
87f0efe2
RR
1326 case HDN_ITEMCHANGED: // user is resizing a column
1327 case HDN_ENDTRACK: // user has finished resizing a column
1328 case HDN_ENDDRAG: // user has finished reordering a column
1329
1330 // update the width of the modified column:
c741d33f 1331 if (nmHDR->pitem != NULL &&
9861f022
RR
1332 (nmHDR->pitem->mask & HDI_WIDTH) != 0)
1333 {
1334 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
1335 unsigned int w = nmHDR->pitem->cxy;
1336 wxDataViewColumn *col = GetColumn(idx);
1337
1338 // see UpdateDisplay() for more info about COLUMN_WIDTH_OFFSET
1339 if (idx == 0 && w > COLUMN_WIDTH_OFFSET)
1340 w -= COLUMN_WIDTH_OFFSET;
1341
1342 if (w >= (unsigned)col->GetMinWidth())
1343 col->SetInternalWidth(w);
1344 }
87f0efe2
RR
1345 break;
1346
1347 case HDN_ITEMCLICK:
1348 {
9861f022 1349 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
d3f00f59
VZ
1350 wxDataViewModel * model = GetOwner()->GetModel();
1351
1352 if(nmHDR->iButton == 0)
1353 {
1354 wxDataViewColumn *col = GetColumn(idx);
1355 if(col->IsSortable())
1356 {
66e09788 1357 if(model && m_owner->GetSortingColumn() == idx)
d3f00f59
VZ
1358 {
1359 bool order = col->IsSortOrderAscending();
1360 col->SetSortOrder(!order);
d3f00f59
VZ
1361 }
1362 else if(model)
1363 {
66e09788 1364 m_owner->SetSortingColumn(idx);
d3f00f59
VZ
1365 }
1366 }
1367 UpdateDisplay();
1368 if(model)
1369 model->Resort();
1370 }
1371
c741d33f 1372 wxEventType evt = nmHDR->iButton == 0 ?
87f0efe2
RR
1373 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1374 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
9861f022 1375 SendEvent(evt, idx);
87f0efe2
RR
1376 }
1377 break;
1378
1379 case NM_RCLICK:
1380 {
1381 // NOTE: for some reason (i.e. for a bug in Windows)
1382 // the HDN_ITEMCLICK notification is not sent on
1383 // right clicks, so we need to handle NM_RCLICK
1384
1385 POINT ptClick;
9861f022 1386 int column = wxMSWGetColumnClicked(nmhdr, &ptClick);
87f0efe2 1387 if (column != wxNOT_FOUND)
9861f022
RR
1388 {
1389 HDITEM item;
1390 item.mask = HDI_LPARAM;
1391 Header_GetItem((HWND)m_hWnd, column, &item);
1392
1393 // 'idx' may be different from 'column' if there are
1394 // hidden columns...
1395 unsigned int idx = (unsigned int)item.lParam;
87f0efe2 1396 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
9861f022
RR
1397 idx);
1398 }
87f0efe2
RR
1399 }
1400 break;
1401
1402 case HDN_GETDISPINFOW:
1403 // see wxListCtrl::MSWOnNotify for more info!
1404 break;
1405
1406 case HDN_ITEMDBLCLICK:
1407 {
9861f022 1408 unsigned int idx = GetColumnIdxFromHeader(nmHDR);
87f0efe2
RR
1409 int w = GetOwner()->GetBestColumnWidth(idx);
1410
1411 // update the native control:
1412 HDITEM hd;
1413 ZeroMemory(&hd, sizeof(hd));
1414 hd.mask = HDI_WIDTH;
1415 hd.cxy = w;
c741d33f 1416 Header_SetItem(GetHwnd(),
9861f022
RR
1417 nmHDR->iItem, // NOTE: we don't want 'idx' here!
1418 &hd);
87f0efe2
RR
1419
1420 // update the wxDataViewColumn class:
9861f022 1421 GetColumn(idx)->SetInternalWidth(w);
87f0efe2
RR
1422 }
1423 break;
1424
1425 default:
1426 return wxWindow::MSWOnNotify(idCtrl, lParam, result);
1427 }
1428
1429 return true;
1430}
1431
c741d33f 1432void wxDataViewHeaderWindowMSW::ScrollWindow(int WXUNUSED(dx), int WXUNUSED(dy),
87f0efe2
RR
1433 const wxRect *WXUNUSED(rect))
1434{
1435 wxSize ourSz = GetClientSize();
1436 wxSize ownerSz = m_owner->GetClientSize();
1437
1438 // where should the (logical) origin of this window be placed?
1439 int x1 = 0, y1 = 0;
1440 m_owner->CalcUnscrolledPosition(0, 0, &x1, &y1);
4ed7af08 1441
c741d33f
VZ
1442 // put this window on top of our parent and
1443 SetWindowPos((HWND)m_hWnd, HWND_TOP, -x1, 0,
1444 ownerSz.GetWidth() + x1, ourSz.GetHeight(),
87f0efe2
RR
1445 SWP_SHOWWINDOW);
1446}
1447
c741d33f
VZ
1448void wxDataViewHeaderWindowMSW::DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
1449 int WXUNUSED(w), int WXUNUSED(h),
9861f022
RR
1450 int WXUNUSED(f))
1451{
1452 // the wxDataViewCtrl's internal wxBoxSizer will call this function when
1453 // the wxDataViewCtrl window gets resized: the following dummy call
1454 // to ScrollWindow() is required in order to get this header window
1455 // correctly repainted when it's (horizontally) scrolled:
1456
1457 ScrollWindow(0, 0);
1458}
1459
87f0efe2
RR
1460#else // !defined(__WXMSW__)
1461
1462IMPLEMENT_ABSTRACT_CLASS(wxGenericDataViewHeaderWindow, wxWindow)
1463BEGIN_EVENT_TABLE(wxGenericDataViewHeaderWindow, wxWindow)
1464 EVT_PAINT (wxGenericDataViewHeaderWindow::OnPaint)
1465 EVT_MOUSE_EVENTS (wxGenericDataViewHeaderWindow::OnMouse)
1466 EVT_SET_FOCUS (wxGenericDataViewHeaderWindow::OnSetFocus)
4ed7af08
RR
1467END_EVENT_TABLE()
1468
87f0efe2 1469bool wxGenericDataViewHeaderWindow::Create(wxDataViewCtrl *parent, wxWindowID id,
c741d33f
VZ
1470 const wxPoint &pos, const wxSize &size,
1471 const wxString &name )
4ed7af08 1472{
87f0efe2 1473 m_owner = parent;
4b3feaa7 1474
87f0efe2
RR
1475 if (!wxDataViewHeaderWindowBase::Create(parent, id, pos, size, name))
1476 return false;
f554a14b 1477
4ed7af08 1478 wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
2e992e06 1479 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
4ed7af08
RR
1480 SetOwnForegroundColour( attr.colFg );
1481 SetOwnBackgroundColour( attr.colBg );
1482 if (!m_hasFont)
1483 SetOwnFont( attr.font );
4ed7af08 1484
87f0efe2
RR
1485 // set our size hints: wxDataViewCtrl will put this wxWindow inside
1486 // a wxBoxSizer and in order to avoid super-big header windows,
1487 // we need to set our height as fixed
1488 SetMinSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1489 SetMaxSize(wxSize(-1, HEADER_WINDOW_HEIGHT));
1490
1491 return true;
4ed7af08
RR
1492}
1493
87f0efe2 1494void wxGenericDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
4ed7af08 1495{
4b3feaa7
RR
1496 int w, h;
1497 GetClientSize( &w, &h );
1498
2e992e06
VZ
1499 wxAutoBufferedPaintDC dc( this );
1500
1501 dc.SetBackground(GetBackgroundColour());
1502 dc.Clear();
f554a14b 1503
4ed7af08
RR
1504 int xpix;
1505 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1506
1507 int x;
1508 m_owner->GetViewStart( &x, NULL );
1509
1510 // account for the horz scrollbar offset
1511 dc.SetDeviceOrigin( -x * xpix, 0 );
f554a14b 1512
4ed7af08 1513 dc.SetFont( GetFont() );
f554a14b 1514
9861f022 1515 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 1516 unsigned int i;
4b3feaa7
RR
1517 int xpos = 0;
1518 for (i = 0; i < cols; i++)
1519 {
87f0efe2
RR
1520 wxDataViewColumn *col = GetColumn( i );
1521 if (col->IsHidden())
9861f022 1522 continue; // skip it!
f554a14b 1523
87f0efe2 1524 int cw = col->GetWidth();
4b3feaa7 1525 int ch = h;
4b3feaa7 1526
c3112d56 1527 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE;
66e09788 1528 if (col->IsSortable() && GetOwner()->GetSortingColumn() == i)
c3112d56
RR
1529 {
1530 if (col->IsSortOrderAscending())
1531 sortArrow = wxHDR_SORT_ICON_UP;
1532 else
1533 sortArrow = wxHDR_SORT_ICON_DOWN;
1534 }
1535
4b3feaa7
RR
1536 wxRendererNative::Get().DrawHeaderButton
1537 (
1538 this,
1539 dc,
72664514 1540 wxRect(xpos, 0, cw, ch-1),
4b3feaa7 1541 m_parent->IsEnabled() ? 0
c3112d56
RR
1542 : (int)wxCONTROL_DISABLED,
1543 sortArrow
4b3feaa7
RR
1544 );
1545
9861f022
RR
1546 // align as required the column title:
1547 int x = xpos;
1548 wxSize titleSz = dc.GetTextExtent(col->GetTitle());
1549 switch (col->GetAlignment())
1550 {
1551 case wxALIGN_LEFT:
1552 x += HEADER_HORIZ_BORDER;
1553 break;
1554 case wxALIGN_CENTER:
1555 case wxALIGN_CENTER_HORIZONTAL:
1556 x += (cw - titleSz.GetWidth() - 2 * HEADER_HORIZ_BORDER)/2;
1557 break;
1558 case wxALIGN_RIGHT:
1559 x += cw - titleSz.GetWidth() - HEADER_HORIZ_BORDER;
1560 break;
1561 }
1562
1563 // always center the title vertically:
1564 int y = wxMax((ch - titleSz.GetHeight()) / 2, HEADER_VERT_BORDER);
1565
c741d33f 1566 dc.SetClippingRegion( xpos+HEADER_HORIZ_BORDER,
9861f022
RR
1567 HEADER_VERT_BORDER,
1568 wxMax(cw - 2 * HEADER_HORIZ_BORDER, 1), // width
1569 wxMax(ch - 2 * HEADER_VERT_BORDER, 1)); // height
1570 dc.DrawText( col->GetTitle(), x, y );
1571 dc.DestroyClippingRegion();
f554a14b 1572
87f0efe2 1573 xpos += cw;
4b3feaa7 1574 }
4ed7af08
RR
1575}
1576
87f0efe2 1577void wxGenericDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
4ed7af08 1578{
87f0efe2
RR
1579 GetParent()->SetFocus();
1580 event.Skip();
4ed7af08
RR
1581}
1582
87f0efe2 1583void wxGenericDataViewHeaderWindow::OnMouse( wxMouseEvent &event )
4ed7af08 1584{
87f0efe2
RR
1585 // we want to work with logical coords
1586 int x;
1587 m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
1588 int y = event.GetY();
1589
1590 if (m_isDragging)
1591 {
c741d33f 1592 // we don't draw the line beyond our window,
87f0efe2
RR
1593 // but we allow dragging it there
1594 int w = 0;
1595 GetClientSize( &w, NULL );
1596 m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
1597 w -= 6;
1598
1599 // erase the line if it was drawn
c741d33f 1600 if (m_currentX < w)
87f0efe2
RR
1601 DrawCurrent();
1602
c741d33f 1603 if (event.ButtonUp())
87f0efe2
RR
1604 {
1605 m_isDragging = false;
c741d33f 1606 if (HasCapture())
87f0efe2
RR
1607 ReleaseMouse();
1608
1609 m_dirty = true;
1610
9861f022 1611 GetColumn(m_column)->SetWidth(m_currentX - m_minX);
87f0efe2
RR
1612
1613 Refresh();
1614 GetOwner()->Refresh();
1615 }
1616 else
1617 {
1618 m_currentX = wxMax(m_minX + 7, x);
1619
1620 // draw in the new location
1621 if (m_currentX < w) DrawCurrent();
1622 }
1623
1624 }
1625 else // not dragging
1626 {
1627 m_minX = 0;
1628 m_column = wxNOT_FOUND;
1629
1630 bool hit_border = false;
1631
1632 // end of the current column
1633 int xpos = 0;
1634
1635 // find the column where this event occured
9861f022 1636 int countCol = m_owner->GetColumnCount();
c741d33f 1637 for (int column = 0; column < countCol; column++)
87f0efe2
RR
1638 {
1639 wxDataViewColumn *p = GetColumn(column);
1640
c741d33f 1641 if (p->IsHidden())
87f0efe2
RR
1642 continue; // skip if not shown
1643
1644 xpos += p->GetWidth();
1645 m_column = column;
c741d33f 1646 if ((abs(x-xpos) < 3) && (y < 22))
87f0efe2
RR
1647 {
1648 hit_border = true;
1649 break;
1650 }
1651
c741d33f 1652 if (x < xpos)
87f0efe2
RR
1653 {
1654 // inside the column
1655 break;
1656 }
1657
1658 m_minX = xpos;
1659 }
1660
1661 if (m_column == wxNOT_FOUND)
1662 return;
1663
1664 bool resizeable = GetColumn(m_column)->IsResizeable();
1665 if (event.LeftDClick() && resizeable)
1666 {
9861f022 1667 GetColumn(m_column)->SetWidth(GetOwner()->GetBestColumnWidth(m_column));
87f0efe2
RR
1668 Refresh();
1669 }
1670 else if (event.LeftDown() || event.RightUp())
1671 {
c741d33f 1672 if (hit_border && event.LeftDown() && resizeable)
87f0efe2
RR
1673 {
1674 m_isDragging = true;
1675 CaptureMouse();
1676 m_currentX = x;
1677 DrawCurrent();
1678 }
1679 else // click on a column
1680 {
b7e9f8b1 1681 wxDataViewModel * model = GetOwner()->GetModel();
c741d33f 1682 wxEventType evt = event.LeftDown() ?
87f0efe2
RR
1683 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK :
1684 wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK;
1685 SendEvent(evt, m_column);
b7e9f8b1
RR
1686
1687 //Left click the header
1688 if(event.LeftDown())
1689 {
1690 wxDataViewColumn *col = GetColumn(m_column);
1691 if(col->IsSortable())
1692 {
66e09788 1693 unsigned int colnum = m_owner->GetSortingColumn();
b7e9f8b1
RR
1694 if(model && static_cast<int>(colnum) == m_column)
1695 {
1696 bool order = col->IsSortOrderAscending();
1697 col->SetSortOrder(!order);
b7e9f8b1
RR
1698 }
1699 else if(model)
1700 {
66e09788 1701 m_owner->SetSortingColumn(m_column);
b7e9f8b1
RR
1702 }
1703 }
1704 UpdateDisplay();
1705 if(model)
1706 model->Resort();
1707 //Send the column sorted event
1708 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, m_column);
1709 }
87f0efe2
RR
1710 }
1711 }
1712 else if (event.Moving())
1713 {
c741d33f 1714 if (hit_border && resizeable)
87f0efe2
RR
1715 m_currentCursor = m_resizeCursor;
1716 else
1717 m_currentCursor = wxSTANDARD_CURSOR;
1718
1719 SetCursor(*m_currentCursor);
1720 }
1721 }
1722}
1723
1724void wxGenericDataViewHeaderWindow::DrawCurrent()
1725{
1726 int x1 = m_currentX;
1727 int y1 = 0;
1728 ClientToScreen (&x1, &y1);
1729
1730 int x2 = m_currentX-1;
1731#ifdef __WXMSW__
1732 ++x2; // but why ????
1733#endif
1734 int y2 = 0;
1735 m_owner->GetClientSize( NULL, &y2 );
1736 m_owner->ClientToScreen( &x2, &y2 );
1737
1738 wxScreenDC dc;
9861f022
RR
1739 dc.SetLogicalFunction(wxINVERT);
1740 dc.SetPen(m_penCurrent);
1741 dc.SetBrush(*wxTRANSPARENT_BRUSH);
87f0efe2 1742 AdjustDC(dc);
9861f022 1743 dc.DrawLine(x1, y1, x2, y2);
87f0efe2
RR
1744}
1745
1746void wxGenericDataViewHeaderWindow::AdjustDC(wxDC& dc)
1747{
1748 int xpix, x;
1749
1750 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1751 m_owner->GetViewStart( &x, NULL );
1752
1753 // shift the DC origin to match the position of the main window horizontal
1754 // scrollbar: this allows us to always use logical coords
1755 dc.SetDeviceOrigin( -x * xpix, 0 );
4ed7af08
RR
1756}
1757
87f0efe2
RR
1758#endif // defined(__WXMSW__)
1759
0fcce6b9
RR
1760//-----------------------------------------------------------------------------
1761// wxDataViewRenameTimer
1762//-----------------------------------------------------------------------------
1763
1764wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1765{
1766 m_owner = owner;
1767}
1768
1769void wxDataViewRenameTimer::Notify()
1770{
1771 m_owner->OnRenameTimer();
1772}
1773
4ed7af08
RR
1774//-----------------------------------------------------------------------------
1775// wxDataViewMainWindow
1776//-----------------------------------------------------------------------------
1777
704c3490
RR
1778//The tree building helper, declared firstly
1779void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node);
1780
0a71f9e9 1781int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
cab07038
RR
1782{
1783 if (row1 > row2) return 1;
1784 if (row1 == row2) return 0;
1785 return -1;
1786}
1787
1788
45778c96 1789IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
4ed7af08
RR
1790
1791BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1792 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1793 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1794 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
cab07038
RR
1795 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
1796 EVT_CHAR (wxDataViewMainWindow::OnChar)
4ed7af08
RR
1797END_EVENT_TABLE()
1798
1799wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1800 const wxPoint &pos, const wxSize &size, const wxString &name ) :
72664514 1801 wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
cab07038 1802 m_selection( wxDataViewSelectionCmp )
120b9b05 1803
4ed7af08
RR
1804{
1805 SetOwner( parent );
f554a14b 1806
0fcce6b9
RR
1807 m_lastOnSame = false;
1808 m_renameTimer = new wxDataViewRenameTimer( this );
120b9b05 1809
0fcce6b9
RR
1810 // TODO: user better initial values/nothing selected
1811 m_currentCol = NULL;
1812 m_currentRow = 0;
1813
1814 // TODO: we need to calculate this smartly
c741d33f 1815 m_lineHeight =
87f0efe2
RR
1816#ifdef __WXMSW__
1817 17;
1818#else
1819 20;
1820#endif
9861f022 1821 wxASSERT(m_lineHeight > 2*PADDING_TOPBOTTOM);
e21f75bd
RR
1822
1823 m_dragCount = 0;
1824 m_dragStart = wxPoint(0,0);
0a71f9e9
RR
1825 m_lineLastClicked = (unsigned int) -1;
1826 m_lineBeforeLastClicked = (unsigned int) -1;
1827 m_lineSelectSingleOnUp = (unsigned int) -1;
120b9b05 1828
cab07038 1829 m_hasFocus = false;
f554a14b 1830
2e992e06 1831 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
72664514
RR
1832 SetBackgroundColour( *wxWHITE );
1833
9861f022
RR
1834 m_penRule = wxPen(GetRuleColour(), 1, wxSOLID);
1835
3b6280be
RR
1836 //Here I compose a pen can draw black lines, maybe there are something system colour to use
1837 m_penExpander = wxPen( wxColour(0,0,0), 1, wxSOLID );
aba9bfd0
RR
1838 //Some new added code to deal with the tree structure
1839 m_root = new wxDataViewTreeNode( NULL );
704c3490
RR
1840 m_root->SetHasChildren(true);
1841
3b6280be
RR
1842 //Make m_count = -1 will cause the class recaculate the real displaying number of rows.
1843 m_count = -1 ;
4b3feaa7 1844 UpdateDisplay();
4ed7af08
RR
1845}
1846
1847wxDataViewMainWindow::~wxDataViewMainWindow()
1848{
aba9bfd0 1849 DestroyTree();
0fcce6b9
RR
1850 delete m_renameTimer;
1851}
1852
1853void wxDataViewMainWindow::OnRenameTimer()
1854{
1855 // We have to call this here because changes may just have
1856 // been made and no screen update taken place.
1857 if ( m_dirty )
1858 wxSafeYield();
1859
0fcce6b9 1860 int xpos = 0;
9861f022 1861 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 1862 unsigned int i;
0fcce6b9
RR
1863 for (i = 0; i < cols; i++)
1864 {
1865 wxDataViewColumn *c = GetOwner()->GetColumn( i );
9861f022
RR
1866 if (c->IsHidden())
1867 continue; // skip it!
1868
0fcce6b9
RR
1869 if (c == m_currentCol)
1870 break;
1871 xpos += c->GetWidth();
1872 }
c741d33f 1873 wxRect labelRect( xpos, m_currentRow * m_lineHeight,
87f0efe2 1874 m_currentCol->GetWidth(), m_lineHeight );
0fcce6b9 1875
1e510b1e
RR
1876 GetOwner()->CalcScrolledPosition( labelRect.x, labelRect.y,
1877 &labelRect.x, &labelRect.y);
1878
6cdcbce3
RR
1879 wxDataViewItem item = GetItemByRow( m_currentRow );
1880 m_currentCol->GetRenderer()->StartEditing( item, labelRect );
4ed7af08
RR
1881}
1882
d5025dc0
RR
1883//------------------------------------------------------------------
1884// Helper class for do operation on the tree node
1885//------------------------------------------------------------------
aba9bfd0 1886class DoJob
a0f3af5f 1887{
aba9bfd0
RR
1888public:
1889 DoJob(){};
d5025dc0
RR
1890 virtual ~DoJob(){};
1891
1892 //The return value control how the tree-walker tranverse the tree
1893 // 0: Job done, stop tranverse and return
1894 // 1: Ignore the current node's subtree and continue
1895 // 2: Job not done, continue
1896 enum { OK = 0 , IGR = 1, CONT = 2 };
1897 virtual int operator() ( wxDataViewTreeNode * node ) = 0 ;
d47db7e0 1898 virtual int operator() ( void * n ) = 0;
aba9bfd0
RR
1899};
1900
aba9bfd0
RR
1901bool Walker( wxDataViewTreeNode * node, DoJob & func )
1902{
3b6280be 1903 if( node==NULL || !node->HasChildren())
aba9bfd0
RR
1904 return false;
1905
b7e9f8b1
RR
1906 switch( func( node ) )
1907 {
1908 case DoJob::OK :
1909 return true ;
1910 case DoJob::IGR:
1911 return false;
1912 case DoJob::CONT:
1913 default:
1914 ;
1915 }
1916
d47db7e0
RR
1917 wxDataViewTreeNodes nodes = node->GetNodes();
1918 wxDataViewTreeLeaves leaves = node->GetChildren();
1919
1920 int len_nodes = nodes.GetCount();
1921 int len = leaves.GetCount();
1922 int i = 0, nodes_i = 0;
b7e9f8b1 1923
aba9bfd0
RR
1924 for( ; i < len ; i ++ )
1925 {
d47db7e0
RR
1926 void * n = leaves[i];
1927 if( nodes_i < len_nodes && n == nodes[nodes_i]->GetItem().GetID() )
d5025dc0 1928 {
d47db7e0
RR
1929 wxDataViewTreeNode * nd = nodes[nodes_i];
1930 nodes_i++;
1931
d47db7e0
RR
1932 if( Walker( nd , func ) )
1933 return true;
1934
1935 }
1936 else
1937 switch( func( n ) )
1938 {
1939 case DoJob::OK :
1940 return true ;
1941 case DoJob::IGR:
1942 continue;
1943 case DoJob::CONT:
1944 default:
1945 ;
442c56e6 1946 }
aba9bfd0
RR
1947 }
1948 return false;
a0f3af5f
RR
1949}
1950
b7e9f8b1
RR
1951void wxDataViewMainWindow::SendModelEvent( wxEventType type, const wxDataViewItem & item )
1952{
1953 wxWindow *parent = GetParent();
1954 wxDataViewEvent le(type, parent->GetId());
1955
1956 le.SetEventObject(parent);
1957 le.SetModel(GetOwner()->GetModel());
1958 le.SetItem( item );
1959
1960 parent->GetEventHandler()->ProcessEvent(le);
1961}
1962
aba9bfd0 1963bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
a0f3af5f 1964{
66e09788 1965 SortPrepare();
d47db7e0 1966
351461fc
RR
1967 wxDataViewTreeNode * node;
1968 node = FindNode(parent);
b7e9f8b1 1969 SendModelEvent(wxEVT_COMMAND_DATAVIEW_MODEL_ITEM_ADDED, item );
351461fc
RR
1970
1971 if( node == NULL )
1972 return false;
1973
1974 node->SetHasChildren( true );
d47db7e0
RR
1975
1976 if( g_model->IsContainer( item ) )
1977 {
1978 wxDataViewTreeNode * newnode = new wxDataViewTreeNode( node );
1979 newnode->SetItem(item);
b7e9f8b1 1980 newnode->SetHasChildren( true );
d47db7e0
RR
1981 node->AddNode( newnode);
1982 }
1983 else
1984 node->AddLeaf( item.GetID() );
1985
1986 node->ChangeSubTreeCount(1);
1987
351461fc 1988 m_count = -1;
99d471a5 1989 UpdateDisplay();
b7e9f8b1 1990
99d471a5 1991 return true;
a0f3af5f
RR
1992}
1993
d47db7e0 1994void DestroyTreeHelper( wxDataViewTreeNode * node);
704c3490 1995
32143117
VZ
1996bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
1997 const wxDataViewItem& item)
a0f3af5f 1998{
66e09788 1999 SortPrepare();
d47db7e0 2000
351461fc 2001 wxDataViewTreeNode * node;
d3f00f59 2002 node = FindNode(parent);
b7e9f8b1 2003 SendModelEvent(wxEVT_COMMAND_DATAVIEW_MODEL_ITEM_DELETED, item);
351461fc 2004
d47db7e0 2005 if( node == NULL || node->GetChildren().Index( item.GetID() ) == wxNOT_FOUND )
351461fc
RR
2006 {
2007 return false;
2008 }
2009
d47db7e0
RR
2010 int sub = -1;
2011 node->GetChildren().Remove( item.GetID() );
2012 if( GetOwner()->GetModel()->IsContainer( item ) )
2013 {
a8505db0 2014 wxDataViewTreeNode * n = NULL;
d47db7e0
RR
2015 wxDataViewTreeNodes nodes = node->GetNodes();
2016 int len = nodes.GetCount();
2017 for( int i = 0 ; i < len; i ++)
2018 {
2019 if( nodes[i]->GetItem() == item )
2020 {
2021 n = nodes[i];
2022 break;
2023 }
2024 }
a8505db0
JS
2025
2026 if (!n)
2027 return false;
2028
d47db7e0
RR
2029 node->GetNodes().Remove( n );
2030 sub -= n->GetSubTreeCount();
2031 DestroyTreeHelper(n);
2032 }
2033
2034 if( node->GetChildrenNumber() == 0)
2035 node->SetHasChildren( false );
2036
2037 //Make the row number invalid and get a new valid one when user call GetRowCount
442c56e6 2038 m_count = -1;
d47db7e0
RR
2039 node->ChangeSubTreeCount(sub);
2040 //Change the current row to the last row if the current exceed the max row number
2041 if( m_currentRow > GetRowCount() )
2042 m_currentRow = m_count - 1;
351461fc 2043
99d471a5
RR
2044 UpdateDisplay();
2045 return true;
a0f3af5f
RR
2046}
2047
aba9bfd0 2048bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
a0f3af5f 2049{
66e09788 2050 SortPrepare();
b7e9f8b1
RR
2051 g_model->Resort();
2052
2053 SendModelEvent(wxEVT_COMMAND_DATAVIEW_MODEL_ITEM_CHANGED,item);
d47db7e0 2054
99d471a5 2055 return true;
a0f3af5f
RR
2056}
2057
b7e9f8b1 2058bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int col )
a0f3af5f 2059{
9861f022 2060 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
aba9bfd0 2061/*#define MAX_VIRTUAL_WIDTH 100000
9861f022
RR
2062
2063 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
0fdc2321
RR
2064 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2065 Refresh( true, &rect );
2066
2067 return true;
aba9bfd0 2068*/
66e09788 2069 SortPrepare();
b7e9f8b1
RR
2070 g_model->Resort();
2071
2072 //Send event
2073 wxWindow *parent = GetParent();
2074 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_MODEL_VALUE_CHANGED, parent->GetId());
2075 le.SetEventObject(parent);
2076 le.SetModel(GetOwner()->GetModel());
2077 le.SetItem(item);
2078 le.SetColumn(col);
2079 le.SetDataViewColumn(GetOwner()->GetColumn(col));
2080 parent->GetEventHandler()->ProcessEvent(le);
d47db7e0 2081
0fcce6b9 2082 return true;
a0f3af5f
RR
2083}
2084
2085bool wxDataViewMainWindow::Cleared()
2086{
66e09788 2087 SortPrepare();
d47db7e0 2088
704c3490 2089 DestroyTree();
99d471a5 2090 UpdateDisplay();
b7e9f8b1
RR
2091
2092 wxWindow *parent = GetParent();
2093 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_MODEL_CLEARED, parent->GetId());
2094 le.SetEventObject(parent);
2095 le.SetModel(GetOwner()->GetModel());
2096 parent->GetEventHandler()->ProcessEvent(le);
2097
99d471a5 2098 return true;
a0f3af5f
RR
2099}
2100
4b3feaa7
RR
2101void wxDataViewMainWindow::UpdateDisplay()
2102{
2103 m_dirty = true;
2104}
2105
2106void wxDataViewMainWindow::OnInternalIdle()
2107{
2108 wxWindow::OnInternalIdle();
f554a14b 2109
4b3feaa7
RR
2110 if (m_dirty)
2111 {
2112 RecalculateDisplay();
2113 m_dirty = false;
2114 }
2115}
2116
2117void wxDataViewMainWindow::RecalculateDisplay()
2118{
aba9bfd0 2119 wxDataViewModel *model = GetOwner()->GetModel();
4b3feaa7
RR
2120 if (!model)
2121 {
2122 Refresh();
2123 return;
2124 }
f554a14b 2125
9861f022 2126 int width = GetEndOfLastCol();
aba9bfd0 2127 int height = GetRowCount() * m_lineHeight;
4b3feaa7
RR
2128
2129 SetVirtualSize( width, height );
2130 GetOwner()->SetScrollRate( 10, m_lineHeight );
f554a14b 2131
4b3feaa7
RR
2132 Refresh();
2133}
2134
2135void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
2136{
2137 wxWindow::ScrollWindow( dx, dy, rect );
9861f022
RR
2138
2139 if (GetOwner()->m_headerArea)
2140 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
4b3feaa7
RR
2141}
2142
b7e9f8b1
RR
2143void wxDataViewMainWindow::ScrollTo( int rows )
2144{
2145 int x, y;
2146 m_owner->GetScrollPixelsPerUnit( &x, &y );
2147 int sc = rows*m_lineHeight/y;
2148 m_owner->Scroll(0, sc );
2149}
2150
f554a14b 2151void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
4ed7af08 2152{
aba9bfd0 2153 wxDataViewModel *model = GetOwner()->GetModel();
2e992e06
VZ
2154 wxAutoBufferedPaintDC dc( this );
2155
9861f022 2156 // prepare the DC
2e992e06
VZ
2157 dc.SetBackground(GetBackgroundColour());
2158 dc.Clear();
4b3feaa7 2159 GetOwner()->PrepareDC( dc );
4ed7af08 2160 dc.SetFont( GetFont() );
90675b95
RR
2161
2162 wxRect update = GetUpdateRegion().GetBox();
2163 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
f554a14b 2164
9861f022 2165 // compute which items needs to be redrawn
0a71f9e9 2166 unsigned int item_start = wxMax( 0, (update.y / m_lineHeight) );
c741d33f 2167 unsigned int item_count =
87f0efe2 2168 wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
3b6280be 2169 (int)(GetRowCount( )- item_start) );
9861f022
RR
2170 unsigned int item_last = item_start + item_count;
2171
2172 // compute which columns needs to be redrawn
2173 unsigned int cols = GetOwner()->GetColumnCount();
2174 unsigned int col_start = 0;
2175 unsigned int x_start = 0;
2176 for (x_start = 0; col_start < cols; col_start++)
2177 {
2178 wxDataViewColumn *col = GetOwner()->GetColumn(col_start);
2179 if (col->IsHidden())
2180 continue; // skip it!
2181
2182 unsigned int w = col->GetWidth();
2183 if (x_start+w >= (unsigned int)update.x)
2184 break;
2185
2186 x_start += w;
2187 }
90675b95 2188
9861f022
RR
2189 unsigned int col_last = col_start;
2190 unsigned int x_last = x_start;
2191 for (; col_last < cols; col_last++)
2192 {
2193 wxDataViewColumn *col = GetOwner()->GetColumn(col_last);
2194 if (col->IsHidden())
2195 continue; // skip it!
2196
2197 if (x_last > (unsigned int)update.GetRight())
2198 break;
2199
2200 x_last += col->GetWidth();
2201 }
2202
2203 // Draw horizontal rules if required
2204 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
2205 {
2206 dc.SetPen(m_penRule);
2207 dc.SetBrush(*wxTRANSPARENT_BRUSH);
2208
2209 for (unsigned int i = item_start; i <= item_last+1; i++)
2210 {
2211 int y = i * m_lineHeight;
2212 dc.DrawLine(x_start, y, x_last, y);
2213 }
2214 }
2215
2216 // Draw vertical rules if required
2217 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
2218 {
2219 dc.SetPen(m_penRule);
2220 dc.SetBrush(*wxTRANSPARENT_BRUSH);
2221
2222 int x = x_start;
2223 for (unsigned int i = col_start; i < col_last; i++)
2224 {
2225 wxDataViewColumn *col = GetOwner()->GetColumn(i);
2226 if (col->IsHidden())
2227 continue; // skip it
2228
2229 dc.DrawLine(x, item_start * m_lineHeight,
2230 x, item_last * m_lineHeight);
2231
2232 x += col->GetWidth();
2233 }
2234
2235 // Draw last vertical rule
c741d33f 2236 dc.DrawLine(x, item_start * m_lineHeight,
9861f022
RR
2237 x, item_last * m_lineHeight);
2238 }
2239
2240 // redraw the background for the items which are selected/current
2241 for (unsigned int item = item_start; item < item_last; item++)
cab07038 2242 {
87f0efe2
RR
2243 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
2244 if (selected || item == m_currentRow)
cab07038 2245 {
5d3f234b 2246 int flags = selected ? (int)wxCONTROL_SELECTED : 0;
daebb44c
RR
2247 if (item == m_currentRow)
2248 flags |= wxCONTROL_CURRENT;
2249 if (m_hasFocus)
2250 flags |= wxCONTROL_FOCUSED;
9861f022
RR
2251
2252 wxRect rect( x_start, item*m_lineHeight, x_last, m_lineHeight );
daebb44c
RR
2253 wxRendererNative::Get().DrawItemSelectionRect
2254 (
2255 this,
2256 dc,
2257 rect,
2258 flags
2259 );
2260 }
cab07038 2261 }
120b9b05 2262
9861f022 2263 // redraw all cells for all rows which must be repainted and for all columns
90675b95 2264 wxRect cell_rect;
9861f022
RR
2265 cell_rect.x = x_start;
2266 cell_rect.height = m_lineHeight; // -1 is for the horizontal rules
2267 for (unsigned int i = col_start; i < col_last; i++)
90675b95
RR
2268 {
2269 wxDataViewColumn *col = GetOwner()->GetColumn( i );
baa9ebc4 2270 wxDataViewRenderer *cell = col->GetRenderer();
90675b95 2271 cell_rect.width = col->GetWidth();
f554a14b 2272
87f0efe2
RR
2273 if (col->IsHidden())
2274 continue; // skipt it!
2275
9861f022 2276 for (unsigned int item = item_start; item < item_last; item++)
90675b95 2277 {
87f0efe2 2278 // get the cell value and set it into the renderer
90675b95 2279 wxVariant value;
b7e9f8b1
RR
2280 wxDataViewTreeNode * node = GetTreeNodeByRow(item);
2281 if( node == NULL )
2282 {
2283 continue;
2284 }
442c56e6 2285
3b6280be 2286 wxDataViewItem dataitem = node->GetItem();
b7e9f8b1 2287 model->GetValue( value, dataitem, col->GetModelColumn());
90675b95 2288 cell->SetValue( value );
87f0efe2
RR
2289
2290 // update the y offset
9861f022 2291 cell_rect.y = item * m_lineHeight;
87f0efe2 2292
442c56e6 2293 //Draw the expander here.
3b6280be
RR
2294 int indent = node->GetIndentLevel();
2295 if( col->GetModelColumn() == GetOwner()->GetExpanderColumn() )
2296 {
2297 //Calculate the indent first
d5025dc0
RR
2298 indent = cell_rect.x + GetOwner()->GetIndent() * indent;
2299
3b6280be 2300 int expander_width = m_lineHeight - 2*EXPANDER_MARGIN;
d5025dc0
RR
2301 // change the cell_rect.x to the appropriate pos
2302 int expander_x = indent + EXPANDER_MARGIN , expander_y = cell_rect.y + EXPANDER_MARGIN ;
3b6280be
RR
2303 indent = indent + m_lineHeight ; //try to use the m_lineHeight as the expander space
2304 dc.SetPen( m_penExpander );
d5025dc0
RR
2305 dc.SetBrush( wxNullBrush );
2306 if( node->HasChildren() )
2307 {
2308 //dc.DrawRoundedRectangle( expander_x,expander_y,expander_width,expander_width, 1.0);
2309 //dc.DrawLine( expander_x + 2 , expander_y + expander_width/2, expander_x + expander_width - 2, expander_y + expander_width/2 );
2310 wxRect rect( expander_x , expander_y, expander_width, expander_width);
2311 if( node->IsOpen() )
2312 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, wxCONTROL_EXPANDED );
2313 else
2314 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect );
2315 }
2316 else
2317 {
d47db7e0
RR
2318 // I am wandering whether we should draw dot lines between tree nodes
2319 delete node;
2320 //Yes, if the node does not have any child, it must be a leaf which mean that it is a temporarily created by GetTreeNodeByRow
d5025dc0
RR
2321 }
2322
2323 //force the expander column to left-center align
2324 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
3b6280be 2325 }
3b6280be 2326
442c56e6
VZ
2327
2328 // cannot be bigger than allocated space
87f0efe2 2329 wxSize size = cell->GetSize();
d5025dc0 2330 // Because of the tree structure indent, here we should minus the width of the cell for drawing
3b6280be 2331 size.x = wxMin( size.x + 2*PADDING_RIGHTLEFT, cell_rect.width - indent );
9861f022 2332 size.y = wxMin( size.y + 2*PADDING_TOPBOTTOM, cell_rect.height );
87f0efe2
RR
2333
2334 wxRect item_rect(cell_rect.GetTopLeft(), size);
9861f022 2335 int align = cell->GetAlignment();
87f0efe2
RR
2336
2337 // horizontal alignment:
9861f022
RR
2338 item_rect.x = cell_rect.x;
2339 if (align & wxALIGN_CENTER_HORIZONTAL)
2340 item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
2341 else if (align & wxALIGN_RIGHT)
87f0efe2
RR
2342 item_rect.x = cell_rect.x + cell_rect.width - size.x;
2343 //else: wxALIGN_LEFT is the default
2344
2345 // vertical alignment:
9861f022
RR
2346 item_rect.y = cell_rect.y;
2347 if (align & wxALIGN_CENTER_VERTICAL)
87f0efe2 2348 item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
9861f022 2349 else if (align & wxALIGN_BOTTOM)
87f0efe2
RR
2350 item_rect.y = cell_rect.y + cell_rect.height - size.y;
2351 //else: wxALIGN_TOP is the default
2352
9861f022
RR
2353 // add padding
2354 item_rect.x += PADDING_RIGHTLEFT;
2355 item_rect.y += PADDING_TOPBOTTOM;
2356 item_rect.width = size.x - 2 * PADDING_RIGHTLEFT;
2357 item_rect.height = size.y - 2 * PADDING_TOPBOTTOM;
64b3c262 2358
3b6280be
RR
2359 //Here we add the tree indent
2360 item_rect.x += indent;
442c56e6 2361
64b3c262 2362 int state = 0;
87f0efe2 2363 if (m_selection.Index(item) != wxNOT_FOUND)
64b3c262 2364 state |= wxDATAVIEW_CELL_SELECTED;
87f0efe2 2365
9861f022
RR
2366 // TODO: it would be much more efficient to create a clipping
2367 // region for the entire column being rendered (in the OnPaint
2368 // of wxDataViewMainWindow) instead of a single clip region for
2369 // each cell. However it would mean that each renderer should
2370 // respect the given wxRect's top & bottom coords, eventually
2371 // violating only the left & right coords - however the user can
2372 // make its own renderer and thus we cannot be sure of that.
2373 dc.SetClippingRegion( item_rect );
64b3c262 2374 cell->Render( item_rect, &dc, state );
9861f022 2375 dc.DestroyClippingRegion();
90675b95 2376 }
f554a14b 2377
90675b95
RR
2378 cell_rect.x += cell_rect.width;
2379 }
4ed7af08
RR
2380}
2381
9861f022 2382int wxDataViewMainWindow::GetCountPerPage() const
cab07038
RR
2383{
2384 wxSize size = GetClientSize();
2385 return size.y / m_lineHeight;
2386}
2387
9861f022 2388int wxDataViewMainWindow::GetEndOfLastCol() const
e21f75bd
RR
2389{
2390 int width = 0;
0a71f9e9 2391 unsigned int i;
9861f022 2392 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
e21f75bd 2393 {
c741d33f 2394 const wxDataViewColumn *c =
9861f022
RR
2395 wx_const_cast(wxDataViewCtrl*, GetOwner())->GetColumn( i );
2396
2397 if (!c->IsHidden())
2398 width += c->GetWidth();
e21f75bd
RR
2399 }
2400 return width;
2401}
2402
9861f022 2403unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
72664514
RR
2404{
2405 int x = 0;
2406 int y = 0;
2407 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
120b9b05 2408
72664514
RR
2409 return y / m_lineHeight;
2410}
2411
442c56e6 2412unsigned int wxDataViewMainWindow::GetLastVisibleRow()
72664514
RR
2413{
2414 wxSize client_size = GetClientSize();
c741d33f 2415 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
87f0efe2 2416 &client_size.x, &client_size.y );
72664514 2417
5637e131 2418 return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
72664514
RR
2419}
2420
442c56e6 2421unsigned int wxDataViewMainWindow::GetRowCount()
cab07038 2422{
3b6280be
RR
2423 if ( m_count == -1 )
2424 {
2425 m_count = RecalculateCount();
d5025dc0
RR
2426 int width, height;
2427 GetVirtualSize( &width, &height );
3b6280be
RR
2428 height = m_count * m_lineHeight;
2429
2430 SetVirtualSize( width, height );
2431 }
aba9bfd0 2432 return m_count;
cab07038
RR
2433}
2434
0a71f9e9 2435void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
e21f75bd
RR
2436{
2437 m_currentRow = row;
120b9b05 2438
e21f75bd
RR
2439 // send event
2440}
2441
cab07038
RR
2442void wxDataViewMainWindow::SelectAllRows( bool on )
2443{
4a851b11
VZ
2444 if (IsEmpty())
2445 return;
120b9b05 2446
cab07038
RR
2447 if (on)
2448 {
72664514 2449 m_selection.Clear();
0a71f9e9 2450 for (unsigned int i = 0; i < GetRowCount(); i++)
cab07038 2451 m_selection.Add( i );
72664514
RR
2452 Refresh();
2453 }
2454 else
2455 {
0a71f9e9
RR
2456 unsigned int first_visible = GetFirstVisibleRow();
2457 unsigned int last_visible = GetLastVisibleRow();
2458 unsigned int i;
72664514 2459 for (i = 0; i < m_selection.GetCount(); i++)
120b9b05 2460 {
0a71f9e9 2461 unsigned int row = m_selection[i];
72664514
RR
2462 if ((row >= first_visible) && (row <= last_visible))
2463 RefreshRow( row );
2464 }
2465 m_selection.Clear();
cab07038 2466 }
cab07038
RR
2467}
2468
0a71f9e9 2469void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
cab07038
RR
2470{
2471 if (m_selection.Index( row ) == wxNOT_FOUND)
2472 {
2473 if (on)
2474 {
2475 m_selection.Add( row );
2476 RefreshRow( row );
2477 }
2478 }
2479 else
2480 {
2481 if (!on)
2482 {
2483 m_selection.Remove( row );
2484 RefreshRow( row );
2485 }
2486 }
2487}
2488
0a71f9e9 2489void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
cab07038
RR
2490{
2491 if (from > to)
2492 {
0a71f9e9 2493 unsigned int tmp = from;
cab07038
RR
2494 from = to;
2495 to = tmp;
2496 }
2497
0a71f9e9 2498 unsigned int i;
cab07038
RR
2499 for (i = from; i <= to; i++)
2500 {
2501 if (m_selection.Index( i ) == wxNOT_FOUND)
2502 {
2503 if (on)
2504 m_selection.Add( i );
2505 }
2506 else
2507 {
2508 if (!on)
2509 m_selection.Remove( i );
2510 }
2511 }
2512 RefreshRows( from, to );
2513}
2514
87f0efe2
RR
2515void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2516{
2517 for (size_t i=0; i < aSelections.GetCount(); i++)
2518 {
2519 int n = aSelections[i];
2520
2521 m_selection.Add( n );
2522 RefreshRow( n );
2523 }
2524}
2525
0a71f9e9 2526void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
cab07038
RR
2527{
2528 if (m_selection.Index( row ) == wxNOT_FOUND)
2529 m_selection.Add( row );
2530 else
2531 m_selection.Remove( row );
120b9b05 2532 RefreshRow( row );
cab07038
RR
2533}
2534
0a71f9e9 2535bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
cab07038
RR
2536{
2537 return (m_selection.Index( row ) != wxNOT_FOUND);
2538}
2539
0a71f9e9 2540void wxDataViewMainWindow::RefreshRow( unsigned int row )
cab07038 2541{
e21f75bd 2542 wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
cab07038 2543 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2544
cab07038
RR
2545 wxSize client_size = GetClientSize();
2546 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2547 wxRect intersect_rect = client_rect.Intersect( rect );
2548 if (intersect_rect.width > 0)
2549 Refresh( true, &intersect_rect );
2550}
2551
0a71f9e9 2552void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
cab07038
RR
2553{
2554 if (from > to)
2555 {
0a71f9e9 2556 unsigned int tmp = to;
cab07038
RR
2557 to = from;
2558 from = tmp;
2559 }
2560
e21f75bd 2561 wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
cab07038 2562 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2563
cab07038
RR
2564 wxSize client_size = GetClientSize();
2565 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2566 wxRect intersect_rect = client_rect.Intersect( rect );
2567 if (intersect_rect.width > 0)
2568 Refresh( true, &intersect_rect );
2569}
2570
0a71f9e9 2571void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
cab07038 2572{
0a71f9e9 2573 unsigned int count = GetRowCount();
4a851b11
VZ
2574 if (firstRow > count)
2575 return;
120b9b05 2576
e21f75bd 2577 wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
cab07038 2578 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2579
cab07038
RR
2580 wxSize client_size = GetClientSize();
2581 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2582 wxRect intersect_rect = client_rect.Intersect( rect );
2583 if (intersect_rect.width > 0)
2584 Refresh( true, &intersect_rect );
2585}
2586
0a71f9e9 2587void wxDataViewMainWindow::OnArrowChar(unsigned int newCurrent, const wxKeyEvent& event)
cab07038 2588{
4a851b11 2589 wxCHECK_RET( newCurrent < GetRowCount(),
cab07038
RR
2590 _T("invalid item index in OnArrowChar()") );
2591
2592 // if there is no selection, we cannot move it anywhere
2593 if (!HasCurrentRow())
2594 return;
2595
0a71f9e9 2596 unsigned int oldCurrent = m_currentRow;
cab07038
RR
2597
2598 // in single selection we just ignore Shift as we can't select several
2599 // items anyhow
e21f75bd 2600 if ( event.ShiftDown() && !IsSingleSel() )
cab07038
RR
2601 {
2602 RefreshRow( oldCurrent );
120b9b05 2603
e21f75bd 2604 ChangeCurrentRow( newCurrent );
cab07038
RR
2605
2606 // select all the items between the old and the new one
2607 if ( oldCurrent > newCurrent )
2608 {
2609 newCurrent = oldCurrent;
2610 oldCurrent = m_currentRow;
2611 }
2612
2613 SelectRows( oldCurrent, newCurrent, true );
2614 }
2615 else // !shift
2616 {
72664514 2617 RefreshRow( oldCurrent );
120b9b05 2618
cab07038
RR
2619 // all previously selected items are unselected unless ctrl is held
2620 if ( !event.ControlDown() )
2621 SelectAllRows(false);
2622
e21f75bd 2623 ChangeCurrentRow( newCurrent );
cab07038
RR
2624
2625 if ( !event.ControlDown() )
2626 SelectRow( m_currentRow, true );
72664514
RR
2627 else
2628 RefreshRow( m_currentRow );
cab07038
RR
2629 }
2630
9861f022
RR
2631 //EnsureVisible( m_currentRow );
2632}
2633
2634wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2635{
2636 wxRect rect;
2637 rect.x = 0;
2638 rect.y = m_lineHeight * row;
2639 rect.width = GetEndOfLastCol();
2640 rect.height = m_lineHeight;
2641
2642 return rect;
cab07038
RR
2643}
2644
aba9bfd0
RR
2645class RowToItemJob: public DoJob
2646{
2647public:
2648 RowToItemJob( unsigned int row , int current ) { this->row = row; this->current = current ;}
2649 virtual ~RowToItemJob(){};
2650
d5025dc0 2651 virtual int operator() ( wxDataViewTreeNode * node )
d47db7e0
RR
2652 {
2653 current ++;
2654 if( current == static_cast<int>(row))
b7e9f8b1 2655 {
d47db7e0
RR
2656 ret = node->GetItem() ;
2657 return DoJob::OK;
2658 }
d5025dc0 2659
d47db7e0
RR
2660 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2661 {
2662 current += node->GetSubTreeCount();
2663 return DoJob::IGR;
2664 }
2665 else
b7e9f8b1
RR
2666 {
2667 //If the current has no child node, we can find the desired item of the row number directly.
2668 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
2669 if( node->GetNodes().GetCount() == 0)
2670 {
2671 int index = static_cast<int>(row) - current - 1;
2672 ret = node->GetChildren().Item( index );
2673 return DoJob::OK;
2674 }
d47db7e0 2675 return DoJob::CONT;
b7e9f8b1 2676 }
d47db7e0
RR
2677 }
2678
2679 virtual int operator() ( void * n )
2680 {
2681 current ++;
2682 if( current == static_cast<int>(row))
b7e9f8b1 2683 {
d47db7e0
RR
2684 ret = wxDataViewItem( n ) ;
2685 return DoJob::OK;
2686 }
2687 return DoJob::CONT;
2688 }
d5025dc0 2689 wxDataViewItem GetResult(){ return ret; }
aba9bfd0
RR
2690private:
2691 unsigned int row;
2692 int current ;
2693 wxDataViewItem ret;
2694};
2695
2696wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row)
2697{
b7e9f8b1 2698 RowToItemJob job( row, -2 );
aba9bfd0
RR
2699 Walker( m_root , job );
2700 return job.GetResult();
2701}
2702
3b6280be
RR
2703class RowToTreeNodeJob: public DoJob
2704{
2705public:
442c56e6
VZ
2706 RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
2707 {
2708 this->row = row;
2709 this->current = current ;
d47db7e0
RR
2710 ret = NULL ;
2711 parent = node;
2712 }
d5025dc0 2713 virtual ~RowToTreeNodeJob(){};
3b6280be
RR
2714
2715 virtual int operator() ( wxDataViewTreeNode * node )
d5025dc0 2716 {
d47db7e0 2717 current ++;
704c3490 2718 if( current == static_cast<int>(row))
b7e9f8b1 2719 {
d5025dc0
RR
2720 ret = node ;
2721 return DoJob::OK;
3b6280be 2722 }
d47db7e0
RR
2723
2724 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
2725 {
2726 current += node->GetSubTreeCount();
2727 return DoJob::IGR;
2728 }
d5025dc0 2729 else
d47db7e0
RR
2730 {
2731 parent = node;
b7e9f8b1
RR
2732 //If the current has no child node, we can find the desired item of the row number directly.
2733 //This if can speed up finding in some case, and will has a very good effect when it comes to list view
2734 if( node->GetNodes().GetCount() == 0)
2735 {
2736 int index = static_cast<int>(row) - current - 1;
2737 void * n = node->GetChildren().Item( index );
2738 ret = new wxDataViewTreeNode( parent ) ;
2739 ret->SetItem( wxDataViewItem( n ));
2740 ret->SetHasChildren(false);
2741 return DoJob::OK;
2742 }
d47db7e0
RR
2743 return DoJob::CONT;
2744 }
2745
b7e9f8b1 2746
d5025dc0 2747 }
3b6280be 2748
d47db7e0
RR
2749 virtual int operator() ( void * n )
2750 {
2751 current ++;
2752 if( current == static_cast<int>(row))
b7e9f8b1 2753 {
d47db7e0
RR
2754 ret = new wxDataViewTreeNode( parent ) ;
2755 ret->SetItem( wxDataViewItem( n ));
2756 ret->SetHasChildren(false);
2757 return DoJob::OK;
2758 }
442c56e6 2759
d47db7e0
RR
2760 return DoJob::CONT;
2761 }
3b6280be
RR
2762 wxDataViewTreeNode * GetResult(){ return ret; }
2763private:
2764 unsigned int row;
2765 int current ;
2766 wxDataViewTreeNode * ret;
d47db7e0 2767 wxDataViewTreeNode * parent ;
3b6280be
RR
2768};
2769
2770
2771wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row)
2772{
b7e9f8b1 2773 RowToTreeNodeJob job( row , -2, m_root );
3b6280be
RR
2774 Walker( m_root , job );
2775 return job.GetResult();
2776}
2777
66e09788
RR
2778wxDataViewEvent wxDataViewMainWindow::SendExpanderEvent( wxEventType type, const wxDataViewItem & item )
2779{
2780 wxWindow *parent = GetParent();
2781 wxDataViewEvent le(type, parent->GetId());
2782
2783 le.SetEventObject(parent);
2784 le.SetModel(GetOwner()->GetModel());
2785 le.SetItem( item );
2786
2787 parent->GetEventHandler()->ProcessEvent(le);
2788 return le;
2789}
2790
3b6280be
RR
2791void wxDataViewMainWindow::OnExpanding( unsigned int row )
2792{
2793 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
2794 if( node != NULL )
2795 {
d5025dc0 2796 if( node->HasChildren())
66e09788 2797 {
d5025dc0 2798 if( !node->IsOpen())
3b6280be 2799 {
66e09788
RR
2800 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,node->GetItem());
2801 //Check if the user prevent expanding
2802 if( e.GetSkipped() )
2803 return;
2804
d5025dc0 2805 node->ToggleOpen();
704c3490
RR
2806 //Here I build the children of current node
2807 if( node->GetChildrenNumber() == 0 )
2808 BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
d5025dc0 2809 m_count = -1;
351461fc 2810 UpdateDisplay();
66e09788
RR
2811 //Send the expanded event
2812 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
351461fc
RR
2813 }
2814 else
2815 {
2816 SelectRow( row, false );
2817 SelectRow( row + 1, true );
2818 ChangeCurrentRow( row + 1 );
3b6280be 2819 }
66e09788 2820 }
d47db7e0
RR
2821 else
2822 delete node;
3b6280be
RR
2823 }
2824}
2825
2826void wxDataViewMainWindow::OnCollapsing(unsigned int row)
2827{
2828 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
d5025dc0 2829 if( node != NULL )
3b6280be 2830 {
d47db7e0
RR
2831 wxDataViewTreeNode * nd = node;
2832
3b6280be
RR
2833 if( node->HasChildren() && node->IsOpen() )
2834 {
66e09788
RR
2835 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem());
2836 if( e.GetSkipped() )
2837 return;
3b6280be
RR
2838 node->ToggleOpen();
2839 m_count = -1;
351461fc 2840 UpdateDisplay();
66e09788
RR
2841 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,nd->GetItem());
2842 }
2843 else
2844 {
2845 node = node->GetParent();
2846 if( node != NULL )
2847 {
2848 int parent = GetRowByItem( node->GetItem() ) ;
2849 if( parent >= 0 )
2850 {
2851 SelectRow( row, false);
2852 SelectRow(parent , true );
2853 ChangeCurrentRow( parent );
2854 }
2855 }
2856 }
2857 if( !nd->HasChildren())
2858 delete nd;
3b6280be
RR
2859 }
2860}
2861
351461fc
RR
2862wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
2863{
2864 wxDataViewModel * model = GetOwner()->GetModel();
2865 if( model == NULL )
2866 return NULL;
2867
2868 //Compose the a parent-chain of the finding item
2869 ItemList list;
2870 list.DeleteContents( true );
2871 wxDataViewItem it( item );
2872 while( it.IsOk() )
2873 {
2874 wxDataViewItem * pItem = new wxDataViewItem( it );
2875 list.Insert( pItem );
2876 it = model->GetParent( it );
2877 }
2878
442c56e6 2879 //Find the item along the parent-chain.
351461fc 2880 //This algorithm is designed to speed up the node-finding method
351461fc
RR
2881 wxDataViewTreeNode * node = m_root;
2882 for( ItemList::Node * n = list.GetFirst(); n; n = n->GetNext() )
2883 {
2884 if( node->HasChildren() )
2885 {
2886 if( node->GetChildrenNumber() == 0 )
2887 BuildTreeHelper(model, node->GetItem(), node);
2888
d47db7e0 2889 wxDataViewTreeNodes nodes = node->GetNodes();
b7e9f8b1
RR
2890 //The wxSortedArray search a node in binary search, so using Item() is more efficient
2891 wxDataViewTreeNode temp;
2892 temp.SetItem(*(n->GetData()));
2893 int index = nodes.Index( &temp );
2894 if( index == wxNOT_FOUND )
351461fc 2895 return NULL;
b7e9f8b1 2896 node = nodes[index];
351461fc
RR
2897 }
2898 else
2899 return NULL;
2900 }
2901 return node;
2902}
2903
66e09788
RR
2904void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item, unsigned int & column )
2905{
2906 unsigned int cols = GetOwner()->GetColumnCount();
2907 unsigned int colnum = 0;
2908 unsigned int x_start = 0;
2909 int x, y;
2910 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
2911 for (x_start = 0; colnum < cols; colnum++)
2912 {
2913 wxDataViewColumn *col = GetOwner()->GetColumn(colnum);
2914 if (col->IsHidden())
2915 continue; // skip it!
2916
2917 unsigned int w = col->GetWidth();
2918 if (x_start+w >= (unsigned int)x)
2919 break;
2920
2921 x_start += w;
2922 }
2923
2924 column = colnum;
2925 item = GetItemByRow( y/m_lineHeight );
2926}
2927
2928wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item, unsigned int column )
2929{
2930 int row = GetRowByItem(item);
2931 int y = row*m_lineHeight;
2932 int h = m_lineHeight;
2933 int x = 0;
2934 wxDataViewColumn *col = NULL;
2935 for( int i = 0, cols = GetOwner()->GetColumnCount(); i < cols; i ++ )
2936 {
2937 col = GetOwner()->GetColumn( i );
2938 x += col->GetWidth();
2939 if( i == column - 1 )
2940 break;
2941 }
2942 int w = col->GetWidth();
2943 m_owner->CalcScrolledPosition( x, y, &x, &y );
2944 return wxRect(x, y, w, h);
2945}
2946
442c56e6 2947int wxDataViewMainWindow::RecalculateCount()
3b6280be 2948{
d47db7e0 2949 return m_root->GetSubTreeCount();
3b6280be
RR
2950}
2951
aba9bfd0
RR
2952class ItemToRowJob : public DoJob
2953{
2954public:
d47db7e0 2955 ItemToRowJob(const wxDataViewItem & item, ItemList::Node * node )
b7e9f8b1 2956 { this->item = item ; ret = -1 ; nd = node ; }
aba9bfd0
RR
2957 virtual ~ItemToRowJob(){};
2958
b7e9f8b1 2959 //Maybe binary search will help to speed up this process
d5025dc0
RR
2960 virtual int operator() ( wxDataViewTreeNode * node)
2961 {
2962 ret ++;
2963 if( node->GetItem() == item )
d47db7e0 2964 {
d5025dc0 2965 return DoJob::OK;
d47db7e0 2966 }
d5025dc0 2967
d47db7e0
RR
2968 if( nd && node->GetItem() == *(nd->GetData()))
2969 {
2970 nd = nd->GetNext();
d5025dc0 2971 return DoJob::CONT;
d47db7e0 2972 }
d5025dc0 2973 else
d47db7e0
RR
2974 {
2975 ret += node->GetSubTreeCount();
d5025dc0 2976 return DoJob::IGR;
d47db7e0 2977 }
442c56e6 2978
d5025dc0 2979 }
aba9bfd0 2980
d47db7e0
RR
2981 virtual int operator() ( void * n )
2982 {
2983 ret ++;
2984 if( n == item.GetID() )
2985 return DoJob::OK;
2986 return DoJob::CONT;
2987 }
3b6280be
RR
2988 //the row number is begin from zero
2989 int GetResult(){ return ret -1 ; }
aba9bfd0 2990private:
d47db7e0 2991 ItemList::Node * nd;
aba9bfd0
RR
2992 wxDataViewItem item;
2993 int ret;
442c56e6 2994
aba9bfd0
RR
2995};
2996
b7e9f8b1 2997int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item)
aba9bfd0 2998{
d47db7e0
RR
2999 wxDataViewModel * model = GetOwner()->GetModel();
3000 if( model == NULL )
3001 return 0;
3002
b7e9f8b1
RR
3003 if( !item.IsOk() )
3004 return -1;
3005
d47db7e0
RR
3006 //Compose the a parent-chain of the finding item
3007 ItemList list;
b7e9f8b1 3008 wxDataViewItem * pItem = NULL;
d47db7e0
RR
3009 list.DeleteContents( true );
3010 wxDataViewItem it( item );
3011 while( it.IsOk() )
3012 {
b7e9f8b1 3013 pItem = new wxDataViewItem( it );
d47db7e0
RR
3014 list.Insert( pItem );
3015 it = model->GetParent( it );
3016 }
b7e9f8b1
RR
3017 pItem = new wxDataViewItem( );
3018 list.Insert( pItem );
d47db7e0
RR
3019
3020 ItemToRowJob job( item, list.GetFirst() );
aba9bfd0
RR
3021 Walker(m_root , job );
3022 return job.GetResult();
3023}
3024
704c3490 3025void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node)
aba9bfd0 3026{
351461fc 3027 if( !model->IsContainer( item ) )
704c3490 3028 return ;
442c56e6 3029
aba9bfd0 3030 wxDataViewItem i = model->GetFirstChild( item );
d47db7e0 3031 int num = 0;
aba9bfd0
RR
3032 while( i.IsOk() )
3033 {
d47db7e0
RR
3034 num ++;
3035 if( model->IsContainer( i ) )
3036 {
3037 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
3038 n->SetItem(i);
3039 n->SetHasChildren( true ) ;
3040 node->AddNode( n );
3041 }
3042 else
3043 {
3044 node->AddLeaf( i.GetID() );
3045 }
3b6280be 3046 i = model->GetNextSibling( i );
aba9bfd0 3047 }
d47db7e0
RR
3048 node->SetSubTreeCount( num );
3049 wxDataViewTreeNode * n = node->GetParent();
3050 if( n != NULL)
3051 n->ChangeSubTreeCount(num);
3052
aba9bfd0
RR
3053}
3054
3055void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
3056{
3057 //First we define a invalid item to fetch the top-level elements
3058 wxDataViewItem item;
66e09788 3059 SortPrepare();
3b6280be
RR
3060 BuildTreeHelper( model, item, m_root);
3061 m_count = -1 ;
aba9bfd0
RR
3062}
3063
3064void DestroyTreeHelper( wxDataViewTreeNode * node )
3065{
d47db7e0 3066 if( node->GetNodeNumber() != 0 )
aba9bfd0 3067 {
d47db7e0 3068 int len = node->GetNodeNumber();
3b6280be 3069 int i = 0 ;
d47db7e0 3070 wxDataViewTreeNodes nodes = node->GetNodes();
3b6280be 3071 for( ; i < len; i ++ )
aba9bfd0
RR
3072 {
3073 DestroyTreeHelper(nodes[i]);
3074 }
3075 }
3076 delete node;
3077}
3078
3079void wxDataViewMainWindow::DestroyTree()
3080{
3081 DestroyTreeHelper(m_root);
d47db7e0 3082 m_root->SetSubTreeCount(0);
aba9bfd0
RR
3083 m_count = 0 ;
3084}
3085
cab07038
RR
3086void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
3087{
3088 if (event.GetKeyCode() == WXK_TAB)
3089 {
3090 wxNavigationKeyEvent nevent;
3091 nevent.SetWindowChange( event.ControlDown() );
3092 nevent.SetDirection( !event.ShiftDown() );
3093 nevent.SetEventObject( GetParent()->GetParent() );
3094 nevent.SetCurrentFocus( m_parent );
3095 if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
3096 return;
3097 }
3098
3099 // no item -> nothing to do
3100 if (!HasCurrentRow())
3101 {
3102 event.Skip();
3103 return;
3104 }
120b9b05 3105
cab07038
RR
3106 // don't use m_linesPerPage directly as it might not be computed yet
3107 const int pageSize = GetCountPerPage();
3108 wxCHECK_RET( pageSize, _T("should have non zero page size") );
3109
3110 switch ( event.GetKeyCode() )
3111 {
3112 case WXK_UP:
3113 if ( m_currentRow > 0 )
3114 OnArrowChar( m_currentRow - 1, event );
3115 break;
3116
3117 case WXK_DOWN:
4a851b11 3118 if ( m_currentRow < GetRowCount() - 1 )
cab07038
RR
3119 OnArrowChar( m_currentRow + 1, event );
3120 break;
3b6280be
RR
3121 //Add the process for tree expanding/collapsing
3122 case WXK_LEFT:
b7e9f8b1
RR
3123 OnCollapsing(m_currentRow);
3124 break;
3125 case WXK_RIGHT:
3126 OnExpanding( m_currentRow);
3127 break;
cab07038
RR
3128 case WXK_END:
3129 if (!IsEmpty())
3130 OnArrowChar( GetRowCount() - 1, event );
3131 break;
3132
3133 case WXK_HOME:
3134 if (!IsEmpty())
3135 OnArrowChar( 0, event );
3136 break;
3137
3138 case WXK_PAGEUP:
3139 {
3140 int steps = pageSize - 1;
3141 int index = m_currentRow - steps;
3142 if (index < 0)
3143 index = 0;
3144
3145 OnArrowChar( index, event );
3146 }
3147 break;
3148
3149 case WXK_PAGEDOWN:
3150 {
3151 int steps = pageSize - 1;
0a71f9e9
RR
3152 unsigned int index = m_currentRow + steps;
3153 unsigned int count = GetRowCount();
cab07038
RR
3154 if ( index >= count )
3155 index = count - 1;
3156
3157 OnArrowChar( index, event );
3158 }
3159 break;
3160
3161 default:
3162 event.Skip();
3163 }
3164}
3165
4ed7af08
RR
3166void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
3167{
e21f75bd
RR
3168 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
3169 {
3170 // let the base handle mouse wheel events.
3171 event.Skip();
3172 return;
3173 }
3174
0fdc2321
RR
3175 int x = event.GetX();
3176 int y = event.GetY();
3177 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
3178
3179 wxDataViewColumn *col = NULL;
3180
3181 int xpos = 0;
9861f022 3182 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 3183 unsigned int i;
0fdc2321
RR
3184 for (i = 0; i < cols; i++)
3185 {
3186 wxDataViewColumn *c = GetOwner()->GetColumn( i );
9861f022
RR
3187 if (c->IsHidden())
3188 continue; // skip it!
3189
0fdc2321
RR
3190 if (x < xpos + c->GetWidth())
3191 {
3192 col = c;
3193 break;
3194 }
3195 xpos += c->GetWidth();
3196 }
f554a14b 3197 if (!col)
0fdc2321 3198 return;
baa9ebc4 3199 wxDataViewRenderer *cell = col->GetRenderer();
f554a14b 3200
0a71f9e9 3201 unsigned int current = y / m_lineHeight;
120b9b05 3202
e21f75bd
RR
3203 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
3204 {
3205 // Unselect all if below the last row ?
3206 return;
3207 }
f554a14b 3208
aba9bfd0 3209 wxDataViewModel *model = GetOwner()->GetModel();
0fdc2321 3210
e21f75bd
RR
3211 if (event.Dragging())
3212 {
3213 if (m_dragCount == 0)
3214 {
3215 // we have to report the raw, physical coords as we want to be
3216 // able to call HitTest(event.m_pointDrag) from the user code to
3217 // get the item being dragged
3218 m_dragStart = event.GetPosition();
3219 }
3220
3221 m_dragCount++;
3222
3223 if (m_dragCount != 3)
3224 return;
3225
3226 if (event.LeftIsDown())
3227 {
3228 // Notify cell about drag
3229 }
3230 return;
3231 }
3232 else
3233 {
3234 m_dragCount = 0;
3235 }
3236
3237 bool forceClick = false;
3238
0fcce6b9
RR
3239 if (event.ButtonDClick())
3240 {
3241 m_renameTimer->Stop();
3242 m_lastOnSame = false;
3243 }
3244
0fdc2321
RR
3245 if (event.LeftDClick())
3246 {
e21f75bd 3247 if ( current == m_lineLastClicked )
0fdc2321 3248 {
e21f75bd
RR
3249 if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
3250 {
6cdcbce3 3251 wxDataViewItem item = GetItemByRow(current);
e21f75bd 3252 wxVariant value;
6cdcbce3 3253 model->GetValue( value, item, col->GetModelColumn() );
e21f75bd 3254 cell->SetValue( value );
c741d33f 3255 wxRect cell_rect( xpos, current * m_lineHeight,
87f0efe2 3256 col->GetWidth(), m_lineHeight );
6cdcbce3 3257 cell->Activate( cell_rect, model, item, col->GetModelColumn() );
b7e9f8b1
RR
3258
3259 wxWindow *parent = GetParent();
3260 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
3261
3262 le.SetEventObject(parent);
3263 le.SetColumn(col->GetModelColumn());
3264 le.SetDataViewColumn(col);
3265 le.SetModel(GetOwner()->GetModel());
3266
3267 parent->GetEventHandler()->ProcessEvent(le);
e21f75bd
RR
3268 }
3269 return;
3270 }
3271 else
3272 {
3273 // The first click was on another item, so don't interpret this as
3274 // a double click, but as a simple click instead
3275 forceClick = true;
0fdc2321 3276 }
120b9b05 3277 }
f554a14b 3278
0fcce6b9
RR
3279 if (event.LeftUp())
3280 {
0a71f9e9 3281 if (m_lineSelectSingleOnUp != (unsigned int)-1)
e21f75bd
RR
3282 {
3283 // select single line
3284 SelectAllRows( false );
3285 SelectRow( m_lineSelectSingleOnUp, true );
3286 }
120b9b05 3287
3b6280be 3288 //Process the event of user clicking the expander
d5025dc0
RR
3289 bool expander = false;
3290 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
3291 if( node!=NULL && node->HasChildren() )
3292 {
3293 int indent = node->GetIndentLevel();
3294 indent = GetOwner()->GetIndent()*indent;
3295 wxRect rect( xpos + indent + EXPANDER_MARGIN, current * m_lineHeight + EXPANDER_MARGIN, m_lineHeight-2*EXPANDER_MARGIN,m_lineHeight-2*EXPANDER_MARGIN);
3296 if( rect.Contains( x, y) )
3297 {
3298 expander = true;
704c3490
RR
3299 if( node->IsOpen() )
3300 OnCollapsing(current);
3301 else
3302 OnExpanding( current );
d5025dc0
RR
3303 }
3304 }
3b6280be
RR
3305
3306 //If the user click the expander, we do not do editing even if the column with expander are editable
3307 if (m_lastOnSame && !expander )
0fcce6b9 3308 {
a8461d31 3309 if ((col == m_currentCol) && (current == m_currentRow) &&
0fcce6b9
RR
3310 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
3311 {
3312 m_renameTimer->Start( 100, true );
3313 }
3314 }
3315
3316 m_lastOnSame = false;
0a71f9e9 3317 m_lineSelectSingleOnUp = (unsigned int)-1;
120b9b05 3318 }
e21f75bd
RR
3319 else
3320 {
3321 // This is necessary, because after a DnD operation in
3322 // from and to ourself, the up event is swallowed by the
3323 // DnD code. So on next non-up event (which means here and
3324 // now) m_lineSelectSingleOnUp should be reset.
0a71f9e9 3325 m_lineSelectSingleOnUp = (unsigned int)-1;
e21f75bd
RR
3326 }
3327
3328 if (event.RightDown())
3329 {
3330 m_lineBeforeLastClicked = m_lineLastClicked;
3331 m_lineLastClicked = current;
3332
3333 // If the item is already selected, do not update the selection.
3334 // Multi-selections should not be cleared if a selected item is clicked.
3335 if (!IsRowSelected(current))
3336 {
3337 SelectAllRows(false);
3338 ChangeCurrentRow(current);
3339 SelectRow(m_currentRow,true);
3340 }
3341
3342 // notify cell about right click
3343 // cell->...
120b9b05 3344
e21f75bd
RR
3345 // Allow generation of context menu event
3346 event.Skip();
3347 }
3348 else if (event.MiddleDown())
3349 {
3350 // notify cell about middle click
3351 // cell->...
3352 }
3353 if (event.LeftDown() || forceClick)
0fcce6b9 3354 {
72664514
RR
3355#ifdef __WXMSW__
3356 SetFocus();
3357#endif
120b9b05 3358
e21f75bd
RR
3359 m_lineBeforeLastClicked = m_lineLastClicked;
3360 m_lineLastClicked = current;
3361
0a71f9e9 3362 unsigned int oldCurrentRow = m_currentRow;
e21f75bd
RR
3363 bool oldWasSelected = IsRowSelected(m_currentRow);
3364
3365 bool cmdModifierDown = event.CmdDown();
3366 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
3367 {
3368 if ( IsSingleSel() || !IsRowSelected(current) )
3369 {
3370 SelectAllRows( false );
3371
3372 ChangeCurrentRow(current);
3373
3374 SelectRow(m_currentRow,true);
3375 }
3376 else // multi sel & current is highlighted & no mod keys
3377 {
3378 m_lineSelectSingleOnUp = current;
3379 ChangeCurrentRow(current); // change focus
3380 }
3381 }
3382 else // multi sel & either ctrl or shift is down
3383 {
3384 if (cmdModifierDown)
3385 {
3386 ChangeCurrentRow(current);
3387
3388 ReverseRowSelection(m_currentRow);
3389 }
3390 else if (event.ShiftDown())
3391 {
3392 ChangeCurrentRow(current);
3393
0a71f9e9 3394 unsigned int lineFrom = oldCurrentRow,
e21f75bd
RR
3395 lineTo = current;
3396
3397 if ( lineTo < lineFrom )
3398 {
3399 lineTo = lineFrom;
3400 lineFrom = m_currentRow;
3401 }
3402
3403 SelectRows(lineFrom, lineTo, true);
3404 }
3405 else // !ctrl, !shift
3406 {
3407 // test in the enclosing if should make it impossible
3408 wxFAIL_MSG( _T("how did we get here?") );
3409 }
3410 }
3411
72664514
RR
3412 if (m_currentRow != oldCurrentRow)
3413 RefreshRow( oldCurrentRow );
e21f75bd 3414
0fcce6b9 3415 wxDataViewColumn *oldCurrentCol = m_currentCol;
120b9b05 3416
0fcce6b9
RR
3417 // Update selection here...
3418 m_currentCol = col;
120b9b05 3419
c741d33f 3420 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
87f0efe2 3421 (current == oldCurrentRow)) && oldWasSelected;
0fdc2321 3422 }
4ed7af08
RR
3423}
3424
3425void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
3426{
cab07038 3427 m_hasFocus = true;
120b9b05 3428
cab07038
RR
3429 if (HasCurrentRow())
3430 Refresh();
120b9b05 3431
cab07038
RR
3432 event.Skip();
3433}
3434
3435void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
3436{
3437 m_hasFocus = false;
120b9b05 3438
cab07038
RR
3439 if (HasCurrentRow())
3440 Refresh();
120b9b05 3441
4ed7af08
RR
3442 event.Skip();
3443}
3444
704c3490
RR
3445wxDataViewItem wxDataViewMainWindow::GetSelection()
3446{
3447 if( m_selection.GetCount() != 1 )
3448 return wxDataViewItem();
d47db7e0
RR
3449
3450 return GetItemByRow( m_selection.Item(0));
704c3490
RR
3451}
3452
4ed7af08
RR
3453//-----------------------------------------------------------------------------
3454// wxDataViewCtrl
3455//-----------------------------------------------------------------------------
3456
3457IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
3458
4b3feaa7
RR
3459BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
3460 EVT_SIZE(wxDataViewCtrl::OnSize)
3461END_EVENT_TABLE()
3462
4ed7af08
RR
3463wxDataViewCtrl::~wxDataViewCtrl()
3464{
3465 if (m_notifier)
3466 GetModel()->RemoveNotifier( m_notifier );
3467}
3468
3469void wxDataViewCtrl::Init()
3470{
3471 m_notifier = NULL;
3472}
3473
3474bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
f554a14b 3475 const wxPoint& pos, const wxSize& size,
4ed7af08
RR
3476 long style, const wxValidator& validator )
3477{
c741d33f 3478 if (!wxControl::Create( parent, id, pos, size,
87f0efe2 3479 style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
4b3feaa7
RR
3480 return false;
3481
4ed7af08 3482 Init();
f554a14b 3483
4ed7af08
RR
3484#ifdef __WXMAC__
3485 MacSetClipChildren( true ) ;
3486#endif
3487
f554a14b 3488 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
9861f022
RR
3489
3490 if (HasFlag(wxDV_NO_HEADER))
3491 m_headerArea = NULL;
3492 else
3493 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY );
f554a14b 3494
4ed7af08 3495 SetTargetWindow( m_clientArea );
f554a14b 3496
4ed7af08 3497 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
9861f022
RR
3498 if (m_headerArea)
3499 sizer->Add( m_headerArea, 0, wxGROW );
4ed7af08
RR
3500 sizer->Add( m_clientArea, 1, wxGROW );
3501 SetSizer( sizer );
3502
3503 return true;
3504}
3505
3506#ifdef __WXMSW__
3507WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
3508 WXWPARAM wParam,
3509 WXLPARAM lParam)
3510{
b910a8ad 3511 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
4ed7af08
RR
3512
3513#ifndef __WXWINCE__
3514 // we need to process arrows ourselves for scrolling
3515 if ( nMsg == WM_GETDLGCODE )
3516 {
3517 rc |= DLGC_WANTARROWS;
3518 }
3519#endif
3520
3521 return rc;
3522}
3523#endif
3524
f554a14b 3525void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
4ed7af08 3526{
4b3feaa7
RR
3527 // We need to override OnSize so that our scrolled
3528 // window a) does call Layout() to use sizers for
3529 // positioning the controls but b) does not query
3530 // the sizer for their size and use that for setting
3531 // the scrollable area as set that ourselves by
3532 // calling SetScrollbar() further down.
3533
3534 Layout();
3535
3536 AdjustScrollbars();
4ed7af08
RR
3537}
3538
aba9bfd0 3539bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
4ed7af08
RR
3540{
3541 if (!wxDataViewCtrlBase::AssociateModel( model ))
3542 return false;
3543
aba9bfd0 3544 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
4ed7af08 3545
f554a14b 3546 model->AddNotifier( m_notifier );
4ed7af08 3547
aba9bfd0
RR
3548 m_clientArea->BuildTree(model);
3549
4b3feaa7 3550 m_clientArea->UpdateDisplay();
f554a14b 3551
4ed7af08
RR
3552 return true;
3553}
3554
3555bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
3556{
3557 if (!wxDataViewCtrlBase::AppendColumn(col))
3558 return false;
f554a14b 3559
9861f022 3560 OnColumnChange();
4ed7af08
RR
3561 return true;
3562}
3563
9861f022
RR
3564void wxDataViewCtrl::OnColumnChange()
3565{
3566 if (m_headerArea)
3567 m_headerArea->UpdateDisplay();
3568
3569 m_clientArea->UpdateDisplay();
3570}
3b6280be
RR
3571
3572void wxDataViewCtrl::DoSetExpanderColumn()
3573{
3574 m_clientArea->UpdateDisplay();
3575}
3576
3577void wxDataViewCtrl::DoSetIndent()
3578{
3579 m_clientArea->UpdateDisplay();
3580}
3581
b7e9f8b1 3582//Selection code with wxDataViewItem as parameters
66e09788
RR
3583wxDataViewItem wxDataViewCtrl::GetSelection()
3584{
3585 return m_clientArea->GetSelection();
3586}
3587
b7e9f8b1
RR
3588int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
3589{
3590 sel.Empty();
3591 wxDataViewSelection selection = m_clientArea->GetSelections();
3592 int len = selection.GetCount();
3593 for( int i = 0; i < len; i ++)
3594 {
3595 unsigned int row = selection[i];
3596 sel.Add( m_clientArea->GetItemByRow( row ) );
3597 }
3598 return len;
3599}
3600
3601void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
3602{
3603 wxDataViewSelection selection(wxDataViewSelectionCmp) ;
3604 int len = sel.GetCount();
3605 for( int i = 0; i < len; i ++ )
3606 {
3607 int row = m_clientArea->GetRowByItem( sel[i] );
3608 if( row >= 0 )
3609 selection.Add( static_cast<unsigned int>(row) );
3610 }
3611 m_clientArea->SetSelections( selection );
3612}
3613
3614void wxDataViewCtrl::Select( const wxDataViewItem & item )
704c3490 3615{
b7e9f8b1
RR
3616 int row = m_clientArea->GetRowByItem( item );
3617 if( row >= 0 )
3618 m_clientArea->SelectRow(row, true);
704c3490 3619}
3b6280be 3620
b7e9f8b1 3621void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
6ff7eee7 3622{
b7e9f8b1
RR
3623 int row = m_clientArea->GetRowByItem( item );
3624 if( row >= 0 )
3625 m_clientArea->SelectRow(row, false);
6ff7eee7
RR
3626}
3627
b7e9f8b1 3628bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
6ff7eee7 3629{
b7e9f8b1
RR
3630 int row = m_clientArea->GetRowByItem( item );
3631 if( row >= 0 )
3632 {
3633 return m_clientArea->IsRowSelected(row);
3634 }
3635 return false;
6ff7eee7
RR
3636}
3637
b7e9f8b1
RR
3638//Selection code with row number as parameter
3639int wxDataViewCtrl::GetSelections( wxArrayInt & sel ) const
6ff7eee7 3640{
b7e9f8b1
RR
3641 sel.Empty();
3642 wxDataViewSelection selection = m_clientArea->GetSelections();
3643 int len = selection.GetCount();
3644 for( int i = 0; i < len; i ++)
3645 {
3646 unsigned int row = selection[i];
3647 sel.Add( row );
3648 }
3649 return len;
6ff7eee7 3650}
df387169 3651
b7e9f8b1 3652void wxDataViewCtrl::SetSelections( const wxArrayInt & sel )
fc211fe5 3653{
b7e9f8b1
RR
3654 wxDataViewSelection selection(wxDataViewSelectionCmp) ;
3655 int len = sel.GetCount();
3656 for( int i = 0; i < len; i ++ )
3657 {
3658 int row = sel[i];
3659 if( row >= 0 )
3660 selection.Add( static_cast<unsigned int>(row) );
3661 }
3662 m_clientArea->SetSelections( selection );
fc211fe5
RR
3663}
3664
b7e9f8b1 3665void wxDataViewCtrl::Select( int row )
6ff7eee7 3666{
b7e9f8b1
RR
3667 if( row >= 0 )
3668 m_clientArea->SelectRow( row, true );
3669}
df387169 3670
b7e9f8b1
RR
3671void wxDataViewCtrl::Unselect( int row )
3672{
3673 if( row >= 0 )
3674 m_clientArea->SelectRow(row, false);
3675}
3676
3677bool wxDataViewCtrl::IsSelected( int row ) const
3678{
3679 if( row >= 0 )
3680 return m_clientArea->IsRowSelected(row);
6ff7eee7
RR
3681 return false;
3682}
3683
b7e9f8b1 3684void wxDataViewCtrl::SelectRange( int from, int to )
6ff7eee7 3685{
b7e9f8b1
RR
3686 wxArrayInt sel;
3687 for( int i = from; i < to; i ++ )
3688 sel.Add( i );
3689 m_clientArea->Select(sel);
3690}
df387169 3691
b7e9f8b1
RR
3692void wxDataViewCtrl::UnselectRange( int from, int to )
3693{
3694 wxDataViewSelection sel = m_clientArea->GetSelections();
3695 for( int i = from; i < to; i ++ )
3696 if( sel.Index( i ) != wxNOT_FOUND )
3697 sel.Remove( i );
3698 m_clientArea->SetSelections(sel);
6ff7eee7
RR
3699}
3700
b7e9f8b1 3701void wxDataViewCtrl::SelectAll()
6ff7eee7 3702{
b7e9f8b1
RR
3703 m_clientArea->SelectAllRows(true);
3704}
df387169 3705
b7e9f8b1
RR
3706void wxDataViewCtrl::UnselectAll()
3707{
3708 m_clientArea->SelectAllRows(false);
6ff7eee7 3709}
b7e9f8b1
RR
3710
3711void wxDataViewCtrl::EnsureVisible( int row )
3712{
3713 m_clientArea->ScrollTo( row );
3714}
3715
66e09788 3716void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, wxDataViewColumn * column )
b7e9f8b1
RR
3717{
3718 int row = m_clientArea->GetRowByItem(item);
3719 if( row >= 0 )
3720 EnsureVisible(row);
3721}
3722
66e09788
RR
3723void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item, unsigned int & column ) const
3724{
3725 m_clientArea->HitTest(point, item, column);
3726}
3727
3728wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item, unsigned int column ) const
3729{
3730 return m_clientArea->GetItemRect(item, column);
3731}
3732
b7e9f8b1
RR
3733wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
3734{
3735 return m_clientArea->GetItemByRow( row );
3736}
3737
3738int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
3739{
3740 return m_clientArea->GetRowByItem( item );
3741}
3742
3743 #endif
4ed7af08
RR
3744 // !wxUSE_GENERICDATAVIEWCTRL
3745
f554a14b 3746#endif
4ed7af08 3747 // wxUSE_DATAVIEWCTRL