]> git.saurik.com Git - wxWidgets.git/blame - src/generic/datavgen.cpp
allow passing literal strings and results of c_str() to SetXXXLabels() methods too
[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 335 {
59e60167 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 {
59e60167 348 return m_open;
442c56e6 349 }
d47db7e0
RR
350
351 void ToggleOpen()
442c56e6 352 {
438fb233 353 int len = m_nodes.GetCount();
d47db7e0 354 int sum = 0;
59e60167 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 )
59e60167 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();
59e60167 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 549
59e60167 550 int RecalculateCount();
3b6280be 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 1366 m_imageList = new wxImageList( 16, 16 );
d3acc83a 1367 (void)Header_SetImageList((HWND) m_hWnd, m_imageList->GetHIMAGELIST());
0c8ed3eb 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;
59e60167 1537 m_vetoColumnDrag = true;
029458ce
RR
1538 }
1539 }
1540 else
309bd665
RR
1541 {
1542 // veto it!
59e60167 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 2054 //Make m_count = -1 will cause the class recaculate the real displaying number of rows.
59e60167 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;
59e60167 2257 indent = indent + m_lineHeight; //try to use the m_lineHeight as the expander space
1117d56f
RR
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
59e60167 2280 wxDELETE(node);
74123073 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:
59e60167
VZ
2375 DoJob() { }
2376 virtual ~DoJob() { }
1117d56f
RR
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 };
59e60167 2383 virtual int operator() ( wxDataViewTreeNode * node ) = 0;
1117d56f
RR
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 :
59e60167 2395 return true;
1117d56f
RR
2396 case DoJob::IGR:
2397 return false;
2398 case DoJob::CONT:
59e60167
VZ
2399 default:
2400 ;
1117d56f
RR
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
59e60167 2410 for(; i < len; i ++ )
1117d56f
RR
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 :
59e60167 2426 return true;
1117d56f
RR
2427 case DoJob::IGR:
2428 continue;
2429 case DoJob::CONT:
2430 default:
59e60167 2431 ;
1117d56f
RR
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();
59e60167 2519 for( int i = 0; i < len; i ++)
d47db7e0
RR
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
59e60167 2978 wxDELETE(node);
e170469f 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
59e60167 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
59e60167 3088 wxDELETE(node);
e170469f 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:
59e60167
VZ
3123 RowToItemJob( unsigned int row , int current ) { this->row = row; this->current = current;}
3124 virtual ~RowToItemJob() { }
aba9bfd0 3125
d5025dc0 3126 virtual int operator() ( wxDataViewTreeNode * node )
d47db7e0
RR
3127 {
3128 current ++;
3129 if( current == static_cast<int>(row))
9bcc8016 3130 {
59e60167 3131 ret = node->GetItem();
d47db7e0
RR
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 {
59e60167 3159 ret = wxDataViewItem( n );
d47db7e0
RR
3160 return DoJob::OK;
3161 }
3162 return DoJob::CONT;
3163 }
d5025dc0 3164 wxDataViewItem GetResult(){ return ret; }
aba9bfd0
RR
3165private:
3166 unsigned int row;
59e60167 3167 int current;
aba9bfd0
RR
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;
59e60167
VZ
3191 this->current = current;
3192 ret = NULL;
d47db7e0
RR
3193 parent = node;
3194 }
59e60167 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 {
59e60167 3202 ret = node;
d5025dc0 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 );
59e60167 3220 ret = new wxDataViewTreeNode( parent );
b7e9f8b1
RR
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 {
59e60167 3236 ret = new wxDataViewTreeNode( parent );
d47db7e0
RR
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;
59e60167 3247 int current;
3b6280be 3248 wxDataViewTreeNode * ret;
59e60167 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{
9657c197
RR
3277 if (IsVirtualList())
3278 return;
3279
3b6280be
RR
3280 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
3281 if( node != NULL )
3282 {
d5025dc0 3283 if( node->HasChildren())
66e09788 3284 {
d5025dc0 3285 if( !node->IsOpen())
3b6280be 3286 {
66e09788
RR
3287 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,node->GetItem());
3288 //Check if the user prevent expanding
3289 if( e.GetSkipped() )
3290 return;
b7fe2261 3291
d5025dc0 3292 node->ToggleOpen();
704c3490
RR
3293 //Here I build the children of current node
3294 if( node->GetChildrenNumber() == 0 )
24c4a50f
RR
3295 {
3296 SortPrepare();
74123073 3297 ::BuildTreeHelper(GetOwner()->GetModel(), node->GetItem(), node);
24c4a50f 3298 }
d5025dc0 3299 m_count = -1;
351461fc 3300 UpdateDisplay();
66e09788
RR
3301 //Send the expanded event
3302 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
351461fc
RR
3303 }
3304 else
3305 {
3306 SelectRow( row, false );
3307 SelectRow( row + 1, true );
3308 ChangeCurrentRow( row + 1 );
526e19e2 3309 SendSelectionChangedEvent( GetItemByRow(row+1));
3b6280be 3310 }
66e09788 3311 }
d47db7e0
RR
3312 else
3313 delete node;
3b6280be
RR
3314 }
3315}
3316
3317void wxDataViewMainWindow::OnCollapsing(unsigned int row)
3318{
9657c197
RR
3319 if (IsVirtualList())
3320 return;
3321
3b6280be 3322 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
d5025dc0 3323 if( node != NULL )
3b6280be 3324 {
d47db7e0
RR
3325 wxDataViewTreeNode * nd = node;
3326
3b6280be
RR
3327 if( node->HasChildren() && node->IsOpen() )
3328 {
66e09788
RR
3329 wxDataViewEvent e = SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem());
3330 if( e.GetSkipped() )
3331 return;
3b6280be
RR
3332 node->ToggleOpen();
3333 m_count = -1;
351461fc 3334 UpdateDisplay();
66e09788
RR
3335 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,nd->GetItem());
3336 }
3337 else
3338 {
3339 node = node->GetParent();
3340 if( node != NULL )
3341 {
59e60167 3342 int parent = GetRowByItem( node->GetItem() );
66e09788
RR
3343 if( parent >= 0 )
3344 {
3345 SelectRow( row, false);
3346 SelectRow(parent , true );
3347 ChangeCurrentRow( parent );
526e19e2 3348 SendSelectionChangedEvent( node->GetItem() );
66e09788
RR
3349 }
3350 }
3351 }
3352 if( !nd->HasChildren())
3353 delete nd;
3b6280be
RR
3354 }
3355}
3356
351461fc
RR
3357wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
3358{
3359 wxDataViewModel * model = GetOwner()->GetModel();
3360 if( model == NULL )
3361 return NULL;
3362
3363 //Compose the a parent-chain of the finding item
3364 ItemList list;
3365 list.DeleteContents( true );
3366 wxDataViewItem it( item );
3367 while( it.IsOk() )
3368 {
3369 wxDataViewItem * pItem = new wxDataViewItem( it );
3370 list.Insert( pItem );
3371 it = model->GetParent( it );
3372 }
3373
442c56e6 3374 //Find the item along the parent-chain.
351461fc 3375 //This algorithm is designed to speed up the node-finding method
351461fc 3376 wxDataViewTreeNode * node = m_root;
59e60167 3377 for( ItemList::const_iterator iter = list.begin(); iter !=list.end(); iter++ )
351461fc
RR
3378 {
3379 if( node->HasChildren() )
3380 {
3381 if( node->GetChildrenNumber() == 0 )
24c4a50f
RR
3382 {
3383 SortPrepare();
74123073 3384 ::BuildTreeHelper(model, node->GetItem(), node);
24c4a50f 3385 }
351461fc 3386
d47db7e0 3387 wxDataViewTreeNodes nodes = node->GetNodes();
d2c1ee8a
RR
3388 unsigned int i;
3389 bool found = false;
777f9cef 3390
d2c1ee8a 3391 for (i = 0; i < nodes.GetCount(); i ++)
d92cb015 3392 {
c899416d 3393 if (nodes[i]->GetItem() == (**iter))
b7fe2261 3394 {
d2c1ee8a
RR
3395 if (nodes[i]->GetItem() == item)
3396 return nodes[i];
777f9cef 3397
d92cb015 3398 node = nodes[i];
d2c1ee8a 3399 found = true;
d92cb015
RR
3400 break;
3401 }
3402 }
d2c1ee8a 3403 if (!found)
351461fc 3404 return NULL;
351461fc
RR
3405 }
3406 else
3407 return NULL;
3408 }
d2c1ee8a 3409 return NULL;
351461fc
RR
3410}
3411
a87b466d 3412void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column )
66e09788 3413{
fbda518c 3414 wxDataViewColumn *col = NULL;
66e09788
RR
3415 unsigned int cols = GetOwner()->GetColumnCount();
3416 unsigned int colnum = 0;
3417 unsigned int x_start = 0;
3418 int x, y;
3419 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
3420 for (x_start = 0; colnum < cols; colnum++)
3421 {
fbda518c 3422 col = GetOwner()->GetColumn(colnum);
66e09788
RR
3423 if (col->IsHidden())
3424 continue; // skip it!
3425
3426 unsigned int w = col->GetWidth();
3427 if (x_start+w >= (unsigned int)x)
3428 break;
3429
3430 x_start += w;
3431 }
3432
fbda518c 3433 column = col;
344ed1f3 3434 item = GetItemByRow( GetLineAt( y ) );
66e09788
RR
3435}
3436
fbda518c 3437wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column )
66e09788
RR
3438{
3439 int row = GetRowByItem(item);
344ed1f3
RR
3440 int y = GetLineStart( row );
3441 int h = GetLineHeight( m_lineHeight );
66e09788
RR
3442 int x = 0;
3443 wxDataViewColumn *col = NULL;
3444 for( int i = 0, cols = GetOwner()->GetColumnCount(); i < cols; i ++ )
3445 {
3446 col = GetOwner()->GetColumn( i );
3447 x += col->GetWidth();
fbda518c 3448 if( GetOwner()->GetColumn(i+1) == column )
66e09788
RR
3449 break;
3450 }
3451 int w = col->GetWidth();
3452 m_owner->CalcScrolledPosition( x, y, &x, &y );
3453 return wxRect(x, y, w, h);
3454}
3455
442c56e6 3456int wxDataViewMainWindow::RecalculateCount()
3b6280be 3457{
a3cc79d9
RR
3458 if (!m_root)
3459 {
3460 wxDataViewIndexListModel *list_model = (wxDataViewIndexListModel*) GetOwner()->GetModel();
f52984b8
RR
3461#ifndef __WXMAC__
3462 return list_model->GetLastIndex() + 1;
3463#else
3464 return list_model->GetLastIndex() - 1;
3465#endif
a3cc79d9
RR
3466 }
3467 else
3468 {
3469 return m_root->GetSubTreeCount();
3470 }
3b6280be
RR
3471}
3472
aba9bfd0
RR
3473class ItemToRowJob : public DoJob
3474{
3475public:
59e60167
VZ
3476 ItemToRowJob(const wxDataViewItem& item_, ItemList::const_iterator iter)
3477 : m_iter(iter),
3478 item(item_)
3479 {
3480 ret = -1;
3481 }
aba9bfd0 3482
b7e9f8b1 3483 //Maybe binary search will help to speed up this process
d5025dc0
RR
3484 virtual int operator() ( wxDataViewTreeNode * node)
3485 {
3486 ret ++;
3487 if( node->GetItem() == item )
d47db7e0 3488 {
d5025dc0 3489 return DoJob::OK;
d47db7e0 3490 }
d5025dc0 3491
c899416d 3492 if( node->GetItem() == **m_iter )
d47db7e0 3493 {
59e60167 3494 m_iter++;
d5025dc0 3495 return DoJob::CONT;
d47db7e0 3496 }
d5025dc0 3497 else
d47db7e0
RR
3498 {
3499 ret += node->GetSubTreeCount();
d5025dc0 3500 return DoJob::IGR;
d47db7e0 3501 }
442c56e6 3502
d5025dc0 3503 }
aba9bfd0 3504
d47db7e0
RR
3505 virtual int operator() ( void * n )
3506 {
3507 ret ++;
3508 if( n == item.GetID() )
3509 return DoJob::OK;
3510 return DoJob::CONT;
3511 }
3b6280be 3512 //the row number is begin from zero
59e60167
VZ
3513 int GetResult() { return ret -1; }
3514
aba9bfd0 3515private:
c899416d 3516 ItemList::const_iterator m_iter;
aba9bfd0
RR
3517 wxDataViewItem item;
3518 int ret;
442c56e6 3519
aba9bfd0
RR
3520};
3521
344ed1f3 3522int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
aba9bfd0 3523{
344ed1f3 3524 const wxDataViewModel * model = GetOwner()->GetModel();
d47db7e0 3525 if( model == NULL )
fbda518c 3526 return -1;
d47db7e0 3527
a3cc79d9 3528 if (!m_root)
d47db7e0 3529 {
a3cc79d9 3530 return wxPtrToUInt( item.GetID() );
d47db7e0 3531 }
a3cc79d9
RR
3532 else
3533 {
3534 if( !item.IsOk() )
3535 return -1;
3536
3537 //Compose the a parent-chain of the finding item
3538 ItemList list;
3539 wxDataViewItem * pItem = NULL;
3540 list.DeleteContents( true );
3541 wxDataViewItem it( item );
3542 while( it.IsOk() )
3543 {
3544 pItem = new wxDataViewItem( it );
3545 list.Insert( pItem );
3546 it = model->GetParent( it );
3547 }
3548 pItem = new wxDataViewItem( );
3549 list.Insert( pItem );
d47db7e0 3550
a3cc79d9
RR
3551 ItemToRowJob job( item, list.begin() );
3552 Walker(m_root , job );
3553 return job.GetResult();
3554 }
aba9bfd0
RR
3555}
3556
74123073 3557static void BuildTreeHelper( wxDataViewModel * model, wxDataViewItem & item, wxDataViewTreeNode * node)
aba9bfd0 3558{
351461fc 3559 if( !model->IsContainer( item ) )
59e60167 3560 return;
442c56e6 3561
c899416d
RR
3562 wxDataViewItemArray children;
3563 unsigned int num = model->GetChildren( item, children);
67be459b 3564 unsigned int index = 0;
c899416d 3565 while( index < num )
aba9bfd0 3566 {
c899416d 3567 if( model->IsContainer( children[index] ) )
d47db7e0
RR
3568 {
3569 wxDataViewTreeNode * n = new wxDataViewTreeNode( node );
c899416d 3570 n->SetItem(children[index]);
59e60167 3571 n->SetHasChildren( true );
d47db7e0
RR
3572 node->AddNode( n );
3573 }
3574 else
3575 {
c899416d 3576 node->AddLeaf( children[index].GetID() );
d47db7e0 3577 }
c899416d 3578 index ++;
aba9bfd0 3579 }
d47db7e0
RR
3580 node->SetSubTreeCount( num );
3581 wxDataViewTreeNode * n = node->GetParent();
3582 if( n != NULL)
3583 n->ChangeSubTreeCount(num);
3584
aba9bfd0
RR
3585}
3586
3587void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
3588{
51bdecff
RR
3589 DestroyTree();
3590
e39de702 3591 if (GetOwner()->GetModel()->IsVirtualListModel())
a3cc79d9 3592 {
59e60167 3593 m_count = -1;
a3cc79d9
RR
3594 return;
3595 }
3596
51bdecff
RR
3597 m_root = new wxDataViewTreeNode( NULL );
3598 m_root->SetHasChildren(true);
3599
aba9bfd0
RR
3600 //First we define a invalid item to fetch the top-level elements
3601 wxDataViewItem item;
66e09788 3602 SortPrepare();
3b6280be 3603 BuildTreeHelper( model, item, m_root);
59e60167 3604 m_count = -1;
aba9bfd0
RR
3605}
3606
74123073 3607static void DestroyTreeHelper( wxDataViewTreeNode * node )
aba9bfd0 3608{
d47db7e0 3609 if( node->GetNodeNumber() != 0 )
aba9bfd0 3610 {
d47db7e0 3611 int len = node->GetNodeNumber();
59e60167 3612 int i = 0;
74123073 3613 wxDataViewTreeNodes& nodes = node->GetNodes();
59e60167 3614 for(; i < len; i ++ )
aba9bfd0
RR
3615 {
3616 DestroyTreeHelper(nodes[i]);
3617 }
3618 }
3619 delete node;
3620}
3621
3622void wxDataViewMainWindow::DestroyTree()
3623{
344ed1f3 3624 if (!IsVirtualList())
51bdecff 3625 {
74123073 3626 ::DestroyTreeHelper(m_root);
51bdecff
RR
3627 m_count = 0;
3628 m_root = NULL;
3629 }
aba9bfd0
RR
3630}
3631
cab07038
RR
3632void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
3633{
f029f1d1
VS
3634 if ( HandleAsNavigationKey(event) )
3635 return;
cab07038
RR
3636
3637 // no item -> nothing to do
3638 if (!HasCurrentRow())
3639 {
3640 event.Skip();
3641 return;
3642 }
120b9b05 3643
cab07038
RR
3644 // don't use m_linesPerPage directly as it might not be computed yet
3645 const int pageSize = GetCountPerPage();
3646 wxCHECK_RET( pageSize, _T("should have non zero page size") );
3647
3648 switch ( event.GetKeyCode() )
3649 {
d37b709c
RR
3650 case WXK_RETURN:
3651 {
3652 if (m_currentRow > 0)
3653 {
3654 wxWindow *parent = GetParent();
3655 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
3656 le.SetItem( GetItemByRow(m_currentRow) );
3657 le.SetEventObject(parent);
3658 le.SetModel(GetOwner()->GetModel());
3659
3660 parent->GetEventHandler()->ProcessEvent(le);
3661 }
3662 break;
3663 }
cab07038
RR
3664 case WXK_UP:
3665 if ( m_currentRow > 0 )
3666 OnArrowChar( m_currentRow - 1, event );
3667 break;
3668
3669 case WXK_DOWN:
4a851b11 3670 if ( m_currentRow < GetRowCount() - 1 )
cab07038
RR
3671 OnArrowChar( m_currentRow + 1, event );
3672 break;
3b6280be
RR
3673 //Add the process for tree expanding/collapsing
3674 case WXK_LEFT:
9bcc8016
JS
3675 OnCollapsing(m_currentRow);
3676 break;
3677 case WXK_RIGHT:
3678 OnExpanding( m_currentRow);
3679 break;
cab07038
RR
3680 case WXK_END:
3681 if (!IsEmpty())
3682 OnArrowChar( GetRowCount() - 1, event );
3683 break;
3684
3685 case WXK_HOME:
3686 if (!IsEmpty())
3687 OnArrowChar( 0, event );
3688 break;
3689
3690 case WXK_PAGEUP:
3691 {
3692 int steps = pageSize - 1;
3693 int index = m_currentRow - steps;
3694 if (index < 0)
3695 index = 0;
3696
3697 OnArrowChar( index, event );
3698 }
3699 break;
3700
3701 case WXK_PAGEDOWN:
3702 {
3703 int steps = pageSize - 1;
0a71f9e9
RR
3704 unsigned int index = m_currentRow + steps;
3705 unsigned int count = GetRowCount();
cab07038
RR
3706 if ( index >= count )
3707 index = count - 1;
3708
3709 OnArrowChar( index, event );
3710 }
3711 break;
3712
3713 default:
3714 event.Skip();
3715 }
3716}
3717
4ed7af08
RR
3718void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
3719{
e21f75bd
RR
3720 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
3721 {
3722 // let the base handle mouse wheel events.
3723 event.Skip();
3724 return;
3725 }
3726
0fdc2321
RR
3727 int x = event.GetX();
3728 int y = event.GetY();
3729 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
0fdc2321 3730 wxDataViewColumn *col = NULL;
b7fe2261 3731
0fdc2321 3732 int xpos = 0;
9861f022 3733 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 3734 unsigned int i;
0fdc2321
RR
3735 for (i = 0; i < cols; i++)
3736 {
3737 wxDataViewColumn *c = GetOwner()->GetColumn( i );
9861f022
RR
3738 if (c->IsHidden())
3739 continue; // skip it!
3740
0fdc2321
RR
3741 if (x < xpos + c->GetWidth())
3742 {
3743 col = c;
3744 break;
3745 }
3746 xpos += c->GetWidth();
3747 }
f554a14b 3748 if (!col)
0fdc2321 3749 return;
f554a14b 3750
24c4a50f 3751 wxDataViewRenderer *cell = col->GetRenderer();
344ed1f3 3752 unsigned int current = GetLineAt( y );
e21f75bd
RR
3753 if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
3754 {
3755 // Unselect all if below the last row ?
3756 return;
3757 }
f554a14b 3758
24c4a50f
RR
3759 //Test whether the mouse is hovered on the tree item button
3760 bool hover = false;
344ed1f3 3761 if ((!IsVirtualList()) && (GetOwner()->GetExpanderColumn() == col))
24c4a50f
RR
3762 {
3763 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
3764 if( node!=NULL && node->HasChildren() )
3765 {
3766 int indent = node->GetIndentLevel();
3767 indent = GetOwner()->GetIndent()*indent;
777f9cef 3768 wxRect rect( xpos + indent + EXPANDER_MARGIN,
344ed1f3
RR
3769 GetLineStart( current ) + EXPANDER_MARGIN + (GetLineHeight(current)/2) - (m_lineHeight/2) - EXPANDER_OFFSET,
3770 m_lineHeight-2*EXPANDER_MARGIN,
981cd83e 3771 m_lineHeight-2*EXPANDER_MARGIN + EXPANDER_OFFSET);
24c4a50f
RR
3772 if( rect.Contains( x, y) )
3773 {
3774 //So the mouse is over the expander
3775 hover = true;
3776 if (m_underMouse && m_underMouse != node)
3777 {
3778 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
df2c23e7 3779 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
24c4a50f
RR
3780 }
3781 if (m_underMouse != node)
3782 {
3783 //wxLogMessage("Do the row: %d", current);
df2c23e7 3784 RefreshRow(current);
24c4a50f
RR
3785 }
3786 m_underMouse = node;
3787 }
3788 }
438fb233 3789 if (node!=NULL && !node->HasChildren())
24c4a50f
RR
3790 delete node;
3791 }
3792 if (!hover)
3793 {
3794 if (m_underMouse != NULL)
3795 {
526e19e2 3796 //wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
df2c23e7 3797 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
24c4a50f
RR
3798 m_underMouse = NULL;
3799 }
3800 }
3801
aba9bfd0 3802 wxDataViewModel *model = GetOwner()->GetModel();
0fdc2321 3803
e21f75bd
RR
3804 if (event.Dragging())
3805 {
3806 if (m_dragCount == 0)
3807 {
3808 // we have to report the raw, physical coords as we want to be
3809 // able to call HitTest(event.m_pointDrag) from the user code to
3810 // get the item being dragged
3811 m_dragStart = event.GetPosition();
3812 }
3813
3814 m_dragCount++;
3815
3816 if (m_dragCount != 3)
3817 return;
3818
3819 if (event.LeftIsDown())
3820 {
3821 // Notify cell about drag
3822 }
3823 return;
3824 }
3825 else
3826 {
3827 m_dragCount = 0;
3828 }
3829
3830 bool forceClick = false;
3831
0fcce6b9
RR
3832 if (event.ButtonDClick())
3833 {
3834 m_renameTimer->Stop();
3835 m_lastOnSame = false;
3836 }
3837
438fb233
RR
3838 wxDataViewItem item = GetItemByRow(current);
3839 bool ignore_other_columns =
3840 ((GetOwner()->GetExpanderColumn() != col) &&
3841 (model->IsContainer(item)) &&
3842 (!model->HasContainerColumns(item)));
b5ec7dd6 3843
0fdc2321
RR
3844 if (event.LeftDClick())
3845 {
e21f75bd 3846 if ( current == m_lineLastClicked )
0fdc2321 3847 {
438fb233 3848 if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE))
e21f75bd
RR
3849 {
3850 wxVariant value;
6cdcbce3 3851 model->GetValue( value, item, col->GetModelColumn() );
e21f75bd 3852 cell->SetValue( value );
344ed1f3
RR
3853 wxRect cell_rect( xpos, GetLineStart( current ),
3854 col->GetWidth(), GetLineHeight( current ) );
6cdcbce3 3855 cell->Activate( cell_rect, model, item, col->GetModelColumn() );
b7e9f8b1 3856
2c3f6edd
RR
3857 }
3858 else
3859 {
b7e9f8b1
RR
3860 wxWindow *parent = GetParent();
3861 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
45c156e0 3862 le.SetItem( item );
b7e9f8b1 3863 le.SetEventObject(parent);
b7e9f8b1
RR
3864 le.SetModel(GetOwner()->GetModel());
3865
3866 parent->GetEventHandler()->ProcessEvent(le);
e21f75bd
RR
3867 }
3868 return;
3869 }
3870 else
3871 {
3872 // The first click was on another item, so don't interpret this as
3873 // a double click, but as a simple click instead
3874 forceClick = true;
0fdc2321 3875 }
120b9b05 3876 }
f554a14b 3877
0fcce6b9
RR
3878 if (event.LeftUp())
3879 {
0a71f9e9 3880 if (m_lineSelectSingleOnUp != (unsigned int)-1)
e21f75bd
RR
3881 {
3882 // select single line
3883 SelectAllRows( false );
3884 SelectRow( m_lineSelectSingleOnUp, true );
3885 }
120b9b05 3886
3b6280be 3887 //Process the event of user clicking the expander
d5025dc0 3888 bool expander = false;
344ed1f3 3889 if ((!IsVirtualList()) && (GetOwner()->GetExpanderColumn() == col))
d5025dc0 3890 {
9bcc8016
JS
3891 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
3892 if( node!=NULL && node->HasChildren() )
3893 {
3894 int indent = node->GetIndentLevel();
3895 indent = GetOwner()->GetIndent()*indent;
777f9cef 3896 wxRect rect( xpos + indent + EXPANDER_MARGIN,
344ed1f3
RR
3897 GetLineStart( current ) + EXPANDER_MARGIN + (GetLineHeight(current)/2) - (m_lineHeight/2) - EXPANDER_OFFSET,
3898 m_lineHeight-2*EXPANDER_MARGIN,
981cd83e 3899 m_lineHeight-2*EXPANDER_MARGIN + EXPANDER_OFFSET);
777f9cef 3900
9bcc8016
JS
3901 if( rect.Contains( x, y) )
3902 {
3903 expander = true;
3904 if( node->IsOpen() )
3905 OnCollapsing(current);
3906 else
3907 OnExpanding( current );
3908 }
3909 }
74123073
RR
3910 if (node && !node->HasChildren())
3911 delete node;
d5025dc0 3912 }
3b6280be 3913 //If the user click the expander, we do not do editing even if the column with expander are editable
438fb233 3914 if (m_lastOnSame && !expander && !ignore_other_columns)
0fcce6b9 3915 {
a8461d31 3916 if ((col == m_currentCol) && (current == m_currentRow) &&
0bdfa388 3917 (cell->GetMode() & wxDATAVIEW_CELL_EDITABLE) )
0fcce6b9
RR
3918 {
3919 m_renameTimer->Start( 100, true );
3920 }
3921 }
3922
3923 m_lastOnSame = false;
0a71f9e9 3924 m_lineSelectSingleOnUp = (unsigned int)-1;
120b9b05 3925 }
e21f75bd
RR
3926 else
3927 {
3928 // This is necessary, because after a DnD operation in
3929 // from and to ourself, the up event is swallowed by the
3930 // DnD code. So on next non-up event (which means here and
3931 // now) m_lineSelectSingleOnUp should be reset.
0a71f9e9 3932 m_lineSelectSingleOnUp = (unsigned int)-1;
e21f75bd
RR
3933 }
3934
3935 if (event.RightDown())
3936 {
3937 m_lineBeforeLastClicked = m_lineLastClicked;
3938 m_lineLastClicked = current;
3939
3940 // If the item is already selected, do not update the selection.
3941 // Multi-selections should not be cleared if a selected item is clicked.
3942 if (!IsRowSelected(current))
3943 {
3944 SelectAllRows(false);
3945 ChangeCurrentRow(current);
3946 SelectRow(m_currentRow,true);
526e19e2 3947 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
e21f75bd
RR
3948 }
3949
f210b1b5
RR
3950 wxVariant value;
3951 model->GetValue( value, item, col->GetModelColumn() );
0bdfa388
RR
3952 wxWindow *parent = GetParent();
3953 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
3954 le.SetItem( item );
3955 le.SetEventObject(parent);
3956 le.SetModel(GetOwner()->GetModel());
3957 le.SetValue(value);
3958 parent->GetEventHandler()->ProcessEvent(le);
e21f75bd
RR
3959 }
3960 else if (event.MiddleDown())
3961 {
e21f75bd
RR
3962 }
3963 if (event.LeftDown() || forceClick)
0fcce6b9 3964 {
72664514 3965 SetFocus();
120b9b05 3966
e21f75bd
RR
3967 m_lineBeforeLastClicked = m_lineLastClicked;
3968 m_lineLastClicked = current;
3969
0a71f9e9 3970 unsigned int oldCurrentRow = m_currentRow;
e21f75bd
RR
3971 bool oldWasSelected = IsRowSelected(m_currentRow);
3972
3973 bool cmdModifierDown = event.CmdDown();
3974 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
3975 {
3976 if ( IsSingleSel() || !IsRowSelected(current) )
3977 {
3978 SelectAllRows( false );
e21f75bd 3979 ChangeCurrentRow(current);
e21f75bd 3980 SelectRow(m_currentRow,true);
526e19e2 3981 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
e21f75bd
RR
3982 }
3983 else // multi sel & current is highlighted & no mod keys
3984 {
3985 m_lineSelectSingleOnUp = current;
3986 ChangeCurrentRow(current); // change focus
3987 }
3988 }
3989 else // multi sel & either ctrl or shift is down
3990 {
3991 if (cmdModifierDown)
3992 {
3993 ChangeCurrentRow(current);
e21f75bd 3994 ReverseRowSelection(m_currentRow);
526e19e2 3995 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
e21f75bd
RR
3996 }
3997 else if (event.ShiftDown())
3998 {
3999 ChangeCurrentRow(current);
4000
0a71f9e9 4001 unsigned int lineFrom = oldCurrentRow,
e21f75bd
RR
4002 lineTo = current;
4003
4004 if ( lineTo < lineFrom )
4005 {
4006 lineTo = lineFrom;
4007 lineFrom = m_currentRow;
4008 }
4009
4010 SelectRows(lineFrom, lineTo, true);
526e19e2 4011 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
e21f75bd
RR
4012 }
4013 else // !ctrl, !shift
4014 {
4015 // test in the enclosing if should make it impossible
4016 wxFAIL_MSG( _T("how did we get here?") );
4017 }
4018 }
777f9cef 4019
72664514
RR
4020 if (m_currentRow != oldCurrentRow)
4021 RefreshRow( oldCurrentRow );
e21f75bd 4022
0fcce6b9 4023 wxDataViewColumn *oldCurrentCol = m_currentCol;
120b9b05 4024
0fcce6b9
RR
4025 // Update selection here...
4026 m_currentCol = col;
120b9b05 4027
c741d33f 4028 m_lastOnSame = !forceClick && ((col == oldCurrentCol) &&
87f0efe2 4029 (current == oldCurrentRow)) && oldWasSelected;
0bdfa388
RR
4030
4031 // Call LeftClick after everything else as under GTK+
4032 if (cell->GetMode() & wxDATAVIEW_CELL_ACTIVATABLE)
4033 {
4034 // notify cell about right click
4035 wxVariant value;
4036 model->GetValue( value, item, col->GetModelColumn() );
4037 cell->SetValue( value );
344ed1f3
RR
4038 wxRect cell_rect( xpos, GetLineStart( current ),
4039 col->GetWidth(), GetLineHeight( current ) );
0bdfa388
RR
4040 /* ignore ret */ cell->LeftClick( event.GetPosition(), cell_rect, model, item, col->GetModelColumn());
4041 }
0fdc2321 4042 }
4ed7af08
RR
4043}
4044
4045void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
4046{
cab07038 4047 m_hasFocus = true;
120b9b05 4048
cab07038
RR
4049 if (HasCurrentRow())
4050 Refresh();
120b9b05 4051
cab07038
RR
4052 event.Skip();
4053}
4054
4055void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
4056{
4057 m_hasFocus = false;
120b9b05 4058
cab07038
RR
4059 if (HasCurrentRow())
4060 Refresh();
120b9b05 4061
4ed7af08
RR
4062 event.Skip();
4063}
4064
fbda518c 4065wxDataViewItem wxDataViewMainWindow::GetSelection() const
704c3490
RR
4066{
4067 if( m_selection.GetCount() != 1 )
4068 return wxDataViewItem();
d47db7e0
RR
4069
4070 return GetItemByRow( m_selection.Item(0));
704c3490
RR
4071}
4072
4ed7af08
RR
4073//-----------------------------------------------------------------------------
4074// wxDataViewCtrl
4075//-----------------------------------------------------------------------------
a76c2f37 4076WX_DEFINE_LIST(wxDataViewColumnList)
4ed7af08
RR
4077
4078IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
4079
4b3feaa7
RR
4080BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
4081 EVT_SIZE(wxDataViewCtrl::OnSize)
4082END_EVENT_TABLE()
4083
4ed7af08
RR
4084wxDataViewCtrl::~wxDataViewCtrl()
4085{
4086 if (m_notifier)
4087 GetModel()->RemoveNotifier( m_notifier );
74123073 4088
b3a3c9d8 4089 m_cols.Clear();
4ed7af08
RR
4090}
4091
4092void wxDataViewCtrl::Init()
4093{
b3a3c9d8 4094 m_cols.DeleteContents(true);
4ed7af08
RR
4095 m_notifier = NULL;
4096}
4097
4098bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
f554a14b 4099 const wxPoint& pos, const wxSize& size,
4ed7af08
RR
4100 long style, const wxValidator& validator )
4101{
008e999e
RD
4102 if ( (style & wxBORDER_MASK) == 0)
4103 style |= wxBORDER_SUNKEN;
777f9cef 4104
c741d33f 4105 if (!wxControl::Create( parent, id, pos, size,
008e999e 4106 style | wxScrolledWindowStyle, validator))
4b3feaa7
RR
4107 return false;
4108
b89cac3f 4109 SetInitialSize(size);
b5ec7dd6 4110
4ed7af08 4111 Init();
f554a14b 4112
4ed7af08 4113#ifdef __WXMAC__
59e60167 4114 MacSetClipChildren( true );
4ed7af08
RR
4115#endif
4116
f554a14b 4117 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
9861f022
RR
4118
4119 if (HasFlag(wxDV_NO_HEADER))
4120 m_headerArea = NULL;
4121 else
4122 m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY );
f554a14b 4123
4ed7af08 4124 SetTargetWindow( m_clientArea );
f554a14b 4125
4ed7af08 4126 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
9861f022
RR
4127 if (m_headerArea)
4128 sizer->Add( m_headerArea, 0, wxGROW );
4ed7af08
RR
4129 sizer->Add( m_clientArea, 1, wxGROW );
4130 SetSizer( sizer );
b5ec7dd6 4131
4ed7af08
RR
4132 return true;
4133}
4134
4135#ifdef __WXMSW__
4136WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
4137 WXWPARAM wParam,
4138 WXLPARAM lParam)
4139{
b910a8ad 4140 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
4ed7af08
RR
4141
4142#ifndef __WXWINCE__
4143 // we need to process arrows ourselves for scrolling
4144 if ( nMsg == WM_GETDLGCODE )
4145 {
4146 rc |= DLGC_WANTARROWS;
4147 }
4148#endif
4149
4150 return rc;
4151}
4152#endif
4153
f554a14b 4154void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
4ed7af08 4155{
4b3feaa7
RR
4156 // We need to override OnSize so that our scrolled
4157 // window a) does call Layout() to use sizers for
4158 // positioning the controls but b) does not query
4159 // the sizer for their size and use that for setting
4160 // the scrollable area as set that ourselves by
4161 // calling SetScrollbar() further down.
4162
4163 Layout();
4164
4165 AdjustScrollbars();
4ed7af08
RR
4166}
4167
aba9bfd0 4168bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
4ed7af08
RR
4169{
4170 if (!wxDataViewCtrlBase::AssociateModel( model ))
4171 return false;
4172
aba9bfd0 4173 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
4ed7af08 4174
f554a14b 4175 model->AddNotifier( m_notifier );
4ed7af08 4176
51bdecff 4177 m_clientArea->DestroyTree();
cfa42cb8 4178
aba9bfd0
RR
4179 m_clientArea->BuildTree(model);
4180
4b3feaa7 4181 m_clientArea->UpdateDisplay();
f554a14b 4182
4ed7af08
RR
4183 return true;
4184}
4185
4186bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
4187{
4188 if (!wxDataViewCtrlBase::AppendColumn(col))
4189 return false;
f554a14b 4190
afebb87b 4191 m_cols.Append( col );
9861f022 4192 OnColumnChange();
736fe67c
RR
4193 return true;
4194}
4195
4196bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
4197{
4198 if (!wxDataViewCtrlBase::PrependColumn(col))
4199 return false;
4200
4201 m_cols.Insert( col );
4202 OnColumnChange();
4ed7af08
RR
4203 return true;
4204}
4205
19723525
RR
4206bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
4207{
4208 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
4209 return false;
4210
4211 m_cols.Insert( pos, col );
4212 OnColumnChange();
4213 return true;
4214}
4215
9861f022
RR
4216void wxDataViewCtrl::OnColumnChange()
4217{
4218 if (m_headerArea)
4219 m_headerArea->UpdateDisplay();
4220
4221 m_clientArea->UpdateDisplay();
4222}
3b6280be
RR
4223
4224void wxDataViewCtrl::DoSetExpanderColumn()
4225{
4226 m_clientArea->UpdateDisplay();
4227}
4228
4229void wxDataViewCtrl::DoSetIndent()
4230{
4231 m_clientArea->UpdateDisplay();
4232}
4233
afebb87b
RR
4234unsigned int wxDataViewCtrl::GetColumnCount() const
4235{
4236 return m_cols.GetCount();
4237}
4238
4239wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int pos ) const
4240{
4241 wxDataViewColumnList::const_iterator iter;
67be459b 4242 unsigned int i = 0;
afebb87b
RR
4243 for (iter = m_cols.begin(); iter!=m_cols.end(); iter++)
4244 {
4245 if (i == pos)
4246 return *iter;
4247
4248 if ((*iter)->IsHidden())
4249 continue;
4250 i ++;
4251 }
4252 return NULL;
4253}
4254
fc8c1a15
RR
4255void wxDataViewCtrl::ColumnMoved( wxDataViewColumn* col, unsigned int new_pos )
4256{
4257 if (new_pos > m_cols.GetCount()) return;
59e60167 4258
09958a80
RR
4259 // Exchange position
4260 m_cols.DeleteContents(false);
fc8c1a15
RR
4261 m_cols.DeleteObject( col );
4262 m_cols.Insert( new_pos, col );
09958a80 4263 m_cols.DeleteContents(true);
cfa42cb8 4264
fc8c1a15
RR
4265 m_clientArea->UpdateDisplay();
4266}
4267
afebb87b
RR
4268bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
4269{
b3a3c9d8 4270 wxDataViewColumnList::compatibility_iterator ret = m_cols.Find( column );
4264606e 4271 if (!ret)
afebb87b
RR
4272 return false;
4273
b7fe2261 4274 m_cols.Erase(ret);
afebb87b
RR
4275 OnColumnChange();
4276
4277 return true;
4278}
4279
4280bool wxDataViewCtrl::ClearColumns()
4281{
b3a3c9d8 4282 m_cols.Clear();
afebb87b
RR
4283 OnColumnChange();
4284 return true;
4285}
4286
453091c2
RR
4287int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
4288{
526e19e2
RR
4289 int ret = 0, dead = 0;
4290 int len = GetColumnCount();
4291 for (int i=0; i<len; i++)
4292 {
4293 wxDataViewColumn * col = GetColumn(i);
4294 if (col->IsHidden())
4295 continue;
4296 ret += col->GetWidth();
4297 if (column==col)
4298 {
4299 CalcScrolledPosition( ret, dead, &ret, &dead );
4300 break;
4301 }
4302 }
4303 return ret;
453091c2
RR
4304}
4305
21f47fb9
RR
4306wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
4307{
4308 return NULL;
4309}
4310
b7e9f8b1 4311//Selection code with wxDataViewItem as parameters
fbda518c 4312wxDataViewItem wxDataViewCtrl::GetSelection() const
66e09788
RR
4313{
4314 return m_clientArea->GetSelection();
4315}
4316
b7e9f8b1
RR
4317int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
4318{
4319 sel.Empty();
4320 wxDataViewSelection selection = m_clientArea->GetSelections();
4321 int len = selection.GetCount();
4322 for( int i = 0; i < len; i ++)
4323 {
4324 unsigned int row = selection[i];
4325 sel.Add( m_clientArea->GetItemByRow( row ) );
4326 }
4327 return len;
4328}
4329
4330void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
4331{
59e60167 4332 wxDataViewSelection selection(wxDataViewSelectionCmp);
b7e9f8b1
RR
4333 int len = sel.GetCount();
4334 for( int i = 0; i < len; i ++ )
4335 {
4336 int row = m_clientArea->GetRowByItem( sel[i] );
4337 if( row >= 0 )
4338 selection.Add( static_cast<unsigned int>(row) );
4339 }
4340 m_clientArea->SetSelections( selection );
4341}
4342
4343void wxDataViewCtrl::Select( const wxDataViewItem & item )
704c3490 4344{
b7e9f8b1
RR
4345 int row = m_clientArea->GetRowByItem( item );
4346 if( row >= 0 )
57f2a652
RR
4347 {
4348 //Unselect all rows before select another in the single select mode
4349 if (m_clientArea->IsSingleSel())
4350 m_clientArea->SelectAllRows(false);
b7e9f8b1 4351 m_clientArea->SelectRow(row, true);
57f2a652 4352 }
704c3490 4353}
3b6280be 4354
b7e9f8b1 4355void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
6ff7eee7 4356{
b7e9f8b1
RR
4357 int row = m_clientArea->GetRowByItem( item );
4358 if( row >= 0 )
4359 m_clientArea->SelectRow(row, false);
6ff7eee7
RR
4360}
4361
b7e9f8b1 4362bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
6ff7eee7 4363{
b7e9f8b1
RR
4364 int row = m_clientArea->GetRowByItem( item );
4365 if( row >= 0 )
4366 {
4367 return m_clientArea->IsRowSelected(row);
4368 }
4369 return false;
6ff7eee7
RR
4370}
4371
b7e9f8b1
RR
4372//Selection code with row number as parameter
4373int wxDataViewCtrl::GetSelections( wxArrayInt & sel ) const
6ff7eee7 4374{
b7e9f8b1
RR
4375 sel.Empty();
4376 wxDataViewSelection selection = m_clientArea->GetSelections();
4377 int len = selection.GetCount();
4378 for( int i = 0; i < len; i ++)
4379 {
4380 unsigned int row = selection[i];
4381 sel.Add( row );
4382 }
4383 return len;
6ff7eee7 4384}
df387169 4385
b7e9f8b1 4386void wxDataViewCtrl::SetSelections( const wxArrayInt & sel )
fc211fe5 4387{
59e60167 4388 wxDataViewSelection selection(wxDataViewSelectionCmp);
b7e9f8b1
RR
4389 int len = sel.GetCount();
4390 for( int i = 0; i < len; i ++ )
4391 {
4392 int row = sel[i];
4393 if( row >= 0 )
4394 selection.Add( static_cast<unsigned int>(row) );
4395 }
4396 m_clientArea->SetSelections( selection );
fc211fe5
RR
4397}
4398
b7e9f8b1 4399void wxDataViewCtrl::Select( int row )
6ff7eee7 4400{
b7e9f8b1 4401 if( row >= 0 )
57f2a652
RR
4402 {
4403 if (m_clientArea->IsSingleSel())
4404 m_clientArea->SelectAllRows(false);
b7e9f8b1 4405 m_clientArea->SelectRow( row, true );
57f2a652 4406 }
b7e9f8b1 4407}
df387169 4408
b7e9f8b1
RR
4409void wxDataViewCtrl::Unselect( int row )
4410{
4411 if( row >= 0 )
4412 m_clientArea->SelectRow(row, false);
4413}
4414
4415bool wxDataViewCtrl::IsSelected( int row ) const
4416{
4417 if( row >= 0 )
4418 return m_clientArea->IsRowSelected(row);
6ff7eee7
RR
4419 return false;
4420}
4421
b7e9f8b1 4422void wxDataViewCtrl::SelectRange( int from, int to )
6ff7eee7 4423{
b7fe2261 4424 wxArrayInt sel;
b7e9f8b1
RR
4425 for( int i = from; i < to; i ++ )
4426 sel.Add( i );
4427 m_clientArea->Select(sel);
4428}
df387169 4429
b7e9f8b1
RR
4430void wxDataViewCtrl::UnselectRange( int from, int to )
4431{
4432 wxDataViewSelection sel = m_clientArea->GetSelections();
4433 for( int i = from; i < to; i ++ )
4434 if( sel.Index( i ) != wxNOT_FOUND )
4435 sel.Remove( i );
4436 m_clientArea->SetSelections(sel);
6ff7eee7
RR
4437}
4438
b7e9f8b1 4439void wxDataViewCtrl::SelectAll()
6ff7eee7 4440{
b7e9f8b1
RR
4441 m_clientArea->SelectAllRows(true);
4442}
df387169 4443
b7e9f8b1
RR
4444void wxDataViewCtrl::UnselectAll()
4445{
4446 m_clientArea->SelectAllRows(false);
6ff7eee7 4447}
b7e9f8b1 4448
fbda518c 4449void wxDataViewCtrl::EnsureVisible( int row, int column )
b7e9f8b1 4450{
fbda518c
RR
4451 if( row < 0 )
4452 row = 0;
67be459b 4453 if( row > (int) m_clientArea->GetRowCount() )
fbda518c
RR
4454 row = m_clientArea->GetRowCount();
4455
4456 int first = m_clientArea->GetFirstVisibleRow();
4457 int last = m_clientArea->GetLastVisibleRow();
4458 if( row < first )
4459 m_clientArea->ScrollTo( row, column );
4460 else if( row > last )
4461 m_clientArea->ScrollTo( row - last + first, column );
4462 else
4463 m_clientArea->ScrollTo( first, column );
b7e9f8b1
RR
4464}
4465
fbda518c 4466void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataViewColumn * column )
b7e9f8b1
RR
4467{
4468 int row = m_clientArea->GetRowByItem(item);
4469 if( row >= 0 )
fbda518c
RR
4470 {
4471 if( column == NULL )
9bcc8016 4472 EnsureVisible(row, -1);
fbda518c 4473 else
b7fe2261 4474 {
fbda518c
RR
4475 int col = 0;
4476 int len = GetColumnCount();
4477 for( int i = 0; i < len; i ++ )
4478 if( GetColumn(i) == column )
4479 {
4480 col = i;
4481 break;
4482 }
4483 EnsureVisible( row, col );
4484 }
4485 }
b7fe2261 4486
b7e9f8b1
RR
4487}
4488
a87b466d 4489void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column ) const
66e09788
RR
4490{
4491 m_clientArea->HitTest(point, item, column);
4492}
4493
fbda518c 4494wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column ) const
66e09788
RR
4495{
4496 return m_clientArea->GetItemRect(item, column);
4497}
4498
b7e9f8b1
RR
4499wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
4500{
4501 return m_clientArea->GetItemByRow( row );
4502}
4503
4504int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
4505{
4506 return m_clientArea->GetRowByItem( item );
4507}
4508
afebb87b
RR
4509void wxDataViewCtrl::Expand( const wxDataViewItem & item )
4510{
4511 int row = m_clientArea->GetRowByItem( item );
4512 if (row != -1)
4513 m_clientArea->Expand(row);
4514}
4515
4516void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
4517{
4518 int row = m_clientArea->GetRowByItem( item );
4519 if (row != -1)
b7fe2261 4520 m_clientArea->Collapse(row);
afebb87b
RR
4521}
4522
b7e9f8b1 4523 #endif
4ed7af08
RR
4524 // !wxUSE_GENERICDATAVIEWCTRL
4525
f554a14b 4526#endif
4ed7af08 4527 // wxUSE_DATAVIEWCTRL