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