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