]> git.saurik.com Git - wxWidgets.git/blame - src/generic/datavgen.cpp
Add wxTextEntryDialog::SetMaxLength().
[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"
818d91a9 37 #include "wx/frame.h"
f554a14b
WS
38#endif
39
4ed7af08 40#include "wx/stockitem.h"
4ed7af08 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"
e2bfe673 48#include "wx/headerctrl.h"
592883ed 49#include "wx/dnd.h"
0645bd38 50#include "wx/stopwatch.h"
64ac3db8 51#include "wx/weakref.h"
4ed7af08 52
4ed7af08
RR
53//-----------------------------------------------------------------------------
54// classes
55//-----------------------------------------------------------------------------
56
5d9e1605
RR
57class wxDataViewColumn;
58class wxDataViewHeaderWindow;
4ed7af08
RR
59class wxDataViewCtrl;
60
5d9e1605
RR
61//-----------------------------------------------------------------------------
62// classes
63//-----------------------------------------------------------------------------
64
9861f022
RR
65static const int SCROLL_UNIT_X = 15;
66
67// the cell padding on the left/right
68static const int PADDING_RIGHTLEFT = 3;
69
3b6280be
RR
70// the expander space margin
71static const int EXPANDER_MARGIN = 4;
9861f022 72
344ed1f3
RR
73#ifdef __WXMSW__
74static const int EXPANDER_OFFSET = 4;
75#else
76static const int EXPANDER_OFFSET = 1;
77#endif
78
bc4f1ff2 79// Below is the compare stuff.
4cef3873 80// For the generic implementation, both the leaf nodes and the nodes are sorted for
bc4f1ff2
FM
81// fast search when needed
82static wxDataViewModel* g_model;
c4864c04
VZ
83
84// The column is either the index of the column to be used for sorting or one
85// of the special values in this enum:
86enum
87{
0c5a056a
VZ
88 // Sort when we're thawed later.
89 SortColumn_OnThaw = -3,
90
c4864c04
VZ
91 // Don't sort at all.
92 SortColumn_None = -2,
93
94 // Sort using the model default sort order.
95 SortColumn_Default = -1
96};
97
98static int g_column = SortColumn_None;
24c4a50f 99static bool g_asending = true;
57f2a652 100
1841f079
VZ
101// ----------------------------------------------------------------------------
102// helper functions
103// ----------------------------------------------------------------------------
104
105namespace
106{
107
108// Return the expander column or, if it is not set, the first column and also
109// set it as the expander one for the future.
110wxDataViewColumn* GetExpanderColumnOrFirstOne(wxDataViewCtrl* dataview)
111{
112 wxDataViewColumn* expander = dataview->GetExpanderColumn();
113 if (!expander)
114 {
115 // TODO-RTL: last column for RTL support
116 expander = dataview->GetColumnAt( 0 );
117 dataview->SetExpanderColumn(expander);
118 }
119
120 return expander;
121}
122
123} // anonymous namespace
124
5d9e1605
RR
125//-----------------------------------------------------------------------------
126// wxDataViewColumn
127//-----------------------------------------------------------------------------
128
129void wxDataViewColumn::Init(int width, wxAlignment align, int flags)
130{
d0154e3a 131 m_width = width;
5d9e1605
RR
132 m_minWidth = 0;
133 m_align = align;
134 m_flags = flags;
135 m_sort = false;
136 m_sortAscending = true;
137}
ce00f59b 138
d0154e3a
VS
139int wxDataViewColumn::GetWidth() const
140{
141 switch ( m_width )
142 {
143 case wxCOL_WIDTH_DEFAULT:
144 return wxDVC_DEFAULT_WIDTH;
145
146 case wxCOL_WIDTH_AUTOSIZE:
147 wxCHECK_MSG( m_owner, wxDVC_DEFAULT_WIDTH, "no owner control" );
148 return m_owner->GetBestColumnWidth(m_owner->GetColumnIndex(this));
149
150 default:
151 return m_width;
152 }
153}
154
5d9e1605
RR
155void wxDataViewColumn::UpdateDisplay()
156{
157 if (m_owner)
158 {
159 int idx = m_owner->GetColumnIndex( this );
160 m_owner->OnColumnChange( idx );
161 }
162}
163
15b8afdc
VZ
164void wxDataViewColumn::SetSortOrder(bool ascending)
165{
166 if ( !m_owner )
167 return;
168
169 // First unset the old sort column if any.
170 int oldSortKey = m_owner->GetSortingColumnIndex();
171 if ( oldSortKey != wxNOT_FOUND )
172 {
173 m_owner->GetColumn(oldSortKey)->UnsetAsSortKey();
174 }
175
176 // Now set this one as the new sort column.
177 const int idx = m_owner->GetColumnIndex(this);
178 m_owner->SetSortingColumnIndex(idx);
179
180 m_sort = true;
181 m_sortAscending = ascending;
182
183 // Call this directly instead of using UpdateDisplay() as we already have
184 // the column index, no need to look it up again.
185 m_owner->OnColumnChange(idx);
186}
187
fa3d4aaf
VZ
188//-----------------------------------------------------------------------------
189// wxDataViewHeaderWindow
190//-----------------------------------------------------------------------------
191
e2bfe673 192class wxDataViewHeaderWindow : public wxHeaderCtrl
87f0efe2
RR
193{
194public:
e2bfe673
VZ
195 wxDataViewHeaderWindow(wxDataViewCtrl *parent)
196 : wxHeaderCtrl(parent)
87f0efe2 197 {
87f0efe2
RR
198 }
199
e2bfe673
VZ
200 wxDataViewCtrl *GetOwner() const
201 { return static_cast<wxDataViewCtrl *>(GetParent()); }
87f0efe2 202
3bfaa5a7
VZ
203protected:
204 // implement/override wxHeaderCtrl functions by forwarding them to the main
205 // control
482d06f8 206 virtual const wxHeaderColumn& GetColumn(unsigned int idx) const
87f0efe2 207 {
e2bfe673 208 return *(GetOwner()->GetColumn(idx));
87f0efe2
RR
209 }
210
3bfaa5a7
VZ
211 virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
212 {
213 wxDataViewCtrl * const owner = GetOwner();
214
215 int widthContents = owner->GetBestColumnWidth(idx);
216 owner->GetColumn(idx)->SetWidth(wxMax(widthTitle, widthContents));
aef252d9 217 owner->OnColumnChange(idx);
3bfaa5a7
VZ
218
219 return true;
220 }
221
222private:
fa3d4aaf
VZ
223 bool SendEvent(wxEventType type, unsigned int n)
224 {
225 wxDataViewCtrl * const owner = GetOwner();
226 wxDataViewEvent event(type, owner->GetId());
227
228 event.SetEventObject(owner);
229 event.SetColumn(n);
230 event.SetDataViewColumn(owner->GetColumn(n));
231 event.SetModel(owner->GetModel());
232
233 // for events created by wxDataViewHeaderWindow the
234 // row / value fields are not valid
857f05e1 235 return owner->ProcessWindowEvent(event);
fa3d4aaf
VZ
236 }
237
238 void OnClick(wxHeaderCtrlEvent& event)
239 {
46234a03
VZ
240 const unsigned idx = event.GetColumn();
241
242 if ( SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, idx) )
243 return;
244
245 // default handling for the column click is to sort by this column or
246 // toggle its sort order
247 wxDataViewCtrl * const owner = GetOwner();
248 wxDataViewColumn * const col = owner->GetColumn(idx);
249 if ( !col->IsSortable() )
250 {
251 // no default handling for non-sortable columns
fa3d4aaf 252 event.Skip();
46234a03
VZ
253 return;
254 }
255
256 if ( col->IsSortKey() )
257 {
258 // already using this column for sorting, just change the order
259 col->ToggleSortOrder();
260 }
261 else // not using this column for sorting yet
262 {
aadbdd16 263 col->SetSortOrder(true);
46234a03
VZ
264 }
265
266 wxDataViewModel * const model = owner->GetModel();
267 if ( model )
268 model->Resort();
269
270 owner->OnColumnChange(idx);
ff7bc95e
VZ
271
272 SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, idx);
fa3d4aaf
VZ
273 }
274
275 void OnRClick(wxHeaderCtrlEvent& event)
276 {
277 if ( !SendEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
278 event.GetColumn()) )
279 event.Skip();
280 }
281
bc4f1ff2 282 void OnResize(wxHeaderCtrlEvent& event)
aef252d9 283 {
dcb6cbec
VZ
284 wxDataViewCtrl * const owner = GetOwner();
285
565804f2 286 const unsigned col = event.GetColumn();
dcb6cbec 287 owner->GetColumn(col)->SetWidth(event.GetWidth());
565804f2 288 GetOwner()->OnColumnChange(col);
aef252d9
VZ
289 }
290
702f5349
VZ
291 void OnEndReorder(wxHeaderCtrlEvent& event)
292 {
293 wxDataViewCtrl * const owner = GetOwner();
294 owner->ColumnMoved(owner->GetColumn(event.GetColumn()),
977a41ec 295 event.GetNewOrder());
702f5349
VZ
296 }
297
fa3d4aaf 298 DECLARE_EVENT_TABLE()
c0c133e1 299 wxDECLARE_NO_COPY_CLASS(wxDataViewHeaderWindow);
a0f3af5f 300};
4ed7af08 301
fa3d4aaf
VZ
302BEGIN_EVENT_TABLE(wxDataViewHeaderWindow, wxHeaderCtrl)
303 EVT_HEADER_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnClick)
304 EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxDataViewHeaderWindow::OnRClick)
aef252d9 305
bc4f1ff2
FM
306 EVT_HEADER_RESIZING(wxID_ANY, wxDataViewHeaderWindow::OnResize)
307 EVT_HEADER_END_RESIZE(wxID_ANY, wxDataViewHeaderWindow::OnResize)
702f5349
VZ
308
309 EVT_HEADER_END_REORDER(wxID_ANY, wxDataViewHeaderWindow::OnEndReorder)
fa3d4aaf
VZ
310END_EVENT_TABLE()
311
0fcce6b9
RR
312//-----------------------------------------------------------------------------
313// wxDataViewRenameTimer
314//-----------------------------------------------------------------------------
315
316class wxDataViewRenameTimer: public wxTimer
317{
318private:
319 wxDataViewMainWindow *m_owner;
320
321public:
322 wxDataViewRenameTimer( wxDataViewMainWindow *owner );
323 void Notify();
324};
325
aba9bfd0
RR
326//-----------------------------------------------------------------------------
327// wxDataViewTreeNode
328//-----------------------------------------------------------------------------
bc4f1ff2 329
442c56e6 330class wxDataViewTreeNode;
57f2a652 331WX_DEFINE_ARRAY( wxDataViewTreeNode *, wxDataViewTreeNodes );
d47db7e0 332
4cef3873 333int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
bc4f1ff2 334 wxDataViewTreeNode ** node2);
aba9bfd0
RR
335
336class wxDataViewTreeNode
337{
338public:
422aa8ec 339 wxDataViewTreeNode(wxDataViewTreeNode *parent, const wxDataViewItem& item)
fb5f677b
VZ
340 : m_parent(parent),
341 m_item(item),
d0cfefc4 342 m_branchData(NULL)
b5ec7dd6 343 {
438fb233 344 }
b5ec7dd6 345
d0cfefc4
VS
346 ~wxDataViewTreeNode()
347 {
a2d3c415
VS
348 if ( m_branchData )
349 {
350 wxDataViewTreeNodes& nodes = m_branchData->children;
351 for ( wxDataViewTreeNodes::iterator i = nodes.begin();
352 i != nodes.end();
353 ++i )
354 {
355 delete *i;
356 }
357
358 delete m_branchData;
359 }
d0cfefc4
VS
360 }
361
422aa8ec 362 static wxDataViewTreeNode* CreateRootNode()
d47db7e0 363 {
422aa8ec 364 wxDataViewTreeNode *n = new wxDataViewTreeNode(NULL, wxDataViewItem());
ddcc73a7 365 n->m_branchData = new BranchNodeData;
d0cfefc4 366 n->m_branchData->open = true;
422aa8ec 367 return n;
d47db7e0 368 }
aba9bfd0 369
344ed1f3 370 wxDataViewTreeNode * GetParent() const { return m_parent; }
422aa8ec 371
a2d3c415 372 const wxDataViewTreeNodes& GetChildNodes() const
d0cfefc4
VS
373 {
374 wxASSERT( m_branchData != NULL );
ff3c5ad3 375 return m_branchData->children;
d0cfefc4 376 }
442c56e6 377
35219832 378 void InsertChild(wxDataViewTreeNode *node, unsigned index)
d47db7e0 379 {
d0cfefc4
VS
380 if ( !m_branchData )
381 m_branchData = new BranchNodeData;
382
35219832
VS
383 m_branchData->children.Insert(node, index);
384
422aa8ec 385 // TODO: insert into sorted array directly in O(log n) instead of resorting in O(n log n)
24c4a50f 386 if (g_column >= -1)
ff3c5ad3 387 m_branchData->children.Sort( &wxGenericTreeModelNodeCmp );
57f2a652 388 }
aba9bfd0 389
a2d3c415
VS
390 void RemoveChild(wxDataViewTreeNode *node)
391 {
392 wxCHECK_RET( m_branchData != NULL, "leaf node doesn't have children" );
393 m_branchData->children.Remove(node);
394 }
395
5bb82ad4
VS
396 // returns position of child node for given item in children list or wxNOT_FOUND
397 int FindChildByItem(const wxDataViewItem& item) const
398 {
399 if ( !m_branchData )
400 return wxNOT_FOUND;
401
402 const wxDataViewTreeNodes& nodes = m_branchData->children;
403 const int len = nodes.size();
404 for ( int i = 0; i < len; i++ )
405 {
406 if ( nodes[i]->m_item == item )
407 return i;
408 }
409 return wxNOT_FOUND;
410 }
411
344ed1f3 412 const wxDataViewItem & GetItem() const { return m_item; }
438fb233 413 void SetItem( const wxDataViewItem & item ) { m_item = item; }
aba9bfd0 414
344ed1f3 415 int GetIndentLevel() const
3b6280be 416 {
59e60167 417 int ret = 0;
344ed1f3 418 const wxDataViewTreeNode * node = this;
438fb233
RR
419 while( node->GetParent()->GetParent() != NULL )
420 {
421 node = node->GetParent();
422 ret ++;
423 }
424 return ret;
3b6280be 425 }
aba9bfd0 426
344ed1f3 427 bool IsOpen() const
442c56e6 428 {
d0cfefc4 429 return m_branchData && m_branchData->open;
442c56e6 430 }
d47db7e0
RR
431
432 void ToggleOpen()
442c56e6 433 {
ddcc73a7
VZ
434 // We do not allow the (invisible) root node to be collapsed because
435 // there is no way to expand it again.
436 if ( !m_parent )
437 return;
438
d0cfefc4
VS
439 wxCHECK_RET( m_branchData != NULL, "can't open leaf node" );
440
d47db7e0 441 int sum = 0;
422aa8ec 442
ff3c5ad3 443 const wxDataViewTreeNodes& nodes = m_branchData->children;
d0cfefc4 444 const int len = nodes.GetCount();
59e60167 445 for ( int i = 0;i < len; i ++)
d0cfefc4 446 sum += 1 + nodes[i]->GetSubTreeCount();
d47db7e0 447
d0cfefc4 448 if (m_branchData->open)
d47db7e0
RR
449 {
450 ChangeSubTreeCount(-sum);
d0cfefc4 451 m_branchData->open = !m_branchData->open;
d47db7e0
RR
452 }
453 else
454 {
d0cfefc4 455 m_branchData->open = !m_branchData->open;
422aa8ec 456 ChangeSubTreeCount(+sum);
d47db7e0
RR
457 }
458 }
422aa8ec
VS
459
460 // "HasChildren" property corresponds to model's IsContainer(). Note that it may be true
ff3c5ad3 461 // even if GetChildNodes() is empty; see below.
d0cfefc4
VS
462 bool HasChildren() const
463 {
464 return m_branchData != NULL;
465 }
466
467 void SetHasChildren(bool has)
468 {
ddcc73a7
VZ
469 // The invisible root item always has children, so ignore any attempts
470 // to change this.
471 if ( !m_parent )
472 return;
473
d0cfefc4
VS
474 if ( !has )
475 {
476 wxDELETE(m_branchData);
477 }
478 else if ( m_branchData == NULL )
479 {
480 m_branchData = new BranchNodeData;
481 }
482 }
483
484 int GetSubTreeCount() const
485 {
486 return m_branchData ? m_branchData->subTreeCount : 0;
487 }
d47db7e0 488
442c56e6 489 void ChangeSubTreeCount( int num )
d47db7e0 490 {
d0cfefc4
VS
491 wxASSERT( m_branchData != NULL );
492
493 if( !m_branchData->open )
59e60167 494 return;
422aa8ec 495
d0cfefc4
VS
496 m_branchData->subTreeCount += num;
497 wxASSERT( m_branchData->subTreeCount >= 0 );
422aa8ec 498
438fb233
RR
499 if( m_parent )
500 m_parent->ChangeSubTreeCount(num);
d47db7e0
RR
501 }
502
d3f00f59
VZ
503 void Resort()
504 {
d0cfefc4
VS
505 if ( !m_branchData )
506 return;
507
24c4a50f 508 if (g_column >= -1)
d3f00f59 509 {
ff3c5ad3 510 wxDataViewTreeNodes& nodes = m_branchData->children;
d0cfefc4
VS
511
512 nodes.Sort( &wxGenericTreeModelNodeCmp );
513 int len = nodes.GetCount();
57f2a652 514 for (int i = 0; i < len; i ++)
422aa8ec 515 {
d0cfefc4
VS
516 if ( nodes[i]->HasChildren() )
517 nodes[i]->Resort();
422aa8ec 518 }
57ab4546 519 }
57ab4546
VS
520 }
521
522
aba9bfd0 523private:
d0cfefc4
VS
524 wxDataViewTreeNode *m_parent;
525
422aa8ec
VS
526 // Corresponding model item.
527 wxDataViewItem m_item;
528
d0cfefc4
VS
529 // Data specific to non-leaf (branch, inner) nodes. They are kept in a
530 // separate struct in order to conserve memory.
531 struct BranchNodeData
532 {
533 BranchNodeData()
534 : open(false),
535 subTreeCount(0)
536 {
537 }
422aa8ec 538
d0cfefc4
VS
539 // Child nodes. Note that this may be empty even if m_hasChildren in
540 // case this branch of the tree wasn't expanded and realized yet.
ff3c5ad3 541 wxDataViewTreeNodes children;
422aa8ec 542
d0cfefc4
VS
543 // Is the branch node currently open (expanded)?
544 bool open;
422aa8ec 545
d0cfefc4
VS
546 // Total count of expanded (i.e. visible with the help of some
547 // scrolling) items in the subtree, but excluding this node. I.e. it is
548 // 0 for leaves and is the number of rows the subtree occupies for
549 // branch nodes.
550 int subTreeCount;
551 };
422aa8ec 552
d0cfefc4 553 BranchNodeData *m_branchData;
aba9bfd0
RR
554};
555
422aa8ec 556
4cef3873 557int LINKAGEMODE wxGenericTreeModelNodeCmp( wxDataViewTreeNode ** node1,
bc4f1ff2 558 wxDataViewTreeNode ** node2)
d47db7e0 559{
57f2a652 560 return g_model->Compare( (*node1)->GetItem(), (*node2)->GetItem(), g_column, g_asending );
d47db7e0
RR
561}
562
d47db7e0 563
a0f3af5f
RR
564//-----------------------------------------------------------------------------
565// wxDataViewMainWindow
566//-----------------------------------------------------------------------------
4ed7af08 567
c3b0247d 568WX_DEFINE_SORTED_ARRAY_SIZE_T(unsigned int, wxDataViewSelection);
cab07038 569
a0f3af5f 570class wxDataViewMainWindow: public wxWindow
4ed7af08 571{
a0f3af5f
RR
572public:
573 wxDataViewMainWindow( wxDataViewCtrl *parent,
574 wxWindowID id,
575 const wxPoint &pos = wxDefaultPosition,
576 const wxSize &size = wxDefaultSize,
577 const wxString &name = wxT("wxdataviewctrlmainwindow") );
d3c7fc99 578 virtual ~wxDataViewMainWindow();
777f9cef 579
c2c89730 580 bool IsList() const { return GetModel()->IsListModel(); }
344ed1f3 581 bool IsVirtualList() const { return m_root == NULL; }
4ed7af08 582
aba9bfd0
RR
583 // notifications from wxDataViewModel
584 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
32143117 585 bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item );
aba9bfd0 586 bool ItemChanged( const wxDataViewItem &item );
d93cc830 587 bool ValueChanged( const wxDataViewItem &item, unsigned int model_column );
a0f3af5f 588 bool Cleared();
d3f00f59 589 void Resort()
b7fe2261 590 {
344ed1f3 591 if (!IsVirtualList())
a3cc79d9
RR
592 {
593 SortPrepare();
594 m_root->Resort();
595 }
b7fe2261 596 UpdateDisplay();
66e09788 597 }
4ed7af08 598
0c5a056a
VZ
599 // Override the base class method to resort if needed, i.e. if
600 // SortPrepare() was called -- and ignored -- while we were frozen.
601 virtual void DoThaw()
602 {
603 if ( g_column == SortColumn_OnThaw )
604 {
605 Resort();
606 g_column = SortColumn_None;
607 }
608
609 wxWindow::DoThaw();
610 }
611
66e09788
RR
612 void SortPrepare()
613 {
c2c89730 614 g_model = GetModel();
0c5a056a
VZ
615
616 // Avoid sorting while the window is frozen, this allows to quickly add
617 // many items without resorting after each addition and only resort
618 // them all at once when the window is finally thawed, see above.
619 if ( IsFrozen() )
620 {
621 g_column = SortColumn_OnThaw;
622 return;
623 }
624
57f2a652 625 wxDataViewColumn* col = GetOwner()->GetSortingColumn();
66e09788
RR
626 if( !col )
627 {
24c4a50f 628 if (g_model->HasDefaultCompare())
c4864c04 629 g_column = SortColumn_Default;
24c4a50f 630 else
c4864c04 631 g_column = SortColumn_None;
24c4a50f 632
66e09788
RR
633 g_asending = true;
634 return;
635 }
57f2a652 636 g_column = col->GetModelColumn();
b7fe2261 637 g_asending = col->IsSortOrderAscending();
66e09788 638 }
b7fe2261 639
a0f3af5f
RR
640 void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
641 wxDataViewCtrl *GetOwner() { return m_owner; }
9861f022 642 const wxDataViewCtrl *GetOwner() const { return m_owner; }
4ed7af08 643
c2c89730
VS
644 wxDataViewModel* GetModel() { return GetOwner()->GetModel(); }
645 const wxDataViewModel* GetModel() const { return GetOwner()->GetModel(); }
646
9adeb77a 647#if wxUSE_DRAG_AND_DROP
818d91a9 648 wxBitmap CreateItemBitmap( unsigned int row, int &indent );
9adeb77a 649#endif // wxUSE_DRAG_AND_DROP
a0f3af5f 650 void OnPaint( wxPaintEvent &event );
64ac3db8 651 void OnCharHook( wxKeyEvent &event );
cab07038 652 void OnChar( wxKeyEvent &event );
ed3aece5 653 void OnVerticalNavigation(int delta, const wxKeyEvent& event);
cfc21881
VS
654 void OnLeftKey();
655 void OnRightKey();
a0f3af5f
RR
656 void OnMouse( wxMouseEvent &event );
657 void OnSetFocus( wxFocusEvent &event );
cab07038 658 void OnKillFocus( wxFocusEvent &event );
f554a14b 659
a0f3af5f
RR
660 void UpdateDisplay();
661 void RecalculateDisplay();
662 void OnInternalIdle();
f554a14b 663
0fcce6b9 664 void OnRenameTimer();
0fcce6b9 665
9861f022 666 void ScrollWindow( int dx, int dy, const wxRect *rect = NULL );
fbda518c 667 void ScrollTo( int rows, int column );
120b9b05 668
80ce465c 669 unsigned GetCurrentRow() const { return m_currentRow; }
0a71f9e9
RR
670 bool HasCurrentRow() { return m_currentRow != (unsigned int)-1; }
671 void ChangeCurrentRow( unsigned int row );
1c8e71c9 672 bool TryAdvanceCurrentColumn(wxDataViewTreeNode *node, bool forward);
120b9b05 673
f52f3795
VS
674 wxDataViewColumn *GetCurrentColumn() const { return m_currentCol; }
675 void ClearCurrentColumn() { m_currentCol = NULL; }
676
47b378bd 677 bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
cab07038 678 bool IsEmpty() { return GetRowCount() == 0; }
120b9b05 679
9861f022
RR
680 int GetCountPerPage() const;
681 int GetEndOfLastCol() const;
682 unsigned int GetFirstVisibleRow() const;
bc4f1ff2 683
4cef3873
VZ
684 // I change this method to un const because in the tree view,
685 // the displaying number of the tree are changing along with the
bc4f1ff2 686 // expanding/collapsing of the tree nodes
3b6280be 687 unsigned int GetLastVisibleRow();
f2bf2d71 688 unsigned int GetRowCount() const;
120b9b05 689
f23b8c0d 690 const wxDataViewSelection& GetSelections() const { return m_selection; }
4cef3873 691 void SetSelections( const wxDataViewSelection & sel )
bc4f1ff2 692 { m_selection = sel; UpdateDisplay(); }
87f0efe2 693 void Select( const wxArrayInt& aSelections );
cab07038 694 void SelectAllRows( bool on );
0a71f9e9
RR
695 void SelectRow( unsigned int row, bool on );
696 void SelectRows( unsigned int from, unsigned int to, bool on );
697 void ReverseRowSelection( unsigned int row );
698 bool IsRowSelected( unsigned int row );
526e19e2 699 void SendSelectionChangedEvent( const wxDataViewItem& item);
120b9b05 700
0a71f9e9
RR
701 void RefreshRow( unsigned int row );
702 void RefreshRows( unsigned int from, unsigned int to );
703 void RefreshRowsAfter( unsigned int firstRow );
120b9b05 704
9861f022
RR
705 // returns the colour to be used for drawing the rules
706 wxColour GetRuleColour() const
707 {
708 return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
709 }
710
9861f022 711 wxRect GetLineRect( unsigned int row ) const;
777f9cef 712
344ed1f3
RR
713 int GetLineStart( unsigned int row ) const; // row * m_lineHeight in fixed mode
714 int GetLineHeight( unsigned int row ) const; // m_lineHeight in fixed mode
715 int GetLineAt( unsigned int y ) const; // y / m_lineHeight in fixed mode
9861f022 716
bbfd4548 717 void SetRowHeight( int lineHeight ) { m_lineHeight = lineHeight; }
0fdeaabe 718 int GetRowHeight() const { return m_lineHeight; }
1af67319 719 int GetDefaultRowHeight() const;
bbfd4548 720
bc4f1ff2 721 // Some useful functions for row and item mapping
fbda518c 722 wxDataViewItem GetItemByRow( unsigned int row ) const;
344ed1f3 723 int GetRowByItem( const wxDataViewItem & item ) const;
777f9cef 724
0fdeaabe
VS
725 wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row ) const;
726 // We did not need this temporarily
727 // wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
728
bc4f1ff2 729 // Methods for building the mapping tree
aba9bfd0
RR
730 void BuildTree( wxDataViewModel * model );
731 void DestroyTree();
a87b466d 732 void HitTest( const wxPoint & point, wxDataViewItem & item, wxDataViewColumn* &column );
fbda518c 733 wxRect GetItemRect( const wxDataViewItem & item, const wxDataViewColumn* column );
afebb87b 734
235d5f88
RR
735 void Expand( unsigned int row );
736 void Collapse( unsigned int row );
739a8399 737 bool IsExpanded( unsigned int row ) const;
235d5f88 738 bool HasChildren( unsigned int row ) const;
821baf7d 739
9adeb77a 740#if wxUSE_DRAG_AND_DROP
a653c966
RR
741 bool EnableDragSource( const wxDataFormat &format );
742 bool EnableDropTarget( const wxDataFormat &format );
743
9deec111 744 void RemoveDropHint();
a653c966
RR
745 wxDragResult OnDragOver( wxDataFormat format, wxCoord x, wxCoord y, wxDragResult def );
746 bool OnDrop( wxDataFormat format, wxCoord x, wxCoord y );
747 wxDragResult OnData( wxDataFormat format, wxCoord x, wxCoord y, wxDragResult def );
748 void OnLeave();
9adeb77a 749#endif // wxUSE_DRAG_AND_DROP
821baf7d 750
1c8e71c9
VS
751 void OnColumnsCountChanged();
752
64ac3db8
VZ
753 // Called by wxDataViewCtrl and our own OnRenameTimer() to start edit the
754 // specified item in the given column.
755 void StartEditing(const wxDataViewItem& item, const wxDataViewColumn* col);
756
3b6280be 757private:
f2bf2d71 758 int RecalculateCount() const;
3b6280be 759
fd48fe89
VZ
760 // Return false only if the event was vetoed by its handler.
761 bool SendExpanderEvent(wxEventType type, const wxDataViewItem& item);
aba9bfd0 762
442c56e6 763 wxDataViewTreeNode * FindNode( const wxDataViewItem & item );
351461fc 764
1c8e71c9
VS
765 wxDataViewColumn *FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode);
766
cf5d4c76
VS
767 bool IsCellEditableInMode(const wxDataViewItem& item, const wxDataViewColumn *col, wxDataViewCellMode mode) const;
768
1c959a62
VZ
769 void DrawCellBackground( wxDataViewRenderer* cell, wxDC& dc, const wxRect& rect );
770
a0f3af5f 771private:
0fcce6b9
RR
772 wxDataViewCtrl *m_owner;
773 int m_lineHeight;
774 bool m_dirty;
120b9b05 775
0fcce6b9 776 wxDataViewColumn *m_currentCol;
99d471a5 777 unsigned int m_currentRow;
cab07038 778 wxDataViewSelection m_selection;
120b9b05 779
0fcce6b9 780 wxDataViewRenameTimer *m_renameTimer;
0fcce6b9 781 bool m_lastOnSame;
f554a14b 782
cab07038 783 bool m_hasFocus;
1c8e71c9
VS
784 bool m_useCellFocus;
785 bool m_currentColSetByKeyboard;
cab07038 786
9adeb77a 787#if wxUSE_DRAG_AND_DROP
e21f75bd
RR
788 int m_dragCount;
789 wxPoint m_dragStart;
9adeb77a 790
821baf7d
RR
791 bool m_dragEnabled;
792 wxDataFormat m_dragFormat;
9adeb77a 793
821baf7d
RR
794 bool m_dropEnabled;
795 wxDataFormat m_dropFormat;
9deec111
RR
796 bool m_dropHint;
797 unsigned int m_dropHintLine;
9adeb77a 798#endif // wxUSE_DRAG_AND_DROP
e21f75bd
RR
799
800 // for double click logic
0a71f9e9 801 unsigned int m_lineLastClicked,
977a41ec
FM
802 m_lineBeforeLastClicked,
803 m_lineSelectSingleOnUp;
cab07038 804
9861f022
RR
805 // the pen used to draw horiz/vertical rules
806 wxPen m_penRule;
807
3b6280be
RR
808 // the pen used to draw the expander and the lines
809 wxPen m_penExpander;
810
bc4f1ff2 811 // This is the tree structure of the model
442c56e6 812 wxDataViewTreeNode * m_root;
3b6280be 813 int m_count;
bc4f1ff2
FM
814
815 // This is the tree node under the cursor
24c4a50f 816 wxDataViewTreeNode * m_underMouse;
bc4f1ff2 817
64ac3db8
VZ
818 // The control used for editing or NULL.
819 wxWeakRef<wxWindow> m_editorCtrl;
820
821 // Id m_editorCtrl is non-NULL, pointer to the associated renderer.
822 wxDataViewRenderer* m_editorRenderer;
823
a0f3af5f
RR
824private:
825 DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
826 DECLARE_EVENT_TABLE()
827};
4ed7af08 828
f554a14b 829// ---------------------------------------------------------
aba9bfd0 830// wxGenericDataViewModelNotifier
f554a14b 831// ---------------------------------------------------------
a0f3af5f 832
aba9bfd0 833class wxGenericDataViewModelNotifier: public wxDataViewModelNotifier
4ed7af08 834{
a0f3af5f 835public:
aba9bfd0 836 wxGenericDataViewModelNotifier( wxDataViewMainWindow *mainWindow )
a0f3af5f 837 { m_mainWindow = mainWindow; }
f554a14b 838
aba9bfd0
RR
839 virtual bool ItemAdded( const wxDataViewItem & parent, const wxDataViewItem & item )
840 { return m_mainWindow->ItemAdded( parent , item ); }
442c56e6 841 virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
071691a0 842 { return m_mainWindow->ItemDeleted( parent, item ); }
aba9bfd0
RR
843 virtual bool ItemChanged( const wxDataViewItem & item )
844 { return m_mainWindow->ItemChanged(item); }
845 virtual bool ValueChanged( const wxDataViewItem & item , unsigned int col )
846 { return m_mainWindow->ValueChanged( item, col ); }
a0f3af5f
RR
847 virtual bool Cleared()
848 { return m_mainWindow->Cleared(); }
d3f00f59 849 virtual void Resort()
977a41ec 850 { m_mainWindow->Resort(); }
f554a14b 851
a0f3af5f
RR
852 wxDataViewMainWindow *m_mainWindow;
853};
4ed7af08 854
f554a14b 855// ---------------------------------------------------------
baa9ebc4 856// wxDataViewRenderer
f554a14b 857// ---------------------------------------------------------
4ed7af08 858
baa9ebc4 859IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
4ed7af08 860
c741d33f 861wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype,
9861f022
RR
862 wxDataViewCellMode mode,
863 int align) :
6eec70b9 864 wxDataViewCustomRendererBase( varianttype, mode, align )
4ed7af08 865{
9861f022
RR
866 m_align = align;
867 m_mode = mode;
c937bcac 868 m_ellipsizeMode = wxELLIPSIZE_MIDDLE;
6eec70b9 869 m_dc = NULL;
4ed7af08
RR
870}
871
baa9ebc4 872wxDataViewRenderer::~wxDataViewRenderer()
3d9d7cc4 873{
6eec70b9
VZ
874 delete m_dc;
875}
876
877wxDC *wxDataViewRenderer::GetDC()
878{
879 if (m_dc == NULL)
880 {
881 if (GetOwner() == NULL)
882 return NULL;
883 if (GetOwner()->GetOwner() == NULL)
884 return NULL;
885 m_dc = new wxClientDC( GetOwner()->GetOwner() );
886 }
887
888 return m_dc;
889}
890
891void wxDataViewRenderer::SetAlignment( int align )
892{
893 m_align=align;
894}
895
896int wxDataViewRenderer::GetAlignment() const
897{
898 return m_align;
899}
900
6eec70b9
VZ
901// ---------------------------------------------------------
902// wxDataViewCustomRenderer
903// ---------------------------------------------------------
904
905IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
906
907wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
908 wxDataViewCellMode mode, int align ) :
909 wxDataViewRenderer( varianttype, mode, align )
910{
911}
912
f554a14b 913// ---------------------------------------------------------
baa9ebc4 914// wxDataViewTextRenderer
f554a14b 915// ---------------------------------------------------------
4ed7af08 916
6eec70b9 917IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewRenderer)
4ed7af08 918
c741d33f 919wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype,
9861f022 920 wxDataViewCellMode mode, int align ) :
6eec70b9 921 wxDataViewRenderer( varianttype, mode, align )
4ed7af08
RR
922{
923}
924
baa9ebc4 925bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
4ed7af08 926{
90675b95 927 m_text = value.GetString();
f554a14b 928
90675b95 929 return true;
4ed7af08
RR
930}
931
9861f022 932bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
4ed7af08
RR
933{
934 return false;
935}
936
bc4f1ff2 937bool wxDataViewTextRenderer::HasEditorCtrl() const
442c56e6 938{
99d471a5
RR
939 return true;
940}
941
64c70359 942wxWindow* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent,
99d471a5
RR
943 wxRect labelRect, const wxVariant &value )
944{
0a807957
RR
945 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, value,
946 wxPoint(labelRect.x,labelRect.y),
dd90475f
VS
947 wxSize(labelRect.width,labelRect.height),
948 wxTE_PROCESS_ENTER );
0a807957
RR
949
950 // select the text in the control an place the cursor at the end
951 ctrl->SetInsertionPointEnd();
952 ctrl->SelectAll();
953
954 return ctrl;
99d471a5
RR
955}
956
64c70359 957bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant &value )
99d471a5
RR
958{
959 wxTextCtrl *text = (wxTextCtrl*) editor;
960 value = text->GetValue();
961 return true;
962}
963
62265c2c 964bool wxDataViewTextRenderer::Render(wxRect rect, wxDC *dc, int state)
3d9d7cc4 965{
62265c2c 966 RenderText(m_text, 0, rect, dc, state);
90675b95 967 return true;
3d9d7cc4
RR
968}
969
9861f022 970wxSize wxDataViewTextRenderer::GetSize() const
3d9d7cc4 971{
87f0efe2 972 if (!m_text.empty())
86755098
VS
973 return GetTextExtent(m_text);
974 else
975 return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE);
3d9d7cc4
RR
976}
977
2586d4a1 978// ---------------------------------------------------------
baa9ebc4 979// wxDataViewBitmapRenderer
2586d4a1
RR
980// ---------------------------------------------------------
981
6eec70b9 982IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewRenderer)
2586d4a1 983
c741d33f 984wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype,
9861f022 985 wxDataViewCellMode mode, int align ) :
6eec70b9 986 wxDataViewRenderer( varianttype, mode, align )
2586d4a1
RR
987{
988}
989
baa9ebc4 990bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
2586d4a1
RR
991{
992 if (value.GetType() == wxT("wxBitmap"))
993 m_bitmap << value;
994 if (value.GetType() == wxT("wxIcon"))
995 m_icon << value;
996
997 return true;
998}
999
9861f022 1000bool wxDataViewBitmapRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
2586d4a1
RR
1001{
1002 return false;
1003}
1004
baa9ebc4 1005bool wxDataViewBitmapRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
2586d4a1 1006{
a1b806b9 1007 if (m_bitmap.IsOk())
2586d4a1 1008 dc->DrawBitmap( m_bitmap, cell.x, cell.y );
a1b806b9 1009 else if (m_icon.IsOk())
2586d4a1
RR
1010 dc->DrawIcon( m_icon, cell.x, cell.y );
1011
1012 return true;
1013}
1014
9861f022 1015wxSize wxDataViewBitmapRenderer::GetSize() const
2586d4a1 1016{
a1b806b9 1017 if (m_bitmap.IsOk())
2586d4a1 1018 return wxSize( m_bitmap.GetWidth(), m_bitmap.GetHeight() );
a1b806b9 1019 else if (m_icon.IsOk())
2586d4a1
RR
1020 return wxSize( m_icon.GetWidth(), m_icon.GetHeight() );
1021
ce468dc2 1022 return wxSize(wxDVC_DEFAULT_RENDERER_SIZE,wxDVC_DEFAULT_RENDERER_SIZE);
2586d4a1
RR
1023}
1024
f554a14b 1025// ---------------------------------------------------------
baa9ebc4 1026// wxDataViewToggleRenderer
f554a14b 1027// ---------------------------------------------------------
4ed7af08 1028
6eec70b9 1029IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer, wxDataViewRenderer)
4ed7af08 1030
baa9ebc4 1031wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
9861f022 1032 wxDataViewCellMode mode, int align ) :
6eec70b9 1033 wxDataViewRenderer( varianttype, mode, align )
4ed7af08 1034{
90675b95 1035 m_toggle = false;
4ed7af08
RR
1036}
1037
baa9ebc4 1038bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
4ed7af08 1039{
90675b95 1040 m_toggle = value.GetBool();
f554a14b 1041
a8461d31 1042 return true;
4ed7af08
RR
1043}
1044
9861f022 1045bool wxDataViewToggleRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
4ed7af08
RR
1046{
1047 return false;
1048}
f554a14b 1049
baa9ebc4 1050bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
4ed7af08 1051{
862d8041 1052 int flags = 0;
90675b95 1053 if (m_toggle)
862d8041 1054 flags |= wxCONTROL_CHECKED;
98f8e666
VZ
1055 if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE ||
1056 GetEnabled() == false)
862d8041
RR
1057 flags |= wxCONTROL_DISABLED;
1058
45a04dd3
VZ
1059 // Ensure that the check boxes always have at least the minimal required
1060 // size, otherwise DrawCheckBox() doesn't really work well. If this size is
1061 // greater than the cell size, the checkbox will be truncated but this is a
1062 // lesser evil.
1063 wxSize size = cell.GetSize();
1064 size.IncTo(GetSize());
1065 cell.SetSize(size);
68d7680d 1066
90b903c2 1067 wxRendererNative::Get().DrawCheckBox(
862d8041
RR
1068 GetOwner()->GetOwner(),
1069 *dc,
de4bf0b3 1070 cell,
862d8041 1071 flags );
f554a14b 1072
90675b95 1073 return true;
4ed7af08
RR
1074}
1075
dc73d7f5
VS
1076bool wxDataViewToggleRenderer::WXActivateCell(const wxRect& WXUNUSED(cell),
1077 wxDataViewModel *model,
1078 const wxDataViewItem& item,
1079 unsigned int col,
1080 const wxMouseEvent *mouseEvent)
0fdc2321 1081{
dc73d7f5 1082 if ( mouseEvent )
98f8e666 1083 {
dc73d7f5
VS
1084 // only react to clicks directly on the checkbox, not elsewhere in the same cell:
1085 if ( !wxRect(GetSize()).Contains(mouseEvent->GetPosition()) )
1086 return false;
98f8e666 1087 }
dbc3aec1 1088
dc73d7f5
VS
1089 model->ChangeValue(!m_toggle, item, col);
1090 return true;
0fdc2321
RR
1091}
1092
9861f022 1093wxSize wxDataViewToggleRenderer::GetSize() const
4ed7af08 1094{
de4bf0b3
FM
1095 // the window parameter is not used by GetCheckBoxSize() so it's
1096 // safe to pass NULL
1097 return wxRendererNative::Get().GetCheckBoxSize(NULL);
4ed7af08
RR
1098}
1099
f554a14b 1100// ---------------------------------------------------------
baa9ebc4 1101// wxDataViewProgressRenderer
f554a14b 1102// ---------------------------------------------------------
4ed7af08 1103
6eec70b9 1104IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer, wxDataViewRenderer)
4ed7af08 1105
baa9ebc4 1106wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
9861f022 1107 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
6eec70b9 1108 wxDataViewRenderer( varianttype, mode, align )
4ed7af08
RR
1109{
1110 m_label = label;
1111 m_value = 0;
1112}
1113
baa9ebc4 1114bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
4ed7af08
RR
1115{
1116 m_value = (long) value;
f554a14b 1117
4ed7af08
RR
1118 if (m_value < 0) m_value = 0;
1119 if (m_value > 100) m_value = 100;
f554a14b 1120
4ed7af08
RR
1121 return true;
1122}
f554a14b 1123
9861f022
RR
1124bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
1125{
1126 value = (long) m_value;
1127 return true;
1128}
1129
62265c2c
VZ
1130bool
1131wxDataViewProgressRenderer::Render(wxRect rect, wxDC *dc, int WXUNUSED(state))
4ed7af08 1132{
62265c2c 1133 // deflate the rect to leave a small border between bars in adjacent rows
3e60a3c1
VZ
1134 wxRect bar = rect.Deflate(0, 1);
1135
62265c2c
VZ
1136 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1137 dc->SetPen( *wxBLACK_PEN );
1138 dc->DrawRectangle( bar );
4ed7af08 1139
3e60a3c1 1140 bar.width = (int)(bar.width * m_value / 100.);
62265c2c
VZ
1141 dc->SetPen( *wxTRANSPARENT_PEN );
1142
1143 const wxDataViewItemAttr& attr = GetAttr();
1144 dc->SetBrush( attr.HasColour() ? wxBrush(attr.GetColour())
1145 : *wxBLUE_BRUSH );
1146 dc->DrawRectangle( bar );
f554a14b 1147
4ed7af08
RR
1148 return true;
1149}
1150
9861f022 1151wxSize wxDataViewProgressRenderer::GetSize() const
4ed7af08
RR
1152{
1153 return wxSize(40,12);
1154}
f554a14b 1155
b7fe2261 1156// ---------------------------------------------------------
24c4a50f 1157// wxDataViewIconTextRenderer
b7fe2261 1158// ---------------------------------------------------------
24c4a50f 1159
6eec70b9 1160IMPLEMENT_CLASS(wxDataViewIconTextRenderer, wxDataViewRenderer)
24c4a50f 1161
b7fe2261 1162wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(
977a41ec 1163const wxString &varianttype, wxDataViewCellMode mode, int align ) :
6eec70b9 1164 wxDataViewRenderer( varianttype, mode, align )
24c4a50f
RR
1165{
1166 SetMode(mode);
1167 SetAlignment(align);
1168}
1169
24c4a50f
RR
1170bool wxDataViewIconTextRenderer::SetValue( const wxVariant &value )
1171{
1172 m_value << value;
1173 return true;
1174}
1175
b5ec7dd6 1176bool wxDataViewIconTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
24c4a50f
RR
1177{
1178 return false;
1179}
b7fe2261 1180
62265c2c 1181bool wxDataViewIconTextRenderer::Render(wxRect rect, wxDC *dc, int state)
24c4a50f 1182{
52e750fc 1183 int xoffset = 0;
38c34918
VZ
1184
1185 const wxIcon& icon = m_value.GetIcon();
1186 if ( icon.IsOk() )
24c4a50f 1187 {
62265c2c 1188 dc->DrawIcon(icon, rect.x, rect.y + (rect.height - icon.GetHeight())/2);
38c34918 1189 xoffset = icon.GetWidth()+4;
24c4a50f 1190 }
b5ec7dd6 1191
62265c2c 1192 RenderText(m_value.GetText(), xoffset, rect, dc, state);
24c4a50f
RR
1193
1194 return true;
1195}
1196
1197wxSize wxDataViewIconTextRenderer::GetSize() const
1198{
e44ac7bc
RR
1199 if (!m_value.GetText().empty())
1200 {
86755098 1201 wxSize size = GetTextExtent(m_value.GetText());
b5ec7dd6 1202
e44ac7bc 1203 if (m_value.GetIcon().IsOk())
86755098
VS
1204 size.x += m_value.GetIcon().GetWidth() + 4;
1205 return size;
e44ac7bc
RR
1206 }
1207 return wxSize(80,20);
24c4a50f
RR
1208}
1209
64c70359 1210wxWindow* wxDataViewIconTextRenderer::CreateEditorCtrl(wxWindow *parent, wxRect labelRect, const wxVariant& value)
24c4a50f 1211{
c937344c
RR
1212 wxDataViewIconText iconText;
1213 iconText << value;
1214
1215 wxString text = iconText.GetText();
1216
1217 // adjust the label rect to take the width of the icon into account
1218 if (iconText.GetIcon().IsOk())
1219 {
1220 int w = iconText.GetIcon().GetWidth() + 4;
1221 labelRect.x += w;
1222 labelRect.width -= w;
1223 }
1224
0a807957
RR
1225 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, text,
1226 wxPoint(labelRect.x,labelRect.y),
dd90475f
VS
1227 wxSize(labelRect.width,labelRect.height),
1228 wxTE_PROCESS_ENTER );
0a807957
RR
1229
1230 // select the text in the control an place the cursor at the end
1231 ctrl->SetInsertionPointEnd();
1232 ctrl->SelectAll();
1233
1234 return ctrl;
24c4a50f
RR
1235}
1236
64c70359 1237bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVariant& value )
24c4a50f 1238{
c937344c
RR
1239 wxTextCtrl *text = (wxTextCtrl*) editor;
1240
cdacccae
VZ
1241 // The icon can't be edited so get its old value and reuse it.
1242 wxVariant valueOld;
1243 wxDataViewColumn* const col = GetOwner();
1244 GetView()->GetModel()->GetValue(valueOld, m_item, col->GetModelColumn());
1245
1246 wxDataViewIconText iconText;
1247 iconText << valueOld;
1248
1249 // But replace the text with the value entered by user.
1250 iconText.SetText(text->GetValue());
1251
c937344c
RR
1252 value << iconText;
1253 return true;
24c4a50f
RR
1254}
1255
a653c966
RR
1256//-----------------------------------------------------------------------------
1257// wxDataViewDropTarget
1258//-----------------------------------------------------------------------------
1259
9adeb77a
VZ
1260#if wxUSE_DRAG_AND_DROP
1261
818d91a9
RR
1262class wxBitmapCanvas: public wxWindow
1263{
1264public:
1265 wxBitmapCanvas( wxWindow *parent, const wxBitmap &bitmap, const wxSize &size ) :
977a41ec 1266 wxWindow( parent, wxID_ANY, wxPoint(0,0), size )
818d91a9
RR
1267 {
1268 m_bitmap = bitmap;
1269 Connect( wxEVT_PAINT, wxPaintEventHandler(wxBitmapCanvas::OnPaint) );
1270 }
9adeb77a 1271
818d91a9
RR
1272 void OnPaint( wxPaintEvent &WXUNUSED(event) )
1273 {
1274 wxPaintDC dc(this);
1275 dc.DrawBitmap( m_bitmap, 0, 0);
1276 }
9adeb77a 1277
818d91a9
RR
1278 wxBitmap m_bitmap;
1279};
1280
1281class wxDataViewDropSource: public wxDropSource
1282{
1283public:
1284 wxDataViewDropSource( wxDataViewMainWindow *win, unsigned int row ) :
977a41ec 1285 wxDropSource( win )
818d91a9
RR
1286 {
1287 m_win = win;
1288 m_row = row;
1289 m_hint = NULL;
1290 }
9adeb77a 1291
818d91a9
RR
1292 ~wxDataViewDropSource()
1293 {
1294 delete m_hint;
1295 }
9adeb77a 1296
818d91a9
RR
1297 virtual bool GiveFeedback( wxDragResult WXUNUSED(effect) )
1298 {
1299 wxPoint pos = wxGetMousePosition();
9adeb77a 1300
818d91a9
RR
1301 if (!m_hint)
1302 {
1303 int liney = m_win->GetLineStart( m_row );
1304 int linex = 0;
1305 m_win->GetOwner()->CalcUnscrolledPosition( 0, liney, NULL, &liney );
1306 m_win->ClientToScreen( &linex, &liney );
1307 m_dist_x = pos.x - linex;
1308 m_dist_y = pos.y - liney;
9adeb77a 1309
818d91a9
RR
1310 int indent = 0;
1311 wxBitmap ib = m_win->CreateItemBitmap( m_row, indent );
1312 m_dist_x -= indent;
9adeb77a 1313 m_hint = new wxFrame( m_win->GetParent(), wxID_ANY, wxEmptyString,
977a41ec
FM
1314 wxPoint(pos.x - m_dist_x, pos.y + 5 ),
1315 ib.GetSize(),
1316 wxFRAME_TOOL_WINDOW |
1317 wxFRAME_FLOAT_ON_PARENT |
1318 wxFRAME_NO_TASKBAR |
1319 wxNO_BORDER );
818d91a9
RR
1320 new wxBitmapCanvas( m_hint, ib, ib.GetSize() );
1321 m_hint->Show();
1322 }
1323 else
1324 {
1325 m_hint->Move( pos.x - m_dist_x, pos.y + 5 );
1326 m_hint->SetTransparent( 128 );
1327 }
9adeb77a 1328
818d91a9
RR
1329 return false;
1330 }
9adeb77a 1331
818d91a9
RR
1332 wxDataViewMainWindow *m_win;
1333 unsigned int m_row;
1334 wxFrame *m_hint;
1335 int m_dist_x,m_dist_y;
1336};
1337
1338
a653c966
RR
1339class wxDataViewDropTarget: public wxDropTarget
1340{
1341public:
1342 wxDataViewDropTarget( wxDataObject *obj, wxDataViewMainWindow *win ) :
1343 wxDropTarget( obj )
1344 {
1345 m_win = win;
1346 }
1347
1348 virtual wxDragResult OnDragOver( wxCoord x, wxCoord y, wxDragResult def )
ce468dc2 1349 {
977a41ec
FM
1350 wxDataFormat format = GetMatchingPair();
1351 if (format == wxDF_INVALID)
1352 return wxDragNone;
1353 return m_win->OnDragOver( format, x, y, def);
ce468dc2 1354 }
9adeb77a 1355
a653c966 1356 virtual bool OnDrop( wxCoord x, wxCoord y )
ce468dc2 1357 {
977a41ec
FM
1358 wxDataFormat format = GetMatchingPair();
1359 if (format == wxDF_INVALID)
1360 return false;
1361 return m_win->OnDrop( format, x, y );
ce468dc2 1362 }
9adeb77a 1363
a653c966 1364 virtual wxDragResult OnData( wxCoord x, wxCoord y, wxDragResult def )
ce468dc2 1365 {
977a41ec
FM
1366 wxDataFormat format = GetMatchingPair();
1367 if (format == wxDF_INVALID)
1368 return wxDragNone;
1369 if (!GetData())
1370 return wxDragNone;
1371 return m_win->OnData( format, x, y, def );
ce468dc2 1372 }
9adeb77a 1373
a653c966
RR
1374 virtual void OnLeave()
1375 { m_win->OnLeave(); }
1376
1377 wxDataViewMainWindow *m_win;
1378};
1379
9adeb77a
VZ
1380#endif // wxUSE_DRAG_AND_DROP
1381
0fcce6b9
RR
1382//-----------------------------------------------------------------------------
1383// wxDataViewRenameTimer
1384//-----------------------------------------------------------------------------
1385
1386wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
1387{
1388 m_owner = owner;
1389}
1390
1391void wxDataViewRenameTimer::Notify()
1392{
1393 m_owner->OnRenameTimer();
1394}
1395
4ed7af08
RR
1396//-----------------------------------------------------------------------------
1397// wxDataViewMainWindow
1398//-----------------------------------------------------------------------------
1399
bc4f1ff2 1400// The tree building helper, declared firstly
10875c13 1401static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
bc4f1ff2 1402 wxDataViewTreeNode * node);
704c3490 1403
0a71f9e9 1404int LINKAGEMODE wxDataViewSelectionCmp( unsigned int row1, unsigned int row2 )
cab07038
RR
1405{
1406 if (row1 > row2) return 1;
1407 if (row1 == row2) return 0;
1408 return -1;
1409}
1410
45778c96 1411IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
4ed7af08
RR
1412
1413BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
1414 EVT_PAINT (wxDataViewMainWindow::OnPaint)
1415 EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
1416 EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
cab07038 1417 EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus)
64ac3db8 1418 EVT_CHAR_HOOK (wxDataViewMainWindow::OnCharHook)
cab07038 1419 EVT_CHAR (wxDataViewMainWindow::OnChar)
4ed7af08
RR
1420END_EVENT_TABLE()
1421
1422wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
1423 const wxPoint &pos, const wxSize &size, const wxString &name ) :
d81ad1f0 1424 wxWindow( parent, id, pos, size, wxWANTS_CHARS|wxBORDER_NONE, name ),
cab07038 1425 m_selection( wxDataViewSelectionCmp )
120b9b05 1426
4ed7af08
RR
1427{
1428 SetOwner( parent );
f554a14b 1429
64ac3db8
VZ
1430 m_editorRenderer = NULL;
1431
0fcce6b9
RR
1432 m_lastOnSame = false;
1433 m_renameTimer = new wxDataViewRenameTimer( this );
120b9b05 1434
0fcce6b9
RR
1435 // TODO: user better initial values/nothing selected
1436 m_currentCol = NULL;
1c8e71c9
VS
1437 m_currentColSetByKeyboard = false;
1438 m_useCellFocus = false;
ed3aece5 1439 m_currentRow = (unsigned)-1;
1af67319 1440 m_lineHeight = GetDefaultRowHeight();
e21f75bd 1441
9adeb77a 1442#if wxUSE_DRAG_AND_DROP
e21f75bd
RR
1443 m_dragCount = 0;
1444 m_dragStart = wxPoint(0,0);
120b9b05 1445
821baf7d
RR
1446 m_dragEnabled = false;
1447 m_dropEnabled = false;
9deec111
RR
1448 m_dropHint = false;
1449 m_dropHintLine = (unsigned int) -1;
9adeb77a
VZ
1450#endif // wxUSE_DRAG_AND_DROP
1451
1452 m_lineLastClicked = (unsigned int) -1;
1453 m_lineBeforeLastClicked = (unsigned int) -1;
1454 m_lineSelectSingleOnUp = (unsigned int) -1;
821baf7d 1455
cab07038 1456 m_hasFocus = false;
f554a14b 1457
72664514
RR
1458 SetBackgroundColour( *wxWHITE );
1459
cfa42cb8
JS
1460 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
1461
069b2e59 1462 m_penRule = wxPen(GetRuleColour());
9861f022 1463
bc4f1ff2
FM
1464 // compose a pen whichcan draw black lines
1465 // TODO: maybe there is something system colour to use
069b2e59 1466 m_penExpander = wxPen(wxColour(0,0,0));
bc4f1ff2 1467
422aa8ec 1468 m_root = wxDataViewTreeNode::CreateRootNode();
704c3490 1469
bc4f1ff2 1470 // Make m_count = -1 will cause the class recaculate the real displaying number of rows.
59e60167 1471 m_count = -1;
24c4a50f 1472 m_underMouse = NULL;
4b3feaa7 1473 UpdateDisplay();
4ed7af08
RR
1474}
1475
1476wxDataViewMainWindow::~wxDataViewMainWindow()
1477{
aba9bfd0 1478 DestroyTree();
0fcce6b9
RR
1479 delete m_renameTimer;
1480}
1481
9adeb77a 1482
1af67319
VZ
1483int wxDataViewMainWindow::GetDefaultRowHeight() const
1484{
1485#ifdef __WXMSW__
1486 // We would like to use the same line height that Explorer uses. This is
1487 // different from standard ListView control since Vista.
1488 if ( wxGetWinVersion() >= wxWinVersion_Vista )
1489 return wxMax(16, GetCharHeight()) + 6; // 16 = mini icon height
1490 else
1491#endif // __WXMSW__
1492 return wxMax(16, GetCharHeight()) + 1; // 16 = mini icon height
1493}
1494
1495
1496
9adeb77a 1497#if wxUSE_DRAG_AND_DROP
a653c966
RR
1498bool wxDataViewMainWindow::EnableDragSource( const wxDataFormat &format )
1499{
1500 m_dragFormat = format;
1501 m_dragEnabled = format != wxDF_INVALID;
9adeb77a 1502
a653c966
RR
1503 return true;
1504}
9adeb77a 1505
a653c966
RR
1506bool wxDataViewMainWindow::EnableDropTarget( const wxDataFormat &format )
1507{
1508 m_dropFormat = format;
1509 m_dropEnabled = format != wxDF_INVALID;
9adeb77a 1510
a653c966
RR
1511 if (m_dropEnabled)
1512 SetDropTarget( new wxDataViewDropTarget( new wxCustomDataObject( format ), this ) );
9adeb77a 1513
a653c966
RR
1514 return true;
1515}
1516
9deec111
RR
1517void wxDataViewMainWindow::RemoveDropHint()
1518{
1519 if (m_dropHint)
1520 {
1521 m_dropHint = false;
1522 RefreshRow( m_dropHintLine );
1523 m_dropHintLine = (unsigned int) -1;
1524 }
1525}
1526
4cef3873 1527wxDragResult wxDataViewMainWindow::OnDragOver( wxDataFormat format, wxCoord x,
bc4f1ff2 1528 wxCoord y, wxDragResult def )
a653c966
RR
1529{
1530 int xx = x;
1531 int yy = y;
1532 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1533 unsigned int row = GetLineAt( yy );
1534
aecf930c 1535 if ((row >= GetRowCount()) || (xx > GetEndOfLastCol()))
9adeb77a 1536 {
9deec111 1537 RemoveDropHint();
a653c966 1538 return wxDragNone;
9deec111 1539 }
a653c966
RR
1540
1541 wxDataViewItem item = GetItemByRow( row );
9adeb77a 1542
c2c89730 1543 wxDataViewModel *model = GetModel();
9adeb77a 1544
a653c966
RR
1545 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
1546 event.SetEventObject( m_owner );
1547 event.SetItem( item );
1548 event.SetModel( model );
1549 event.SetDataFormat( format );
c04be1a2 1550 event.SetDropEffect( def );
a653c966 1551 if (!m_owner->HandleWindowEvent( event ))
9deec111
RR
1552 {
1553 RemoveDropHint();
a653c966 1554 return wxDragNone;
9deec111 1555 }
a653c966
RR
1556
1557 if (!event.IsAllowed())
9deec111
RR
1558 {
1559 RemoveDropHint();
a653c966 1560 return wxDragNone;
9deec111
RR
1561 }
1562
9adeb77a 1563
9deec111
RR
1564 if (m_dropHint && (row != m_dropHintLine))
1565 RefreshRow( m_dropHintLine );
1566 m_dropHint = true;
1567 m_dropHintLine = row;
1568 RefreshRow( row );
9adeb77a 1569
a653c966
RR
1570 return def;
1571}
1572
1573bool wxDataViewMainWindow::OnDrop( wxDataFormat format, wxCoord x, wxCoord y )
1574{
9deec111
RR
1575 RemoveDropHint();
1576
a653c966
RR
1577 int xx = x;
1578 int yy = y;
1579 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1580 unsigned int row = GetLineAt( yy );
1581
aecf930c 1582 if ((row >= GetRowCount()) || (xx > GetEndOfLastCol()))
a653c966
RR
1583 return false;
1584
1585 wxDataViewItem item = GetItemByRow( row );
9adeb77a 1586
c2c89730 1587 wxDataViewModel *model = GetModel();
9adeb77a 1588
a653c966
RR
1589 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner->GetId() );
1590 event.SetEventObject( m_owner );
1591 event.SetItem( item );
1592 event.SetModel( model );
1593 event.SetDataFormat( format );
1594 if (!m_owner->HandleWindowEvent( event ))
1595 return false;
1596
1597 if (!event.IsAllowed())
1598 return false;
9deec111 1599
a653c966
RR
1600 return true;
1601}
1602
4cef3873 1603wxDragResult wxDataViewMainWindow::OnData( wxDataFormat format, wxCoord x, wxCoord y,
bc4f1ff2 1604 wxDragResult def )
a653c966
RR
1605{
1606 int xx = x;
1607 int yy = y;
1608 m_owner->CalcUnscrolledPosition( xx, yy, &xx, &yy );
1609 unsigned int row = GetLineAt( yy );
1610
aecf930c 1611 if ((row >= GetRowCount()) || (xx > GetEndOfLastCol()))
a653c966
RR
1612 return wxDragNone;
1613
1614 wxDataViewItem item = GetItemByRow( row );
9adeb77a 1615
c2c89730 1616 wxDataViewModel *model = GetModel();
9adeb77a 1617
a653c966
RR
1618 wxCustomDataObject *obj = (wxCustomDataObject *) GetDropTarget()->GetDataObject();
1619
1620 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_DROP, m_owner->GetId() );
1621 event.SetEventObject( m_owner );
1622 event.SetItem( item );
1623 event.SetModel( model );
1624 event.SetDataFormat( format );
1625 event.SetDataSize( obj->GetSize() );
1626 event.SetDataBuffer( obj->GetData() );
c04be1a2 1627 event.SetDropEffect( def );
a653c966
RR
1628 if (!m_owner->HandleWindowEvent( event ))
1629 return wxDragNone;
1630
1631 if (!event.IsAllowed())
1632 return wxDragNone;
1633
1634 return def;
1635}
1636
1637void wxDataViewMainWindow::OnLeave()
1638{
9deec111 1639 RemoveDropHint();
a653c966
RR
1640}
1641
818d91a9
RR
1642wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent )
1643{
1644 int height = GetLineHeight( row );
1645 int width = 0;
1646 unsigned int cols = GetOwner()->GetColumnCount();
1647 unsigned int col;
1648 for (col = 0; col < cols; col++)
1649 {
1650 wxDataViewColumn *column = GetOwner()->GetColumnAt(col);
1651 if (column->IsHidden())
1652 continue; // skip it!
1653 width += column->GetWidth();
1654 }
1655
1656 indent = 0;
ce5abbf9 1657 if (!IsList())
818d91a9
RR
1658 {
1659 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
1660 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
4cef3873 1661 indent = indent + m_lineHeight;
bc4f1ff2 1662 // try to use the m_lineHeight as the expander space
818d91a9
RR
1663 }
1664 width -= indent;
1665
1666 wxBitmap bitmap( width, height );
1667 wxMemoryDC dc( bitmap );
1668 dc.SetFont( GetFont() );
1669 dc.SetPen( *wxBLACK_PEN );
1670 dc.SetBrush( *wxWHITE_BRUSH );
1671 dc.DrawRectangle( 0,0,width,height );
9adeb77a 1672
818d91a9 1673 wxDataViewModel *model = m_owner->GetModel();
9adeb77a 1674
1841f079
VZ
1675 wxDataViewColumn * const
1676 expander = GetExpanderColumnOrFirstOne(GetOwner());
9adeb77a 1677
818d91a9
RR
1678 int x = 0;
1679 for (col = 0; col < cols; col++)
1680 {
1681 wxDataViewColumn *column = GetOwner()->GetColumnAt( col );
1682 wxDataViewRenderer *cell = column->GetRenderer();
1683
1684 if (column->IsHidden())
1685 continue; // skip it!
1686
1687 width = column->GetWidth();
9adeb77a 1688
818d91a9
RR
1689 if (column == expander)
1690 width -= indent;
9adeb77a 1691
818d91a9 1692 wxDataViewItem item = GetItemByRow( row );
f0ccd2cb 1693 cell->PrepareForItem(model, item, column->GetModelColumn());
62265c2c 1694
a6f1201f
VZ
1695 wxRect item_rect(x, 0, width, height);
1696 item_rect.Deflate(PADDING_RIGHTLEFT, 0);
818d91a9 1697
bc4f1ff2 1698 // dc.SetClippingRegion( item_rect );
62265c2c 1699 cell->WXCallRender(item_rect, &dc, 0);
bc4f1ff2 1700 // dc.DestroyClippingRegion();
9adeb77a 1701
818d91a9
RR
1702 x += width;
1703 }
9adeb77a 1704
818d91a9
RR
1705 return bitmap;
1706}
1707
9adeb77a
VZ
1708#endif // wxUSE_DRAG_AND_DROP
1709
1710
1c8e71c9
VS
1711// Draw focus rect for individual cell. Unlike native focus rect, we render
1712// this in foreground text color (typically white) to enhance contrast and
1713// make it visible.
1714static void DrawSelectedCellFocusRect(wxDC& dc, const wxRect& rect)
1715{
1716 // (This code is based on wxRendererGeneric::DrawFocusRect and modified.)
1717
1718 // draw the pixels manually because the "dots" in wxPen with wxDOT style
1719 // may be short traits and not really dots
1720 //
1721 // note that to behave in the same manner as DrawRect(), we must exclude
1722 // the bottom and right borders from the rectangle
1723 wxCoord x1 = rect.GetLeft(),
1724 y1 = rect.GetTop(),
1725 x2 = rect.GetRight(),
1726 y2 = rect.GetBottom();
1727
1728 wxDCPenChanger pen(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
1729
1730 wxCoord z;
1731 for ( z = x1 + 1; z < x2; z += 2 )
1732 dc.DrawPoint(z, rect.GetTop());
1733
1734 wxCoord shift = z == x2 ? 0 : 1;
1735 for ( z = y1 + shift; z < y2; z += 2 )
1736 dc.DrawPoint(x2, z);
1737
1738 shift = z == y2 ? 0 : 1;
1739 for ( z = x2 - shift; z > x1; z -= 2 )
1740 dc.DrawPoint(z, y2);
1741
1742 shift = z == x1 ? 0 : 1;
1743 for ( z = y2 - shift; z > y1; z -= 2 )
1744 dc.DrawPoint(x1, z);
1745}
1746
1747
1117d56f 1748void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
0fcce6b9 1749{
c2c89730 1750 wxDataViewModel *model = GetModel();
1117d56f 1751 wxAutoBufferedPaintDC dc( this );
0fcce6b9 1752
bb58fa37 1753 dc.SetBrush(GetOwner()->GetBackgroundColour());
1117d56f 1754 dc.SetPen( *wxTRANSPARENT_PEN );
bb58fa37 1755 dc.DrawRectangle(GetClientSize());
1117d56f 1756
da87ce5a
VZ
1757 if ( IsEmpty() )
1758 {
1759 // No items to draw.
1760 return;
1761 }
1762
1117d56f
RR
1763 // prepare the DC
1764 GetOwner()->PrepareDC( dc );
1765 dc.SetFont( GetFont() );
1766
1767 wxRect update = GetUpdateRegion().GetBox();
1768 m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
1769
1770 // compute which items needs to be redrawn
344ed1f3 1771 unsigned int item_start = GetLineAt( wxMax(0,update.y) );
1117d56f 1772 unsigned int item_count =
344ed1f3 1773 wxMin( (int)( GetLineAt( wxMax(0,update.y+update.height) ) - item_start + 1),
977a41ec 1774 (int)(GetRowCount( ) - item_start));
1117d56f 1775 unsigned int item_last = item_start + item_count;
99f44d97
VZ
1776
1777 // Send the event to wxDataViewCtrl itself.
1778 wxWindow * const parent = GetParent();
47a8b1e1 1779 wxDataViewEvent cache_event(wxEVT_COMMAND_DATAVIEW_CACHE_HINT, parent->GetId());
99f44d97 1780 cache_event.SetEventObject(parent);
47a8b1e1
VZ
1781 cache_event.SetCache(item_start, item_last - 1);
1782 parent->ProcessWindowEvent(cache_event);
1117d56f
RR
1783
1784 // compute which columns needs to be redrawn
9861f022 1785 unsigned int cols = GetOwner()->GetColumnCount();
f03c22f9
VZ
1786 if ( !cols )
1787 {
1788 // we assume that we have at least one column below and painting an
1789 // empty control is unnecessary anyhow
1790 return;
1791 }
1792
1117d56f 1793 unsigned int col_start = 0;
e822d1bd 1794 unsigned int x_start;
1117d56f 1795 for (x_start = 0; col_start < cols; col_start++)
0fcce6b9 1796 {
702f5349 1797 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_start);
1117d56f 1798 if (col->IsHidden())
9861f022
RR
1799 continue; // skip it!
1800
1117d56f
RR
1801 unsigned int w = col->GetWidth();
1802 if (x_start+w >= (unsigned int)update.x)
0fcce6b9 1803 break;
0fcce6b9 1804
1117d56f
RR
1805 x_start += w;
1806 }
1e510b1e 1807
1117d56f
RR
1808 unsigned int col_last = col_start;
1809 unsigned int x_last = x_start;
1810 for (; col_last < cols; col_last++)
1811 {
702f5349 1812 wxDataViewColumn *col = GetOwner()->GetColumnAt(col_last);
1117d56f
RR
1813 if (col->IsHidden())
1814 continue; // skip it!
afebb87b 1815
1117d56f
RR
1816 if (x_last > (unsigned int)update.GetRight())
1817 break;
4ed7af08 1818
1117d56f
RR
1819 x_last += col->GetWidth();
1820 }
d5025dc0 1821
4bdc891f
VZ
1822 // Draw background of alternate rows specially if required
1823 if ( m_owner->HasFlag(wxDV_ROW_LINES) )
1824 {
1825 wxColour altRowColour = m_owner->m_alternateRowColour;
1826 if ( !altRowColour.IsOk() )
1827 {
1828 // Determine the alternate rows colour automatically from the
1829 // background colour.
1830 const wxColour bgColour = m_owner->GetBackgroundColour();
1831
1832 // Depending on the background, alternate row color
1833 // will be 3% more dark or 50% brighter.
1834 int alpha = bgColour.GetRGB() > 0x808080 ? 97 : 150;
1835 altRowColour = bgColour.ChangeLightness(alpha);
1836 }
1837
1838 dc.SetPen(*wxTRANSPARENT_PEN);
1839 dc.SetBrush(wxBrush(altRowColour));
1840
1841 for (unsigned int item = item_start; item < item_last; item++)
1842 {
1843 if ( item % 2 )
1844 {
1845 dc.DrawRectangle(x_start,
1846 GetLineStart(item),
1847 GetClientSize().GetWidth(),
1848 GetLineHeight(item));
1849 }
1850 }
1851 }
1852
1117d56f
RR
1853 // Draw horizontal rules if required
1854 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
1855 {
1856 dc.SetPen(m_penRule);
1857 dc.SetBrush(*wxTRANSPARENT_BRUSH);
aba9bfd0 1858
de4bf0b3 1859 for (unsigned int i = item_start; i <= item_last; i++)
1117d56f 1860 {
344ed1f3 1861 int y = GetLineStart( i );
1117d56f
RR
1862 dc.DrawLine(x_start, y, x_last, y);
1863 }
1864 }
aba9bfd0 1865
1117d56f
RR
1866 // Draw vertical rules if required
1867 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
b7e9f8b1 1868 {
1117d56f
RR
1869 dc.SetPen(m_penRule);
1870 dc.SetBrush(*wxTRANSPARENT_BRUSH);
b7e9f8b1 1871
3999336c
VS
1872 // NB: Vertical rules are drawn in the last pixel of a column so that
1873 // they align perfectly with native MSW wxHeaderCtrl as well as for
1874 // consistency with MSW native list control. There's no vertical
1875 // rule at the most-left side of the control.
1876
1877 int x = x_start - 1;
1117d56f
RR
1878 for (unsigned int i = col_start; i < col_last; i++)
1879 {
702f5349 1880 wxDataViewColumn *col = GetOwner()->GetColumnAt(i);
1117d56f
RR
1881 if (col->IsHidden())
1882 continue; // skip it
d47db7e0 1883
3999336c
VS
1884 x += col->GetWidth();
1885
344ed1f3
RR
1886 dc.DrawLine(x, GetLineStart( item_start ),
1887 x, GetLineStart( item_last ) );
1117d56f 1888 }
1117d56f
RR
1889 }
1890
1891 // redraw the background for the items which are selected/current
1892 for (unsigned int item = item_start; item < item_last; item++)
aba9bfd0 1893 {
1117d56f 1894 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
1c8e71c9 1895
1117d56f 1896 if (selected || item == m_currentRow)
d5025dc0 1897 {
e3dbeaaf
VZ
1898 wxRect rect( x_start, GetLineStart( item ),
1899 x_last - x_start, GetLineHeight( item ) );
1c8e71c9
VS
1900
1901 // draw selection and whole-item focus:
1902 if ( selected )
1903 {
1904 int flags = wxCONTROL_SELECTED;
1905 if (m_hasFocus)
1906 flags |= wxCONTROL_FOCUSED;
1907
1908 wxRendererNative::Get().DrawItemSelectionRect
1909 (
1910 this,
1911 dc,
1912 rect,
1913 flags
1914 );
1915 }
1916
1917 // draw keyboard focus rect if applicable
1918 if ( item == m_currentRow && m_hasFocus )
1919 {
1920 bool renderColumnFocus = false;
1921
1922 if ( m_useCellFocus && m_currentCol && m_currentColSetByKeyboard )
1923 {
1924 renderColumnFocus = true;
1925
1926 // If this is container node without columns, render full-row focus:
1927 if ( !IsList() )
1928 {
1929 wxDataViewTreeNode *node = GetTreeNodeByRow(item);
1930 if ( node->HasChildren() && !model->HasContainerColumns(node->GetItem()) )
1931 renderColumnFocus = false;
1932 }
1933 }
1934
1935 if ( renderColumnFocus )
1936 {
1937 for ( unsigned int i = col_start; i < col_last; i++ )
1938 {
1939 wxDataViewColumn *col = GetOwner()->GetColumnAt(i);
1940 if ( col->IsHidden() )
1941 continue;
1942
1943 rect.width = col->GetWidth();
1944
1945 if ( col == m_currentCol )
1946 {
1947 // make the rect more visible by adding a small
1948 // margin around it:
1949 rect.Deflate(1, 1);
1950
1951 if ( selected )
1952 {
1953 // DrawFocusRect() uses XOR and is all but
1954 // invisible against dark-blue background. Use
1955 // the same color used for selected text.
1956 DrawSelectedCellFocusRect(dc, rect);
1957 }
1958 else
1959 {
1960 wxRendererNative::Get().DrawFocusRect
1961 (
1962 this,
1963 dc,
1964 rect,
1965 0
1966 );
1967 }
1968 break;
1969 }
1970
1971 rect.x += rect.width;
1972 }
1973 }
1974 else
1975 {
1976 // render focus rectangle for the whole row
1977 wxRendererNative::Get().DrawFocusRect
1978 (
1979 this,
1980 dc,
1981 rect,
1982 selected ? (int)wxCONTROL_SELECTED : 0
1983 );
1984 }
1985 }
d47db7e0 1986 }
aba9bfd0 1987 }
9adeb77a
VZ
1988
1989#if wxUSE_DRAG_AND_DROP
9deec111 1990 if (m_dropHint)
9adeb77a
VZ
1991 {
1992 wxRect rect( x_start, GetLineStart( m_dropHintLine ),
e3dbeaaf 1993 x_last - x_start, GetLineHeight( m_dropHintLine ) );
9deec111
RR
1994 dc.SetPen( *wxBLACK_PEN );
1995 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1996 dc.DrawRectangle( rect );
1997 }
9adeb77a 1998#endif // wxUSE_DRAG_AND_DROP
a0f3af5f 1999
1841f079
VZ
2000 wxDataViewColumn * const
2001 expander = GetExpanderColumnOrFirstOne(GetOwner());
d47db7e0 2002
344ed1f3 2003 // redraw all cells for all rows which must be repainted and all columns
1117d56f
RR
2004 wxRect cell_rect;
2005 cell_rect.x = x_start;
1117d56f 2006 for (unsigned int i = col_start; i < col_last; i++)
d47db7e0 2007 {
702f5349 2008 wxDataViewColumn *col = GetOwner()->GetColumnAt( i );
1117d56f
RR
2009 wxDataViewRenderer *cell = col->GetRenderer();
2010 cell_rect.width = col->GetWidth();
d47db7e0 2011
b10c4089 2012 if ( col->IsHidden() || cell_rect.width <= 0 )
344ed1f3 2013 continue; // skip it!
fbda518c 2014
1117d56f
RR
2015 for (unsigned int item = item_start; item < item_last; item++)
2016 {
2017 // get the cell value and set it into the renderer
1117d56f
RR
2018 wxDataViewTreeNode *node = NULL;
2019 wxDataViewItem dataitem;
cfa42cb8 2020
344ed1f3 2021 if (!IsVirtualList())
1117d56f
RR
2022 {
2023 node = GetTreeNodeByRow(item);
2024 if( node == NULL )
2025 continue;
a0f3af5f 2026
1117d56f 2027 dataitem = node->GetItem();
704c3490 2028
311c4a7a
VZ
2029 // Skip all columns of "container" rows except the expander
2030 // column itself unless HasContainerColumns() overrides this.
1841f079 2031 if ( col != expander &&
311c4a7a
VZ
2032 model->IsContainer(dataitem) &&
2033 !model->HasContainerColumns(dataitem) )
1117d56f
RR
2034 continue;
2035 }
2036 else
2037 {
86ba79ed 2038 dataitem = wxDataViewItem( wxUIntToPtr(item+1) );
1117d56f 2039 }
8b6cf9bf 2040
f0ccd2cb 2041 cell->PrepareForItem(model, dataitem, col->GetModelColumn());
62265c2c 2042
c9287446 2043 // update cell_rect
344ed1f3 2044 cell_rect.y = GetLineStart( item );
bc4f1ff2 2045 cell_rect.height = GetLineHeight( item );
8b6cf9bf 2046
1c959a62
VZ
2047 // draw the background
2048 bool selected = m_selection.Index( item ) != wxNOT_FOUND;
2049 if ( !selected )
2050 DrawCellBackground( cell, dc, cell_rect );
2051
b10c4089 2052 // deal with the expander
1117d56f 2053 int indent = 0;
ce5abbf9 2054 if ((!IsList()) && (col == expander))
1117d56f 2055 {
bc4f1ff2 2056 // Calculate the indent first
b10c4089 2057 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
bc4f1ff2 2058
b10c4089
VZ
2059 // we reserve m_lineHeight of horizontal space for the expander
2060 // but leave EXPANDER_MARGIN around the expander itself
2061 int exp_x = cell_rect.x + indent + EXPANDER_MARGIN;
bc4f1ff2 2062
b10c4089 2063 indent += m_lineHeight;
bc4f1ff2 2064
b10c4089
VZ
2065 // draw expander if needed and visible
2066 if ( node->HasChildren() && exp_x < cell_rect.GetRight() )
1117d56f 2067 {
b10c4089
VZ
2068 dc.SetPen( m_penExpander );
2069 dc.SetBrush( wxNullBrush );
2070
2071 int exp_size = m_lineHeight - 2*EXPANDER_MARGIN;
2072 int exp_y = cell_rect.y + (cell_rect.height - exp_size)/2
2073 + EXPANDER_MARGIN - EXPANDER_OFFSET;
2074
2075 const wxRect rect(exp_x, exp_y, exp_size, exp_size);
2076
1117d56f 2077 int flag = 0;
b10c4089 2078 if ( m_underMouse == node )
1117d56f 2079 flag |= wxCONTROL_CURRENT;
b10c4089
VZ
2080 if ( node->IsOpen() )
2081 flag |= wxCONTROL_EXPANDED;
2082
2083 // ensure that we don't overflow the cell (which might
2084 // happen if the column is very narrow)
2085 wxDCClipper clip(dc, cell_rect);
2086
2087 wxRendererNative::Get().DrawTreeItemButton( this, dc, rect, flag);
1117d56f 2088 }
bc4f1ff2
FM
2089
2090 // force the expander column to left-center align
977a41ec 2091 cell->SetAlignment( wxALIGN_CENTER_VERTICAL );
1117d56f 2092 }
1117d56f 2093
a6f1201f
VZ
2094 wxRect item_rect = cell_rect;
2095 item_rect.Deflate(PADDING_RIGHTLEFT, 0);
2096
2097 // account for the tree indent (harmless if we're not indented)
1117d56f 2098 item_rect.x += indent;
a6f1201f 2099 item_rect.width -= indent;
1117d56f 2100
b10c4089
VZ
2101 if ( item_rect.width <= 0 )
2102 continue;
2103
1117d56f 2104 int state = 0;
1c959a62 2105 if (m_hasFocus && selected)
1117d56f
RR
2106 state |= wxDATAVIEW_CELL_SELECTED;
2107
2108 // TODO: it would be much more efficient to create a clipping
2109 // region for the entire column being rendered (in the OnPaint
2110 // of wxDataViewMainWindow) instead of a single clip region for
2111 // each cell. However it would mean that each renderer should
2112 // respect the given wxRect's top & bottom coords, eventually
2113 // violating only the left & right coords - however the user can
2114 // make its own renderer and thus we cannot be sure of that.
a6f1201f 2115 wxDCClipper clip(dc, item_rect);
2d0d7813 2116
62265c2c 2117 cell->WXCallRender(item_rect, &dc, state);
1117d56f
RR
2118 }
2119
2120 cell_rect.x += cell_rect.width;
2121 }
2122}
2123
1c959a62
VZ
2124
2125void wxDataViewMainWindow::DrawCellBackground( wxDataViewRenderer* cell, wxDC& dc, const wxRect& rect )
2126{
2127 wxRect rectBg( rect );
2128
2129 // don't overlap the horizontal rules
2130 if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
2131 {
2132 rectBg.x++;
2133 rectBg.width--;
2134 }
2135
2136 // don't overlap the vertical rules
2137 if ( m_owner->HasFlag(wxDV_VERT_RULES) )
2138 {
2139 rectBg.y++;
2140 rectBg.height--;
2141 }
2142
2143 cell->RenderBackground(&dc, rectBg);
2144}
2145
1117d56f
RR
2146void wxDataViewMainWindow::OnRenameTimer()
2147{
2148 // We have to call this here because changes may just have
2149 // been made and no screen update taken place.
2150 if ( m_dirty )
977a41ec
FM
2151 {
2152 // TODO: use wxTheApp->SafeYieldFor(NULL, wxEVT_CATEGORY_UI) instead
2153 // (needs to be tested!)
1117d56f 2154 wxSafeYield();
977a41ec 2155 }
1117d56f 2156
0a807957 2157 wxDataViewItem item = GetItemByRow( m_currentRow );
1117d56f 2158
64ac3db8
VZ
2159 StartEditing( item, m_currentCol );
2160}
1117d56f 2161
64ac3db8
VZ
2162void
2163wxDataViewMainWindow::StartEditing(const wxDataViewItem& item,
2164 const wxDataViewColumn* col)
2165{
2166 wxDataViewRenderer* renderer = col->GetRenderer();
cf5d4c76 2167 if ( !IsCellEditableInMode(item, col, wxDATAVIEW_CELL_EDITABLE) )
64ac3db8
VZ
2168 return;
2169
2170 const wxRect itemRect = GetItemRect(item, col);
2171 if ( renderer->StartEditing(item, itemRect) )
2172 {
2173 // Save the renderer to be able to finish/cancel editing it later and
2174 // save the control to be able to detect if we're still editing it.
2175 m_editorRenderer = renderer;
2176 m_editorCtrl = renderer->GetEditorCtrl();
2177 }
1117d56f
RR
2178}
2179
bc4f1ff2 2180//-----------------------------------------------------------------------------
1117d56f 2181// Helper class for do operation on the tree node
bc4f1ff2 2182//-----------------------------------------------------------------------------
1117d56f
RR
2183class DoJob
2184{
2185public:
59e60167
VZ
2186 DoJob() { }
2187 virtual ~DoJob() { }
1117d56f 2188
bc4f1ff2 2189 // The return value control how the tree-walker tranverse the tree
d54f0605
VS
2190 enum
2191 {
2192 DONE, // Job done, stop traversing and return
2193 SKIP_SUBTREE, // Ignore the current node's subtree and continue
2194 CONTINUE // Job not done, continue
2195 };
2196
59e60167 2197 virtual int operator() ( wxDataViewTreeNode * node ) = 0;
1117d56f
RR
2198};
2199
2200bool Walker( wxDataViewTreeNode * node, DoJob & func )
2201{
422aa8ec 2202 wxCHECK_MSG( node, false, "can't walk NULL node" );
1117d56f
RR
2203
2204 switch( func( node ) )
2205 {
d54f0605 2206 case DoJob::DONE:
59e60167 2207 return true;
d54f0605 2208 case DoJob::SKIP_SUBTREE:
1117d56f 2209 return false;
d54f0605 2210 case DoJob::CONTINUE:
422aa8ec 2211 break;
1117d56f
RR
2212 }
2213
d0cfefc4 2214 if ( node->HasChildren() )
1117d56f 2215 {
ff3c5ad3 2216 const wxDataViewTreeNodes& nodes = node->GetChildNodes();
d0cfefc4
VS
2217
2218 for ( wxDataViewTreeNodes::const_iterator i = nodes.begin();
2219 i != nodes.end();
2220 ++i )
2221 {
2222 if ( Walker(*i, func) )
2223 return true;
2224 }
1117d56f 2225 }
422aa8ec 2226
1117d56f
RR
2227 return false;
2228}
2229
2230bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxDataViewItem & item)
2231{
86ba79ed 2232 if (IsVirtualList())
1117d56f 2233 {
49d2b75d 2234 wxDataViewVirtualListModel *list_model =
c2c89730 2235 (wxDataViewVirtualListModel*) GetModel();
49d2b75d 2236 m_count = list_model->GetCount();
1117d56f 2237 }
0b93babd
VS
2238 else
2239 {
2240 SortPrepare();
cfa42cb8 2241
422aa8ec 2242 wxDataViewTreeNode *parentNode = FindNode(parent);
1117d56f 2243
422aa8ec 2244 if ( !parentNode )
0b93babd 2245 return false;
1117d56f 2246
5bb82ad4
VS
2247 wxDataViewItemArray modelSiblings;
2248 GetModel()->GetChildren(parent, modelSiblings);
2249 const int modelSiblingsSize = modelSiblings.size();
2250
2251 int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
2252 wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
35219832 2253
422aa8ec 2254 wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item);
c2c89730 2255 itemNode->SetHasChildren(GetModel()->IsContainer(item));
1117d56f 2256
422aa8ec 2257 parentNode->SetHasChildren(true);
5bb82ad4
VS
2258
2259 const wxDataViewTreeNodes& nodeSiblings = parentNode->GetChildNodes();
2260 const int nodeSiblingsSize = nodeSiblings.size();
2261
2262 int nodePos = 0;
2263
2264 if ( posInModel == modelSiblingsSize - 1 )
2265 {
2266 nodePos = nodeSiblingsSize;
2267 }
2268 else if ( modelSiblingsSize == nodeSiblingsSize + 1 )
2269 {
2270 // This is the simple case when our node tree already matches the
2271 // model and only this one item is missing.
2272 nodePos = posInModel;
2273 }
2274 else
2275 {
2276 // It's possible that a larger discrepancy between the model and
2277 // our realization exists. This can happen e.g. when adding a bunch
2278 // of items to the model and then calling ItemsAdded() just once
2279 // afterwards. In this case, we must find the right position by
2280 // looking at sibling items.
2281
2282 // append to the end if we won't find a better position:
2283 nodePos = nodeSiblingsSize;
2284
2285 for ( int nextItemPos = posInModel + 1;
2286 nextItemPos < modelSiblingsSize;
2287 nextItemPos++ )
2288 {
2289 int nextNodePos = parentNode->FindChildByItem(modelSiblings[nextItemPos]);
2290 if ( nextNodePos != wxNOT_FOUND )
2291 {
2292 nodePos = nextNodePos;
2293 break;
2294 }
2295 }
2296 }
2297
422aa8ec 2298 parentNode->ChangeSubTreeCount(+1);
5bb82ad4 2299 parentNode->InsertChild(itemNode, nodePos);
1117d56f 2300
0b93babd 2301 m_count = -1;
1117d56f 2302 }
1117d56f 2303
bed74e48 2304 GetOwner()->InvalidateColBestWidths();
1117d56f
RR
2305 UpdateDisplay();
2306
2307 return true;
2308}
2309
1117d56f 2310bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
86ba79ed 2311 const wxDataViewItem& item)
1117d56f 2312{
86ba79ed 2313 if (IsVirtualList())
1117d56f 2314 {
49d2b75d 2315 wxDataViewVirtualListModel *list_model =
c2c89730 2316 (wxDataViewVirtualListModel*) GetModel();
49d2b75d 2317 m_count = list_model->GetCount();
03647350 2318
57ab4546
VS
2319 if ( !m_selection.empty() )
2320 {
2321 const int row = GetRowByItem(item);
2322
f6410588
VS
2323 int rowIndexInSelection = wxNOT_FOUND;
2324
57ab4546
VS
2325 const size_t selCount = m_selection.size();
2326 for ( size_t i = 0; i < selCount; i++ )
2327 {
f6410588
VS
2328 if ( m_selection[i] == (unsigned)row )
2329 rowIndexInSelection = i;
2330 else if ( m_selection[i] > (unsigned)row )
57ab4546
VS
2331 m_selection[i]--;
2332 }
2333
f6410588
VS
2334 if ( rowIndexInSelection != wxNOT_FOUND )
2335 m_selection.RemoveAt(rowIndexInSelection);
57ab4546
VS
2336 }
2337
1117d56f 2338 }
b6252949
VS
2339 else // general case
2340 {
422aa8ec 2341 wxDataViewTreeNode *parentNode = FindNode(parent);
1117d56f 2342
b6252949
VS
2343 // Notice that it is possible that the item being deleted is not in the
2344 // tree at all, for example we could be deleting a never shown (because
2345 // collapsed) item in a tree model. So it's not an error if we don't know
2346 // about this item, just return without doing anything then.
b632efe0 2347 if ( !parentNode )
28fefd45 2348 return true;
b23de238 2349
d0cfefc4 2350 wxCHECK_MSG( parentNode->HasChildren(), false, "parent node doesn't have children?" );
ff3c5ad3 2351 const wxDataViewTreeNodes& parentsChildren = parentNode->GetChildNodes();
351461fc 2352
422aa8ec
VS
2353 // We can't use FindNode() to find 'item', because it was already
2354 // removed from the model by the time ItemDeleted() is called, so we
2355 // have to do it manually. We keep track of its position as well for
2356 // later use.
2357 int itemPosInNode = 0;
57ab4546 2358 wxDataViewTreeNode *itemNode = NULL;
422aa8ec
VS
2359 for ( wxDataViewTreeNodes::const_iterator i = parentsChildren.begin();
2360 i != parentsChildren.end();
2361 ++i, ++itemPosInNode )
d47db7e0 2362 {
422aa8ec 2363 if( (*i)->GetItem() == item )
d47db7e0 2364 {
422aa8ec 2365 itemNode = *i;
d47db7e0
RR
2366 break;
2367 }
2368 }
57ab4546 2369
422aa8ec
VS
2370 // If the parent wasn't expanded, it's possible that we didn't have a
2371 // node corresponding to 'item' and so there's nothing left to do.
2372 if ( !itemNode )
b6252949 2373 {
422aa8ec
VS
2374 // If this was the last child to be removed, it's possible the parent
2375 // node became a leaf. Let's ask the model about it.
ff3c5ad3 2376 if ( parentNode->GetChildNodes().empty() )
c2c89730 2377 parentNode->SetHasChildren(GetModel()->IsContainer(parent));
a8505db0 2378
28fefd45 2379 return true;
b6252949 2380 }
57ab4546 2381
422aa8ec
VS
2382 // Delete the item from wxDataViewTreeNode representation:
2383 const int itemsDeleted = 1 + itemNode->GetSubTreeCount();
2384
a2d3c415
VS
2385 parentNode->RemoveChild(itemNode);
2386 delete itemNode;
422aa8ec
VS
2387 parentNode->ChangeSubTreeCount(-itemsDeleted);
2388
b6252949
VS
2389 // Make the row number invalid and get a new valid one when user call GetRowCount
2390 m_count = -1;
422aa8ec
VS
2391
2392 // If this was the last child to be removed, it's possible the parent
2393 // node became a leaf. Let's ask the model about it.
ff3c5ad3 2394 if ( parentNode->GetChildNodes().empty() )
c3b504ad
VZ
2395 {
2396 bool isContainer = GetModel()->IsContainer(parent);
2397 parentNode->SetHasChildren(isContainer);
2398 if ( isContainer )
2399 {
2400 // If it's still a container, make sure we show "+" icon for it
2401 // and not "-" one as there is nothing to collapse any more.
2402 if ( parentNode->IsOpen() )
2403 parentNode->ToggleOpen();
2404 }
2405 }
57ab4546
VS
2406
2407 // Update selection by removing 'item' and its entire children tree from the selection.
2408 if ( !m_selection.empty() )
2409 {
2410 // we can't call GetRowByItem() on 'item', as it's already deleted, so compute it from
b632efe0 2411 // the parent ('parentNode') and position in its list of children
57ab4546
VS
2412 int itemRow;
2413 if ( itemPosInNode == 0 )
2414 {
b632efe0
VS
2415 // 1st child, row number is that of the parent parentNode + 1
2416 itemRow = GetRowByItem(parentNode->GetItem()) + 1;
57ab4546
VS
2417 }
2418 else
2419 {
2420 // row number is that of the sibling above 'item' + its subtree if any + 1
ff3c5ad3 2421 const wxDataViewTreeNode *siblingNode = parentNode->GetChildNodes()[itemPosInNode - 1];
57ab4546 2422
422aa8ec
VS
2423 itemRow = GetRowByItem(siblingNode->GetItem()) +
2424 siblingNode->GetSubTreeCount() +
2425 1;
57ab4546
VS
2426 }
2427
2428 wxDataViewSelection newsel(wxDataViewSelectionCmp);
2429
3823a15e
VZ
2430 const size_t numSelections = m_selection.size();
2431 for ( size_t i = 0; i < numSelections; ++i )
57ab4546 2432 {
3823a15e 2433 const int s = m_selection[i];
57ab4546
VS
2434 if ( s < itemRow )
2435 newsel.push_back(s);
2436 else if ( s >= itemRow + itemsDeleted )
2437 newsel.push_back(s - itemsDeleted);
2438 // else: deleted item, remove from selection
2439 }
2440
2441 m_selection = newsel;
2442 }
d47db7e0 2443 }
24c4a50f 2444
bc4f1ff2 2445 // Change the current row to the last row if the current exceed the max row number
ed3aece5 2446 if ( m_currentRow >= GetRowCount() )
8c7b8711 2447 ChangeCurrentRow(m_count - 1);
351461fc 2448
bed74e48 2449 GetOwner()->InvalidateColBestWidths();
99d471a5 2450 UpdateDisplay();
b7fe2261 2451
99d471a5 2452 return true;
a0f3af5f
RR
2453}
2454
aba9bfd0 2455bool wxDataViewMainWindow::ItemChanged(const wxDataViewItem & item)
a0f3af5f 2456{
66e09788 2457 SortPrepare();
b7e9f8b1
RR
2458 g_model->Resort();
2459
bed74e48 2460 GetOwner()->InvalidateColBestWidths();
ac098108 2461
bc4f1ff2 2462 // Send event
6608fdab
RR
2463 wxWindow *parent = GetParent();
2464 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
2465 le.SetEventObject(parent);
c2c89730 2466 le.SetModel(GetModel());
6608fdab 2467 le.SetItem(item);
857f05e1 2468 parent->ProcessWindowEvent(le);
b5ec7dd6 2469
99d471a5 2470 return true;
a0f3af5f
RR
2471}
2472
d93cc830 2473bool wxDataViewMainWindow::ValueChanged( const wxDataViewItem & item, unsigned int model_column )
a0f3af5f 2474{
d93cc830
RR
2475 int view_column = -1;
2476 unsigned int n_col = m_owner->GetColumnCount();
2477 for (unsigned i = 0; i < n_col; i++)
2478 {
2479 wxDataViewColumn *column = m_owner->GetColumn( i );
2480 if (column->GetModelColumn() == model_column)
2481 {
2482 view_column = (int) i;
2483 break;
2484 }
2485 }
2486 if (view_column == -1)
2487 return false;
2488
9861f022 2489 // NOTE: to be valid, we cannot use e.g. INT_MAX - 1
aba9bfd0 2490/*#define MAX_VIRTUAL_WIDTH 100000
9861f022
RR
2491
2492 wxRect rect( 0, row*m_lineHeight, MAX_VIRTUAL_WIDTH, m_lineHeight );
0fdc2321
RR
2493 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2494 Refresh( true, &rect );
2495
2496 return true;
aba9bfd0 2497*/
66e09788 2498 SortPrepare();
b7e9f8b1
RR
2499 g_model->Resort();
2500
bed74e48 2501 GetOwner()->InvalidateColBestWidth(view_column);
ac098108 2502
bc4f1ff2 2503 // Send event
b7e9f8b1 2504 wxWindow *parent = GetParent();
6608fdab 2505 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, parent->GetId());
b7e9f8b1 2506 le.SetEventObject(parent);
c2c89730 2507 le.SetModel(GetModel());
b7e9f8b1 2508 le.SetItem(item);
d93cc830
RR
2509 le.SetColumn(view_column);
2510 le.SetDataViewColumn(GetOwner()->GetColumn(view_column));
857f05e1 2511 parent->ProcessWindowEvent(le);
d47db7e0 2512
0fcce6b9 2513 return true;
a0f3af5f
RR
2514}
2515
2516bool wxDataViewMainWindow::Cleared()
2517{
704c3490 2518 DestroyTree();
97e5b064 2519 m_selection.Clear();
ed3aece5 2520 m_currentRow = (unsigned)-1;
cfa42cb8 2521
be416221
VZ
2522 if (GetModel())
2523 {
2524 SortPrepare();
2525 BuildTree( GetModel() );
2526 }
2527 else
2528 {
2529 m_count = 0;
2530 }
cfa42cb8 2531
bed74e48 2532 GetOwner()->InvalidateColBestWidths();
99d471a5 2533 UpdateDisplay();
b7e9f8b1 2534
99d471a5 2535 return true;
a0f3af5f
RR
2536}
2537
4b3feaa7
RR
2538void wxDataViewMainWindow::UpdateDisplay()
2539{
2540 m_dirty = true;
7d5d2f23 2541 m_underMouse = NULL;
4b3feaa7
RR
2542}
2543
2544void wxDataViewMainWindow::OnInternalIdle()
2545{
2546 wxWindow::OnInternalIdle();
f554a14b 2547
4b3feaa7
RR
2548 if (m_dirty)
2549 {
2550 RecalculateDisplay();
2551 m_dirty = false;
2552 }
2553}
2554
2555void wxDataViewMainWindow::RecalculateDisplay()
2556{
c2c89730 2557 wxDataViewModel *model = GetModel();
4b3feaa7
RR
2558 if (!model)
2559 {
2560 Refresh();
2561 return;
2562 }
f554a14b 2563
9861f022 2564 int width = GetEndOfLastCol();
344ed1f3 2565 int height = GetLineStart( GetRowCount() );
4b3feaa7
RR
2566
2567 SetVirtualSize( width, height );
2568 GetOwner()->SetScrollRate( 10, m_lineHeight );
f554a14b 2569
4b3feaa7
RR
2570 Refresh();
2571}
2572
2573void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
2574{
d93cc830
RR
2575 m_underMouse = NULL;
2576
4b3feaa7 2577 wxWindow::ScrollWindow( dx, dy, rect );
9861f022
RR
2578
2579 if (GetOwner()->m_headerArea)
2580 GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
4b3feaa7
RR
2581}
2582
fbda518c 2583void wxDataViewMainWindow::ScrollTo( int rows, int column )
b7e9f8b1 2584{
d93cc830
RR
2585 m_underMouse = NULL;
2586
b7e9f8b1
RR
2587 int x, y;
2588 m_owner->GetScrollPixelsPerUnit( &x, &y );
344ed1f3 2589 int sy = GetLineStart( rows )/y;
eff1c3e8 2590 int sx = -1;
fbda518c
RR
2591 if( column != -1 )
2592 {
2593 wxRect rect = GetClientRect();
67be459b 2594 int colnum = 0;
dd639a4f 2595 int x_start, w = 0;
fbda518c
RR
2596 int xx, yy, xe;
2597 m_owner->CalcUnscrolledPosition( rect.x, rect.y, &xx, &yy );
2598 for (x_start = 0; colnum < column; colnum++)
2599 {
702f5349 2600 wxDataViewColumn *col = GetOwner()->GetColumnAt(colnum);
fbda518c
RR
2601 if (col->IsHidden())
2602 continue; // skip it!
2603
2604 w = col->GetWidth();
2605 x_start += w;
2606 }
2607
e822d1bd 2608 int x_end = x_start + w;
fbda518c
RR
2609 xe = xx + rect.width;
2610 if( x_end > xe )
2611 {
2612 sx = ( xx + x_end - xe )/x;
2613 }
2614 if( x_start < xx )
2615 {
b7fe2261 2616 sx = x_start/x;
fbda518c
RR
2617 }
2618 }
2619 m_owner->Scroll( sx, sy );
b7e9f8b1
RR
2620}
2621
9861f022 2622int wxDataViewMainWindow::GetCountPerPage() const
cab07038
RR
2623{
2624 wxSize size = GetClientSize();
2625 return size.y / m_lineHeight;
2626}
2627
9861f022 2628int wxDataViewMainWindow::GetEndOfLastCol() const
e21f75bd
RR
2629{
2630 int width = 0;
0a71f9e9 2631 unsigned int i;
9861f022 2632 for (i = 0; i < GetOwner()->GetColumnCount(); i++)
e21f75bd 2633 {
c741d33f 2634 const wxDataViewColumn *c =
702f5349 2635 const_cast<wxDataViewCtrl*>(GetOwner())->GetColumnAt( i );
9861f022
RR
2636
2637 if (!c->IsHidden())
2638 width += c->GetWidth();
e21f75bd
RR
2639 }
2640 return width;
2641}
2642
9861f022 2643unsigned int wxDataViewMainWindow::GetFirstVisibleRow() const
72664514
RR
2644{
2645 int x = 0;
2646 int y = 0;
2647 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
120b9b05 2648
344ed1f3 2649 return GetLineAt( y );
72664514
RR
2650}
2651
442c56e6 2652unsigned int wxDataViewMainWindow::GetLastVisibleRow()
72664514
RR
2653{
2654 wxSize client_size = GetClientSize();
c741d33f 2655 m_owner->CalcUnscrolledPosition( client_size.x, client_size.y,
977a41ec 2656 &client_size.x, &client_size.y );
72664514 2657
bc4f1ff2 2658 // we should deal with the pixel here
344ed1f3 2659 unsigned int row = GetLineAt(client_size.y) - 1;
b7fe2261 2660
fbda518c 2661 return wxMin( GetRowCount()-1, row );
72664514
RR
2662}
2663
f2bf2d71 2664unsigned int wxDataViewMainWindow::GetRowCount() const
cab07038 2665{
3b6280be
RR
2666 if ( m_count == -1 )
2667 {
f2bf2d71
VZ
2668 wxDataViewMainWindow* const
2669 self = const_cast<wxDataViewMainWindow*>(this);
2670 self->m_count = RecalculateCount();
2671 self->UpdateDisplay();
3b6280be 2672 }
aba9bfd0 2673 return m_count;
cab07038
RR
2674}
2675
0a71f9e9 2676void wxDataViewMainWindow::ChangeCurrentRow( unsigned int row )
e21f75bd
RR
2677{
2678 m_currentRow = row;
120b9b05 2679
e21f75bd
RR
2680 // send event
2681}
2682
cab07038
RR
2683void wxDataViewMainWindow::SelectAllRows( bool on )
2684{
4a851b11
VZ
2685 if (IsEmpty())
2686 return;
120b9b05 2687
cab07038
RR
2688 if (on)
2689 {
72664514 2690 m_selection.Clear();
0a71f9e9 2691 for (unsigned int i = 0; i < GetRowCount(); i++)
cab07038 2692 m_selection.Add( i );
72664514
RR
2693 Refresh();
2694 }
2695 else
2696 {
0a71f9e9
RR
2697 unsigned int first_visible = GetFirstVisibleRow();
2698 unsigned int last_visible = GetLastVisibleRow();
2699 unsigned int i;
72664514 2700 for (i = 0; i < m_selection.GetCount(); i++)
120b9b05 2701 {
0a71f9e9 2702 unsigned int row = m_selection[i];
72664514
RR
2703 if ((row >= first_visible) && (row <= last_visible))
2704 RefreshRow( row );
2705 }
2706 m_selection.Clear();
cab07038 2707 }
cab07038
RR
2708}
2709
0a71f9e9 2710void wxDataViewMainWindow::SelectRow( unsigned int row, bool on )
cab07038
RR
2711{
2712 if (m_selection.Index( row ) == wxNOT_FOUND)
2713 {
2714 if (on)
2715 {
2716 m_selection.Add( row );
2717 RefreshRow( row );
2718 }
2719 }
2720 else
2721 {
2722 if (!on)
2723 {
2724 m_selection.Remove( row );
2725 RefreshRow( row );
2726 }
2727 }
2728}
2729
0a71f9e9 2730void wxDataViewMainWindow::SelectRows( unsigned int from, unsigned int to, bool on )
cab07038
RR
2731{
2732 if (from > to)
2733 {
0a71f9e9 2734 unsigned int tmp = from;
cab07038
RR
2735 from = to;
2736 to = tmp;
2737 }
2738
0a71f9e9 2739 unsigned int i;
cab07038
RR
2740 for (i = from; i <= to; i++)
2741 {
2742 if (m_selection.Index( i ) == wxNOT_FOUND)
2743 {
2744 if (on)
2745 m_selection.Add( i );
2746 }
2747 else
2748 {
2749 if (!on)
2750 m_selection.Remove( i );
2751 }
2752 }
2753 RefreshRows( from, to );
2754}
2755
87f0efe2
RR
2756void wxDataViewMainWindow::Select( const wxArrayInt& aSelections )
2757{
2758 for (size_t i=0; i < aSelections.GetCount(); i++)
2759 {
2760 int n = aSelections[i];
2761
2762 m_selection.Add( n );
2763 RefreshRow( n );
2764 }
2765}
2766
0a71f9e9 2767void wxDataViewMainWindow::ReverseRowSelection( unsigned int row )
cab07038
RR
2768{
2769 if (m_selection.Index( row ) == wxNOT_FOUND)
2770 m_selection.Add( row );
2771 else
2772 m_selection.Remove( row );
120b9b05 2773 RefreshRow( row );
cab07038
RR
2774}
2775
0a71f9e9 2776bool wxDataViewMainWindow::IsRowSelected( unsigned int row )
cab07038
RR
2777{
2778 return (m_selection.Index( row ) != wxNOT_FOUND);
2779}
2780
526e19e2
RR
2781void wxDataViewMainWindow::SendSelectionChangedEvent( const wxDataViewItem& item)
2782{
2783 wxWindow *parent = GetParent();
2784 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, parent->GetId());
2785
2786 le.SetEventObject(parent);
c2c89730 2787 le.SetModel(GetModel());
526e19e2
RR
2788 le.SetItem( item );
2789
857f05e1 2790 parent->ProcessWindowEvent(le);
526e19e2
RR
2791}
2792
0a71f9e9 2793void wxDataViewMainWindow::RefreshRow( unsigned int row )
cab07038 2794{
344ed1f3 2795 wxRect rect( 0, GetLineStart( row ), GetEndOfLastCol(), GetLineHeight( row ) );
cab07038 2796 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2797
cab07038
RR
2798 wxSize client_size = GetClientSize();
2799 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2800 wxRect intersect_rect = client_rect.Intersect( rect );
2801 if (intersect_rect.width > 0)
2802 Refresh( true, &intersect_rect );
2803}
2804
0a71f9e9 2805void wxDataViewMainWindow::RefreshRows( unsigned int from, unsigned int to )
cab07038
RR
2806{
2807 if (from > to)
2808 {
0a71f9e9 2809 unsigned int tmp = to;
cab07038
RR
2810 to = from;
2811 from = tmp;
2812 }
2813
344ed1f3 2814 wxRect rect( 0, GetLineStart( from ), GetEndOfLastCol(), GetLineStart( (to-from+1) ) );
cab07038 2815 m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
120b9b05 2816
cab07038
RR
2817 wxSize client_size = GetClientSize();
2818 wxRect client_rect( 0, 0, client_size.x, client_size.y );
2819 wxRect intersect_rect = client_rect.Intersect( rect );
2820 if (intersect_rect.width > 0)
2821 Refresh( true, &intersect_rect );
2822}
2823
0a71f9e9 2824void wxDataViewMainWindow::RefreshRowsAfter( unsigned int firstRow )
cab07038 2825{
cab07038 2826 wxSize client_size = GetClientSize();
344ed1f3
RR
2827 int start = GetLineStart( firstRow );
2828 m_owner->CalcScrolledPosition( start, 0, &start, NULL );
2829 if (start > client_size.y) return;
2830
2831 wxRect rect( 0, start, client_size.x, client_size.y - start );
777f9cef 2832
344ed1f3 2833 Refresh( true, &rect );
cab07038
RR
2834}
2835
9861f022
RR
2836wxRect wxDataViewMainWindow::GetLineRect( unsigned int row ) const
2837{
2838 wxRect rect;
2839 rect.x = 0;
344ed1f3 2840 rect.y = GetLineStart( row );
9861f022 2841 rect.width = GetEndOfLastCol();
344ed1f3 2842 rect.height = GetLineHeight( row );
9861f022
RR
2843
2844 return rect;
cab07038
RR
2845}
2846
344ed1f3
RR
2847int wxDataViewMainWindow::GetLineStart( unsigned int row ) const
2848{
c2c89730 2849 const wxDataViewModel *model = GetModel();
777f9cef 2850
344ed1f3
RR
2851 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2852 {
2853 // TODO make more efficient
777f9cef 2854
344ed1f3 2855 int start = 0;
777f9cef 2856
344ed1f3
RR
2857 unsigned int r;
2858 for (r = 0; r < row; r++)
2859 {
e170469f
RR
2860 const wxDataViewTreeNode* node = GetTreeNodeByRow(r);
2861 if (!node) return start;
777f9cef 2862
e170469f 2863 wxDataViewItem item = node->GetItem();
777f9cef 2864
e170469f
RR
2865 unsigned int cols = GetOwner()->GetColumnCount();
2866 unsigned int col;
2867 int height = m_lineHeight;
2868 for (col = 0; col < cols; col++)
2869 {
344ed1f3
RR
2870 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2871 if (column->IsHidden())
2872 continue; // skip it!
777f9cef 2873
4cef3873
VZ
2874 if ((col != 0) &&
2875 model->IsContainer(item) &&
ce468dc2 2876 !model->HasContainerColumns(item))
ba5f54e6 2877 continue; // skip it!
777f9cef 2878
4cef3873 2879 wxDataViewRenderer *renderer =
ce468dc2 2880 const_cast<wxDataViewRenderer*>(column->GetRenderer());
f0ccd2cb 2881 renderer->PrepareForItem(model, item, column->GetModelColumn());
86755098 2882
344ed1f3 2883 height = wxMax( height, renderer->GetSize().y );
e170469f 2884 }
777f9cef 2885
e170469f 2886 start += height;
344ed1f3 2887 }
777f9cef 2888
344ed1f3
RR
2889 return start;
2890 }
2891 else
2892 {
2893 return row * m_lineHeight;
2894 }
2895}
2896
2897int wxDataViewMainWindow::GetLineAt( unsigned int y ) const
2898{
c2c89730 2899 const wxDataViewModel *model = GetModel();
ba5f54e6 2900
777f9cef
VZ
2901 // check for the easy case first
2902 if ( !GetOwner()->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
2903 return y / m_lineHeight;
2904
2905 // TODO make more efficient
2906 unsigned int row = 0;
2907 unsigned int yy = 0;
2908 for (;;)
344ed1f3 2909 {
ce468dc2
FM
2910 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2911 if (!node)
2912 {
2913 // not really correct...
2914 return row + ((y-yy) / m_lineHeight);
2915 }
777f9cef 2916
ce468dc2 2917 wxDataViewItem item = node->GetItem();
777f9cef 2918
ce468dc2
FM
2919 unsigned int cols = GetOwner()->GetColumnCount();
2920 unsigned int col;
2921 int height = m_lineHeight;
2922 for (col = 0; col < cols; col++)
2923 {
777f9cef
VZ
2924 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2925 if (column->IsHidden())
2926 continue; // skip it!
2927
4cef3873
VZ
2928 if ((col != 0) &&
2929 model->IsContainer(item) &&
ce468dc2 2930 !model->HasContainerColumns(item))
777f9cef
VZ
2931 continue; // skip it!
2932
4cef3873 2933 wxDataViewRenderer *renderer =
ce468dc2 2934 const_cast<wxDataViewRenderer*>(column->GetRenderer());
f0ccd2cb 2935 renderer->PrepareForItem(model, item, column->GetModelColumn());
86755098 2936
777f9cef 2937 height = wxMax( height, renderer->GetSize().y );
ce468dc2 2938 }
777f9cef 2939
ce468dc2
FM
2940 yy += height;
2941 if (y < yy)
2942 return row;
777f9cef 2943
ce468dc2 2944 row++;
344ed1f3
RR
2945 }
2946}
2947
2948int wxDataViewMainWindow::GetLineHeight( unsigned int row ) const
2949{
c2c89730 2950 const wxDataViewModel *model = GetModel();
777f9cef 2951
344ed1f3
RR
2952 if (GetOwner()->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT)
2953 {
2954 wxASSERT( !IsVirtualList() );
777f9cef 2955
344ed1f3
RR
2956 const wxDataViewTreeNode* node = GetTreeNodeByRow(row);
2957 // wxASSERT( node );
2958 if (!node) return m_lineHeight;
2959
ba5f54e6 2960 wxDataViewItem item = node->GetItem();
777f9cef 2961
981cd83e 2962 int height = m_lineHeight;
777f9cef 2963
344ed1f3
RR
2964 unsigned int cols = GetOwner()->GetColumnCount();
2965 unsigned int col;
2966 for (col = 0; col < cols; col++)
2967 {
2968 const wxDataViewColumn *column = GetOwner()->GetColumn(col);
2969 if (column->IsHidden())
2970 continue; // skip it!
ba5f54e6 2971
4cef3873
VZ
2972 if ((col != 0) &&
2973 model->IsContainer(item) &&
ce468dc2 2974 !model->HasContainerColumns(item))
ba5f54e6 2975 continue; // skip it!
777f9cef 2976
4cef3873 2977 wxDataViewRenderer *renderer =
ce468dc2 2978 const_cast<wxDataViewRenderer*>(column->GetRenderer());
f0ccd2cb 2979 renderer->PrepareForItem(model, item, column->GetModelColumn());
86755098 2980
344ed1f3
RR
2981 height = wxMax( height, renderer->GetSize().y );
2982 }
2983
2984 return height;
2985 }
2986 else
2987 {
2988 return m_lineHeight;
2989 }
2990}
2991
aba9bfd0 2992
3b6280be
RR
2993class RowToTreeNodeJob: public DoJob
2994{
2995public:
442c56e6
VZ
2996 RowToTreeNodeJob( unsigned int row , int current, wxDataViewTreeNode * node )
2997 {
2998 this->row = row;
59e60167
VZ
2999 this->current = current;
3000 ret = NULL;
d47db7e0
RR
3001 parent = node;
3002 }
3b6280be
RR
3003
3004 virtual int operator() ( wxDataViewTreeNode * node )
d5025dc0 3005 {
d47db7e0 3006 current ++;
704c3490 3007 if( current == static_cast<int>(row))
977a41ec 3008 {
59e60167 3009 ret = node;
d54f0605 3010 return DoJob::DONE;
3b6280be 3011 }
d47db7e0
RR
3012
3013 if( node->GetSubTreeCount() + current < static_cast<int>(row) )
3014 {
3015 current += node->GetSubTreeCount();
d54f0605 3016 return DoJob::SKIP_SUBTREE;
d47db7e0 3017 }
d5025dc0 3018 else
d47db7e0
RR
3019 {
3020 parent = node;
bc4f1ff2 3021
422aa8ec
VS
3022 // If the current node has only leaf children, we can find the
3023 // desired node directly. This can speed up finding the node
3024 // in some cases, and will have a very good effect for list views.
d0cfefc4 3025 if ( node->HasChildren() &&
ff3c5ad3 3026 (int)node->GetChildNodes().size() == node->GetSubTreeCount() )
b7e9f8b1 3027 {
422aa8ec 3028 const int index = static_cast<int>(row) - current - 1;
ff3c5ad3 3029 ret = node->GetChildNodes()[index];
d54f0605 3030 return DoJob::DONE;
b7e9f8b1 3031 }
d0cfefc4 3032
d54f0605 3033 return DoJob::CONTINUE;
d47db7e0 3034 }
d5025dc0 3035 }
3b6280be 3036
bc4f1ff2
FM
3037 wxDataViewTreeNode * GetResult() const
3038 { return ret; }
3039
3b6280be
RR
3040private:
3041 unsigned int row;
59e60167 3042 int current;
3b6280be 3043 wxDataViewTreeNode * ret;
59e60167 3044 wxDataViewTreeNode * parent;
3b6280be
RR
3045};
3046
344ed1f3 3047wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) const
3b6280be 3048{
344ed1f3 3049 wxASSERT( !IsVirtualList() );
777f9cef 3050
ed3aece5
VZ
3051 if ( row == (unsigned)-1 )
3052 return NULL;
3053
344ed1f3
RR
3054 RowToTreeNodeJob job( row , -2, m_root );
3055 Walker( m_root , job );
3056 return job.GetResult();
3b6280be
RR
3057}
3058
422aa8ec
VS
3059wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const
3060{
b0272c60 3061 wxDataViewItem item;
422aa8ec
VS
3062 if (IsVirtualList())
3063 {
b0272c60
VZ
3064 if ( row < GetRowCount() )
3065 item = wxDataViewItem(wxUIntToPtr(row+1));
422aa8ec
VS
3066 }
3067 else
3068 {
3069 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
b0272c60
VZ
3070 if ( node )
3071 item = node->GetItem();
422aa8ec 3072 }
b0272c60
VZ
3073
3074 return item;
422aa8ec
VS
3075}
3076
fd48fe89
VZ
3077bool
3078wxDataViewMainWindow::SendExpanderEvent(wxEventType type,
3079 const wxDataViewItem& item)
66e09788
RR
3080{
3081 wxWindow *parent = GetParent();
3082 wxDataViewEvent le(type, parent->GetId());
3083
3084 le.SetEventObject(parent);
c2c89730 3085 le.SetModel(GetModel());
66e09788
RR
3086 le.SetItem( item );
3087
fd48fe89 3088 return !parent->ProcessWindowEvent(le) || le.IsAllowed();
66e09788
RR
3089}
3090
739a8399
RR
3091bool wxDataViewMainWindow::IsExpanded( unsigned int row ) const
3092{
ce5abbf9 3093 if (IsList())
bc4f1ff2 3094 return false;
9adeb77a 3095
739a8399
RR
3096 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
3097 if (!node)
bc4f1ff2 3098 return false;
9adeb77a 3099
739a8399 3100 if (!node->HasChildren())
bc4f1ff2 3101 return false;
9adeb77a 3102
739a8399
RR
3103 return node->IsOpen();
3104}
3105
235d5f88
RR
3106bool wxDataViewMainWindow::HasChildren( unsigned int row ) const
3107{
ce5abbf9 3108 if (IsList())
235d5f88
RR
3109 return false;
3110
3111 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
3112 if (!node)
3113 return false;
739a8399 3114
235d5f88 3115 if (!node->HasChildren())
235d5f88 3116 return false;
235d5f88
RR
3117
3118 return true;
3119}
3120
3121void wxDataViewMainWindow::Expand( unsigned int row )
3b6280be 3122{
ce5abbf9 3123 if (IsList())
bc4f1ff2 3124 return;
9657c197 3125
3b6280be 3126 wxDataViewTreeNode * node = GetTreeNodeByRow(row);
235d5f88
RR
3127 if (!node)
3128 return;
4cef3873 3129
235d5f88 3130 if (!node->HasChildren())
235d5f88 3131 return;
4cef3873 3132
5d2ebe49
VZ
3133 if (!node->IsOpen())
3134 {
3135 if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING, node->GetItem()) )
3136 {
3137 // Vetoed by the event handler.
3138 return;
3139 }
b7fe2261 3140
5d2ebe49 3141 node->ToggleOpen();
977a41ec 3142
5d2ebe49
VZ
3143 // build the children of current node
3144 if( node->GetChildNodes().empty() )
3145 {
3146 SortPrepare();
3147 ::BuildTreeHelper(GetModel(), node->GetItem(), node);
3148 }
977a41ec 3149
5d2ebe49
VZ
3150 // By expanding the node all row indices that are currently in the selection list
3151 // and are greater than our node have become invalid. So we have to correct that now.
3152 const unsigned rowAdjustment = node->GetSubTreeCount();
3153 for(unsigned i=0; i<m_selection.size(); ++i)
3154 {
3155 const unsigned testRow = m_selection[i];
3156 // all rows above us are not affected, so skip them
3157 if(testRow <= row)
3158 continue;
bc4f1ff2 3159
5d2ebe49
VZ
3160 m_selection[i] += rowAdjustment;
3161 }
977a41ec 3162
5d2ebe49
VZ
3163 if(m_currentRow > row)
3164 ChangeCurrentRow(m_currentRow + rowAdjustment);
977a41ec 3165
5d2ebe49
VZ
3166 m_count = -1;
3167 UpdateDisplay();
3168 // Send the expanded event
3169 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,node->GetItem());
3170 }
3b6280be
RR
3171}
3172
235d5f88 3173void wxDataViewMainWindow::Collapse(unsigned int row)
3b6280be 3174{
ce5abbf9 3175 if (IsList())
bc4f1ff2 3176 return;
e822d1bd 3177
7d835958
RR
3178 wxDataViewTreeNode *node = GetTreeNodeByRow(row);
3179 if (!node)
3180 return;
4cef3873 3181
235d5f88 3182 if (!node->HasChildren())
7d835958 3183 return;
d47db7e0 3184
235d5f88 3185 if (node->IsOpen())
3b6280be 3186 {
fd48fe89
VZ
3187 if ( !SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,node->GetItem()) )
3188 {
3189 // Vetoed by the event handler.
66e09788 3190 return;
fd48fe89 3191 }
abdb8c18 3192
977a41ec
FM
3193 // Find out if there are selected items below the current node.
3194 bool selectCollapsingRow = false;
3195 const unsigned rowAdjustment = node->GetSubTreeCount();
3196 unsigned maxRowToBeTested = row + rowAdjustment;
3197 for(unsigned i=0; i<m_selection.size(); ++i)
3198 {
3199 const unsigned testRow = m_selection[i];
3200 if(testRow > row && testRow <= maxRowToBeTested)
3201 {
3202 selectCollapsingRow = true;
3203 // get out as soon as we have found a node that is selected
3204 break;
3205 }
3206 }
3207
3208 node->ToggleOpen();
3209
3210 // If the node to be closed has selected items the user won't see those any longer.
3211 // We select the collapsing node in this case.
3212 if(selectCollapsingRow)
3213 {
3214 SelectAllRows(false);
3215 ChangeCurrentRow(row);
3216 SelectRow(row, true);
3217 SendSelectionChangedEvent(GetItemByRow(row));
3218 }
3219 else
3220 {
3221 // if there were no selected items below our node we still need to "fix" the
3222 // selection list to adjust for the changing of the row indices.
235d5f88 3223 // We actually do the opposite of what we are doing in Expand().
977a41ec
FM
3224 for(unsigned i=0; i<m_selection.size(); ++i)
3225 {
3226 const unsigned testRow = m_selection[i];
3227 // all rows above us are not affected, so skip them
3228 if(testRow <= row)
3229 continue;
3230
3231 m_selection[i] -= rowAdjustment;
3232 }
3233
3234 // if the "current row" is being collapsed away we change it to the current row ;-)
3235 if(m_currentRow > row && m_currentRow <= maxRowToBeTested)
3236 ChangeCurrentRow(row);
3237 else if(m_currentRow > row)
3238 ChangeCurrentRow(m_currentRow - rowAdjustment);
3239 }
abdb8c18 3240
3b6280be 3241 m_count = -1;
351461fc 3242 UpdateDisplay();
7d835958 3243 SendExpanderEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,node->GetItem());
66e09788 3244 }
3b6280be
RR
3245}
3246
351461fc
RR
3247wxDataViewTreeNode * wxDataViewMainWindow::FindNode( const wxDataViewItem & item )
3248{
c2c89730 3249 const wxDataViewModel * model = GetModel();
351461fc
RR
3250 if( model == NULL )
3251 return NULL;
9adeb77a 3252
ce2fe798
RR
3253 if (!item.IsOk())
3254 return m_root;
351461fc 3255
10875c13
VZ
3256 // Compose the parent-chain for the item we are looking for
3257 wxVector<wxDataViewItem> parentChain;
351461fc
RR
3258 wxDataViewItem it( item );
3259 while( it.IsOk() )
3260 {
10875c13
VZ
3261 parentChain.push_back(it);
3262 it = model->GetParent(it);
351461fc
RR
3263 }
3264
bc4f1ff2
FM
3265 // Find the item along the parent-chain.
3266 // This algorithm is designed to speed up the node-finding method
10875c13 3267 wxDataViewTreeNode* node = m_root;
99ef4372 3268 for( unsigned iter = parentChain.size()-1; ; --iter )
351461fc
RR
3269 {
3270 if( node->HasChildren() )
3271 {
ff3c5ad3 3272 if( node->GetChildNodes().empty() )
24c4a50f 3273 {
422aa8ec
VS
3274 // Even though the item is a container, it doesn't have any
3275 // child nodes in the control's representation yet. We have
3276 // to realize its subtree now.
24c4a50f 3277 SortPrepare();
74123073 3278 ::BuildTreeHelper(model, node->GetItem(), node);
24c4a50f 3279 }
351461fc 3280
ff3c5ad3 3281 const wxDataViewTreeNodes& nodes = node->GetChildNodes();
d2c1ee8a 3282 bool found = false;
777f9cef 3283
10875c13 3284 for (unsigned i = 0; i < nodes.GetCount(); ++i)
d92cb015 3285 {
10875c13
VZ
3286 wxDataViewTreeNode* currentNode = nodes[i];
3287 if (currentNode->GetItem() == parentChain[iter])
b7fe2261 3288 {
10875c13
VZ
3289 if (currentNode->GetItem() == item)
3290 return currentNode;
777f9cef 3291
10875c13 3292 node = currentNode;
d2c1ee8a 3293 found = true;
d92cb015
RR
3294 break;
3295 }
3296 }
d2c1ee8a 3297 if (!found)
351461fc 3298 return NULL;
351461fc
RR
3299 }
3300 else
3301 return NULL;
99ef4372
VZ
3302
3303 if ( !iter )
3304 break;
351461fc 3305 }
d2c1ee8a 3306 return NULL;
351461fc
RR
3307}
3308
4cef3873 3309void wxDataViewMainWindow::HitTest( const wxPoint & point, wxDataViewItem & item,
bc4f1ff2 3310 wxDataViewColumn* &column )
66e09788 3311{
fbda518c 3312 wxDataViewColumn *col = NULL;
66e09788
RR
3313 unsigned int cols = GetOwner()->GetColumnCount();
3314 unsigned int colnum = 0;
66e09788
RR
3315 int x, y;
3316 m_owner->CalcUnscrolledPosition( point.x, point.y, &x, &y );
e822d1bd 3317 for (unsigned x_start = 0; colnum < cols; colnum++)
66e09788 3318 {
702f5349 3319 col = GetOwner()->GetColumnAt(colnum);
66e09788
RR
3320 if (col->IsHidden())
3321 continue; // skip it!
3322
3323 unsigned int w = col->GetWidth();
3324 if (x_start+w >= (unsigned int)x)
3325 break;
3326
3327 x_start += w;
3328 }
3329
fbda518c 3330 column = col;
344ed1f3 3331 item = GetItemByRow( GetLineAt( y ) );
66e09788
RR
3332}
3333
4cef3873 3334wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item,
bc4f1ff2 3335 const wxDataViewColumn* column )
66e09788 3336{
c937344c
RR
3337 int xpos = 0;
3338 int width = 0;
4cef3873 3339
c937344c
RR
3340 unsigned int cols = GetOwner()->GetColumnCount();
3341 // If column is null the loop will compute the combined width of all columns.
3342 // Otherwise, it will compute the x position of the column we are looking for.
3343 for (unsigned int i = 0; i < cols; i++)
3344 {
3345 wxDataViewColumn* col = GetOwner()->GetColumnAt( i );
3346
3347 if (col == column)
3348 break;
3349
3350 if (col->IsHidden())
3351 continue; // skip it!
3352
3353 xpos += col->GetWidth();
3354 width += col->GetWidth();
3355 }
3356
3357 if(column != 0)
3358 {
3359 // If we have a column, we need can get its width directly.
3360 if(column->IsHidden())
3361 width = 0;
3362 else
3363 width = column->GetWidth();
3364
3365 }
3366 else
3367 {
3368 // If we have no column, we reset the x position back to zero.
3369 xpos = 0;
3370 }
3371
3372 // we have to take an expander column into account and compute its indentation
3373 // to get the correct x position where the actual text is
3374 int indent = 0;
66e09788 3375 int row = GetRowByItem(item);
1841f079
VZ
3376 if (!IsList() &&
3377 (column == 0 || GetExpanderColumnOrFirstOne(GetOwner()) == column) )
66e09788 3378 {
c937344c
RR
3379 wxDataViewTreeNode* node = GetTreeNodeByRow(row);
3380 indent = GetOwner()->GetIndent() * node->GetIndentLevel();
03647350 3381 indent = indent + m_lineHeight; // use m_lineHeight as the width of the expander
66e09788 3382 }
c937344c
RR
3383
3384 wxRect itemRect( xpos + indent,
3385 GetLineStart( row ),
3386 width - indent,
3387 GetLineHeight( row ) );
3388
3389 GetOwner()->CalcScrolledPosition( itemRect.x, itemRect.y,
3390 &itemRect.x, &itemRect.y );
3391
3392 return itemRect;
66e09788
RR
3393}
3394
f2bf2d71 3395int wxDataViewMainWindow::RecalculateCount() const
3b6280be 3396{
86ba79ed 3397 if (IsVirtualList())
a3cc79d9 3398 {
9330d5af 3399 wxDataViewVirtualListModel *list_model =
c2c89730 3400 (wxDataViewVirtualListModel*) GetModel();
03647350 3401
9330d5af 3402 return list_model->GetCount();
a3cc79d9
RR
3403 }
3404 else
3405 {
3406 return m_root->GetSubTreeCount();
3407 }
3b6280be
RR
3408}
3409
aba9bfd0
RR
3410class ItemToRowJob : public DoJob
3411{
3412public:
10875c13 3413 ItemToRowJob(const wxDataViewItem& item_, wxVector<wxDataViewItem>::reverse_iterator iter)
59e60167 3414 : m_iter(iter),
977a41ec 3415 item(item_)
59e60167
VZ
3416 {
3417 ret = -1;
3418 }
aba9bfd0 3419
bc4f1ff2 3420 // Maybe binary search will help to speed up this process
d5025dc0
RR
3421 virtual int operator() ( wxDataViewTreeNode * node)
3422 {
977a41ec
FM
3423 ret ++;
3424 if( node->GetItem() == item )
3425 {
d54f0605 3426 return DoJob::DONE;
977a41ec 3427 }
d5025dc0 3428
10875c13 3429 if( node->GetItem() == *m_iter )
977a41ec
FM
3430 {
3431 m_iter++;
d54f0605 3432 return DoJob::CONTINUE;
977a41ec
FM
3433 }
3434 else
3435 {
3436 ret += node->GetSubTreeCount();
d54f0605 3437 return DoJob::SKIP_SUBTREE;
977a41ec 3438 }
442c56e6 3439
d5025dc0 3440 }
aba9bfd0 3441
bc4f1ff2
FM
3442 // the row number is begin from zero
3443 int GetResult() const
3444 { return ret -1; }
59e60167 3445
aba9bfd0 3446private:
10875c13 3447 wxVector<wxDataViewItem>::reverse_iterator m_iter;
aba9bfd0
RR
3448 wxDataViewItem item;
3449 int ret;
442c56e6 3450
aba9bfd0
RR
3451};
3452
344ed1f3 3453int wxDataViewMainWindow::GetRowByItem(const wxDataViewItem & item) const
aba9bfd0 3454{
c2c89730 3455 const wxDataViewModel * model = GetModel();
d47db7e0 3456 if( model == NULL )
fbda518c 3457 return -1;
d47db7e0 3458
86ba79ed 3459 if (IsVirtualList())
d47db7e0 3460 {
86ba79ed 3461 return wxPtrToUInt( item.GetID() ) -1;
d47db7e0 3462 }
a3cc79d9
RR
3463 else
3464 {
3465 if( !item.IsOk() )
3466 return -1;
3467
10875c13
VZ
3468 // Compose the parent-chain of the item we are looking for
3469 wxVector<wxDataViewItem> parentChain;
a3cc79d9
RR
3470 wxDataViewItem it( item );
3471 while( it.IsOk() )
3472 {
10875c13
VZ
3473 parentChain.push_back(it);
3474 it = model->GetParent(it);
a3cc79d9 3475 }
d47db7e0 3476
10875c13
VZ
3477 // add an 'invalid' item to represent our 'invisible' root node
3478 parentChain.push_back(wxDataViewItem());
3479
3480 // the parent chain was created by adding the deepest parent first.
3481 // so if we want to start at the root node, we have to iterate backwards through the vector
3482 ItemToRowJob job( item, parentChain.rbegin() );
3483 Walker( m_root, job );
a3cc79d9
RR
3484 return job.GetResult();
3485 }
aba9bfd0
RR
3486}
3487
10875c13 3488static void BuildTreeHelper( const wxDataViewModel * model, const wxDataViewItem & item,
bc4f1ff2 3489 wxDataViewTreeNode * node)
aba9bfd0 3490{
351461fc 3491 if( !model->IsContainer( item ) )
59e60167 3492 return;
442c56e6 3493
c899416d
RR
3494 wxDataViewItemArray children;
3495 unsigned int num = model->GetChildren( item, children);
9adeb77a 3496
422aa8ec 3497 for ( unsigned int index = 0; index < num; index++ )
aba9bfd0 3498 {
422aa8ec
VS
3499 wxDataViewTreeNode *n = new wxDataViewTreeNode(node, children[index]);
3500
3501 if( model->IsContainer(children[index]) )
59e60167 3502 n->SetHasChildren( true );
422aa8ec 3503
35219832 3504 node->InsertChild(n, index);
aba9bfd0 3505 }
d47db7e0 3506
422aa8ec
VS
3507 wxASSERT( node->IsOpen() );
3508 node->ChangeSubTreeCount(+num);
aba9bfd0
RR
3509}
3510
3511void wxDataViewMainWindow::BuildTree(wxDataViewModel * model)
3512{
51bdecff
RR
3513 DestroyTree();
3514
c2c89730 3515 if (GetModel()->IsVirtualListModel())
a3cc79d9 3516 {
59e60167 3517 m_count = -1;
a3cc79d9
RR
3518 return;
3519 }
3520
422aa8ec 3521 m_root = wxDataViewTreeNode::CreateRootNode();
51bdecff 3522
bc4f1ff2 3523 // First we define a invalid item to fetch the top-level elements
aba9bfd0 3524 wxDataViewItem item;
66e09788 3525 SortPrepare();
3b6280be 3526 BuildTreeHelper( model, item, m_root);
59e60167 3527 m_count = -1;
aba9bfd0
RR
3528}
3529
aba9bfd0
RR
3530void wxDataViewMainWindow::DestroyTree()
3531{
344ed1f3 3532 if (!IsVirtualList())
51bdecff 3533 {
a2d3c415
VS
3534 wxDELETE(m_root);
3535 m_count = 0;
51bdecff 3536 }
aba9bfd0
RR
3537}
3538
1c8e71c9
VS
3539wxDataViewColumn*
3540wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode)
3541{
3542 // Edit the current column editable in 'mode'. If no column is focused
3543 // (typically because the user has full row selected), try to find the
3544 // first editable column (this would typically be a checkbox for
3545 // wxDATAVIEW_CELL_ACTIVATABLE and we don't want to force the user to set
3546 // focus on the checkbox column; or on the only editable text column).
3547
3548 wxDataViewColumn *candidate = m_currentCol;
3549
3550 if ( candidate &&
cf5d4c76 3551 !IsCellEditableInMode(item, candidate, mode) &&
1c8e71c9
VS
3552 !m_currentColSetByKeyboard )
3553 {
3554 // If current column was set by mouse to something not editable (in
3555 // 'mode') and the user pressed Space/F2 to edit it, treat the
3556 // situation as if there was whole-row focus, because that's what is
3557 // visually indicated and the mouse click could very well be targeted
3558 // on the row rather than on an individual cell.
3559 //
3560 // But if it was done by keyboard, respect that even if the column
3561 // isn't editable, because focus is visually on that column and editing
3562 // something else would be surprising.
3563 candidate = NULL;
3564 }
3565
3566 if ( !candidate )
3567 {
3568 const unsigned cols = GetOwner()->GetColumnCount();
3569 for ( unsigned i = 0; i < cols; i++ )
3570 {
3571 wxDataViewColumn *c = GetOwner()->GetColumnAt(i);
3572 if ( c->IsHidden() )
3573 continue;
3574
cf5d4c76 3575 if ( IsCellEditableInMode(item, c, mode) )
1c8e71c9
VS
3576 {
3577 candidate = c;
3578 break;
3579 }
3580 }
3581 }
3582
3583 // If on container item without columns, only the expander column
3584 // may be directly editable:
3585 if ( candidate &&
3586 GetOwner()->GetExpanderColumn() != candidate &&
3587 GetModel()->IsContainer(item) &&
3588 !GetModel()->HasContainerColumns(item) )
3589 {
3590 candidate = GetOwner()->GetExpanderColumn();
3591 }
3592
3593 if ( !candidate )
3594 return NULL;
3595
cf5d4c76 3596 if ( !IsCellEditableInMode(item, candidate, mode) )
1c8e71c9
VS
3597 return NULL;
3598
3599 return candidate;
3600}
3601
cf5d4c76
VS
3602bool wxDataViewMainWindow::IsCellEditableInMode(const wxDataViewItem& item,
3603 const wxDataViewColumn *col,
3604 wxDataViewCellMode mode) const
3605{
3606 if ( col->GetRenderer()->GetMode() != mode )
3607 return false;
3608
3609 if ( !GetModel()->IsEnabled(item, col->GetModelColumn()) )
3610 return false;
3611
3612 return true;
3613}
3614
64ac3db8
VZ
3615void wxDataViewMainWindow::OnCharHook(wxKeyEvent& event)
3616{
3617 if ( m_editorCtrl )
3618 {
3619 // Handle any keys special for the in-place editor and return without
3620 // calling Skip() below.
3621 switch ( event.GetKeyCode() )
3622 {
3623 case WXK_ESCAPE:
3624 m_editorRenderer->CancelEditing();
3625 return;
3626
3627 case WXK_RETURN:
3628 m_editorRenderer->FinishEditing();
3629 return;
3630 }
3631 }
3632
3633 event.Skip();
3634}
3635
cab07038
RR
3636void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
3637{
63ced01b
VZ
3638 wxWindow * const parent = GetParent();
3639
3640 // propagate the char event upwards
3641 wxKeyEvent eventForParent(event);
3642 eventForParent.SetEventObject(parent);
3643 if ( parent->ProcessWindowEvent(eventForParent) )
3644 return;
3645
3646 if ( parent->HandleAsNavigationKey(event) )
f029f1d1 3647 return;
cab07038
RR
3648
3649 // no item -> nothing to do
3650 if (!HasCurrentRow())
3651 {
3652 event.Skip();
3653 return;
3654 }
120b9b05 3655
cab07038
RR
3656 // don't use m_linesPerPage directly as it might not be computed yet
3657 const int pageSize = GetCountPerPage();
9a83f860 3658 wxCHECK_RET( pageSize, wxT("should have non zero page size") );
cab07038
RR
3659
3660 switch ( event.GetKeyCode() )
3661 {
d37b709c 3662 case WXK_RETURN:
a739b8b2
VS
3663 if ( event.HasModifiers() )
3664 {
3665 event.Skip();
3666 break;
3667 }
3668 else
d37b709c 3669 {
c2efa4b4
VS
3670 // Enter activates the item, i.e. sends wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED to
3671 // it. Only if that event is not handled do we activate column renderer (which
0fff3dfc 3672 // is normally done by Space) or even inline editing.
c2efa4b4 3673
276227fc 3674 const wxDataViewItem item = GetItemByRow(m_currentRow);
d37b709c 3675
c2efa4b4
VS
3676 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,
3677 parent->GetId());
3678 le.SetItem(item);
3679 le.SetEventObject(parent);
3680 le.SetModel(GetModel());
3681
857f05e1 3682 if ( parent->ProcessWindowEvent(le) )
c2efa4b4
VS
3683 break;
3684 // else: fall through to WXK_SPACE handling
3685 }
3686
3687 case WXK_SPACE:
a739b8b2
VS
3688 if ( event.HasModifiers() )
3689 {
3690 event.Skip();
3691 break;
3692 }
3693 else
c2efa4b4 3694 {
0fff3dfc
VS
3695 // Space toggles activatable items or -- if not activatable --
3696 // starts inline editing (this is normally done using F2 on
3697 // Windows, but Space is common everywhere else, so use it too
3698 // for greater cross-platform compatibility).
3699
1c8e71c9 3700 const wxDataViewItem item = GetItemByRow(m_currentRow);
276227fc 3701
1c8e71c9
VS
3702 // Activate the current activatable column. If not column is focused (typically
3703 // because the user has full row selected), try to find the first activatable
3704 // column (this would typically be a checkbox and we don't want to force the user
3705 // to set focus on the checkbox column).
3706 wxDataViewColumn *activatableCol = FindColumnForEditing(item, wxDATAVIEW_CELL_ACTIVATABLE);
276227fc
VS
3707
3708 if ( activatableCol )
3709 {
3710 const unsigned colIdx = activatableCol->GetModelColumn();
3711 const wxRect cell_rect = GetOwner()->GetItemRect(item, activatableCol);
3712
3713 wxDataViewRenderer *cell = activatableCol->GetRenderer();
3714 cell->PrepareForItem(GetModel(), item, colIdx);
dc73d7f5 3715 cell->WXActivateCell(cell_rect, GetModel(), item, colIdx, NULL);
0fff3dfc
VS
3716
3717 break;
3718 }
3719 // else: fall through to WXK_F2 handling
3720 }
3721
3722 case WXK_F2:
a739b8b2
VS
3723 if ( event.HasModifiers() )
3724 {
3725 event.Skip();
3726 break;
3727 }
3728 else
0fff3dfc
VS
3729 {
3730 if( !m_selection.empty() )
3731 {
3732 // Mimic Windows 7 behavior: edit the item that has focus
3733 // if it is selected and the first selected item if focus
3734 // is out of selection.
3735 int sel;
3736 if ( m_selection.Index(m_currentRow) != wxNOT_FOUND )
3737 sel = m_currentRow;
3738 else
3739 sel = m_selection[0];
3740
3741
3742 const wxDataViewItem item = GetItemByRow(sel);
3743
3744 // Edit the current column. If no column is focused
3745 // (typically because the user has full row selected), try
3746 // to find the first editable column.
3747 wxDataViewColumn *editableCol = FindColumnForEditing(item, wxDATAVIEW_CELL_EDITABLE);
3748
3749 if ( editableCol )
907f09f4 3750 GetOwner()->EditItem(item, editableCol);
276227fc 3751 }
d37b709c
RR
3752 }
3753 break;
48ae48a9 3754
cab07038 3755 case WXK_UP:
ed3aece5 3756 OnVerticalNavigation( -1, event );
cab07038
RR
3757 break;
3758
3759 case WXK_DOWN:
ed3aece5 3760 OnVerticalNavigation( +1, event );
cab07038 3761 break;
bc4f1ff2 3762 // Add the process for tree expanding/collapsing
3b6280be 3763 case WXK_LEFT:
cfc21881 3764 OnLeftKey();
235d5f88 3765 break;
cfc21881 3766
235d5f88 3767 case WXK_RIGHT:
cfc21881 3768 OnRightKey();
235d5f88 3769 break;
cfc21881 3770
cab07038 3771 case WXK_END:
ed3aece5 3772 OnVerticalNavigation( +(int)GetRowCount(), event );
cab07038 3773 break;
ed3aece5 3774
cab07038 3775 case WXK_HOME:
ed3aece5 3776 OnVerticalNavigation( -(int)GetRowCount(), event );
cab07038
RR
3777 break;
3778
3779 case WXK_PAGEUP:
ed3aece5 3780 OnVerticalNavigation( -(pageSize - 1), event );
cab07038
RR
3781 break;
3782
3783 case WXK_PAGEDOWN:
ed3aece5 3784 OnVerticalNavigation( +(pageSize - 1), event );
cab07038
RR
3785 break;
3786
3787 default:
3788 event.Skip();
3789 }
3790}
3791
ed3aece5 3792void wxDataViewMainWindow::OnVerticalNavigation(int delta, const wxKeyEvent& event)
1dc779fc 3793{
1dc779fc 3794 // if there is no selection, we cannot move it anywhere
ed3aece5 3795 if (!HasCurrentRow() || IsEmpty())
1dc779fc
VS
3796 return;
3797
ed3aece5
VZ
3798 int newRow = (int)m_currentRow + delta;
3799
3800 // let's keep the new row inside the allowed range
3801 if ( newRow < 0 )
3802 newRow = 0;
3803
3804 const int rowCount = (int)GetRowCount();
3805 if ( newRow >= rowCount )
3806 newRow = rowCount - 1;
3807
1dc779fc 3808 unsigned int oldCurrent = m_currentRow;
ed3aece5 3809 unsigned int newCurrent = (unsigned int)newRow;
1dc779fc
VS
3810
3811 // in single selection we just ignore Shift as we can't select several
3812 // items anyhow
3813 if ( event.ShiftDown() && !IsSingleSel() )
3814 {
3815 RefreshRow( oldCurrent );
3816
3817 ChangeCurrentRow( newCurrent );
3818
3819 // select all the items between the old and the new one
3820 if ( oldCurrent > newCurrent )
3821 {
3822 newCurrent = oldCurrent;
3823 oldCurrent = m_currentRow;
3824 }
3825
3826 SelectRows( oldCurrent, newCurrent, true );
3827 if (oldCurrent!=newCurrent)
3828 SendSelectionChangedEvent(GetItemByRow(m_selection[0]));
3829 }
3830 else // !shift
3831 {
3832 RefreshRow( oldCurrent );
3833
3834 // all previously selected items are unselected unless ctrl is held
3835 if ( !event.ControlDown() )
3836 SelectAllRows(false);
3837
3838 ChangeCurrentRow( newCurrent );
3839
3840 if ( !event.ControlDown() )
3841 {
3842 SelectRow( m_currentRow, true );
3843 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
3844 }
3845 else
3846 RefreshRow( m_currentRow );
3847 }
3848
3849 GetOwner()->EnsureVisible( m_currentRow, -1 );
3850}
3851
cfc21881
VS
3852void wxDataViewMainWindow::OnLeftKey()
3853{
1c8e71c9
VS
3854 if ( IsList() )
3855 {
3856 TryAdvanceCurrentColumn(NULL, /*forward=*/false);
3857 }
3858 else
3859 {
3860 wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow);
ed3aece5
VZ
3861 if ( !node )
3862 return;
cfc21881 3863
1c8e71c9
VS
3864 if ( TryAdvanceCurrentColumn(node, /*forward=*/false) )
3865 return;
cfc21881 3866
1c8e71c9
VS
3867 // Because TryAdvanceCurrentColumn() return false, we are at the first
3868 // column or using whole-row selection. In this situation, we can use
3869 // the standard TreeView handling of the left key.
3870 if (node->HasChildren() && node->IsOpen())
3871 {
3872 Collapse(m_currentRow);
3873 }
3874 else
3875 {
3876 // if the node is already closed, we move the selection to its parent
3877 wxDataViewTreeNode *parent_node = node->GetParent();
3878
3879 if (parent_node)
3880 {
3881 int parent = GetRowByItem( parent_node->GetItem() );
3882 if ( parent >= 0 )
3883 {
3884 unsigned int row = m_currentRow;
3885 SelectRow( row, false);
3886 SelectRow( parent, true );
3887 ChangeCurrentRow( parent );
3888 GetOwner()->EnsureVisible( parent, -1 );
3889 SendSelectionChangedEvent( parent_node->GetItem() );
3890 }
3891 }
3892 }
3893 }
3894}
3895
3896void wxDataViewMainWindow::OnRightKey()
3897{
3898 if ( IsList() )
cfc21881 3899 {
1c8e71c9 3900 TryAdvanceCurrentColumn(NULL, /*forward=*/true);
cfc21881 3901 }
1c8e71c9 3902 else
cfc21881 3903 {
1c8e71c9 3904 wxDataViewTreeNode* node = GetTreeNodeByRow(m_currentRow);
ed3aece5
VZ
3905 if ( !node )
3906 return;
cfc21881 3907
1c8e71c9 3908 if ( node->HasChildren() )
cfc21881 3909 {
1c8e71c9 3910 if ( !node->IsOpen() )
cfc21881 3911 {
1c8e71c9
VS
3912 Expand( m_currentRow );
3913 }
3914 else
3915 {
3916 // if the node is already open, we move the selection to the first child
cfc21881 3917 unsigned int row = m_currentRow;
1c8e71c9
VS
3918 SelectRow( row, false );
3919 SelectRow( row + 1, true );
3920 ChangeCurrentRow( row + 1 );
3921 GetOwner()->EnsureVisible( row + 1, -1 );
3922 SendSelectionChangedEvent( GetItemByRow(row+1) );
cfc21881
VS
3923 }
3924 }
1c8e71c9
VS
3925 else
3926 {
3927 TryAdvanceCurrentColumn(node, /*forward=*/true);
3928 }
cfc21881
VS
3929 }
3930}
3931
1c8e71c9 3932bool wxDataViewMainWindow::TryAdvanceCurrentColumn(wxDataViewTreeNode *node, bool forward)
cfc21881 3933{
1c8e71c9
VS
3934 if ( GetOwner()->GetColumnCount() == 0 )
3935 return false;
3936
3937 if ( !m_useCellFocus )
3938 return false;
3939
3940 if ( node )
3941 {
3942 // navigation shouldn't work in branch nodes without other columns:
3943 if ( node->HasChildren() && !GetModel()->HasContainerColumns(node->GetItem()) )
3944 return false;
3945 }
3946
3947 if ( m_currentCol == NULL || !m_currentColSetByKeyboard )
3948 {
3949 if ( forward )
3950 {
3951 m_currentCol = GetOwner()->GetColumnAt(1);
3952 m_currentColSetByKeyboard = true;
3953 RefreshRow(m_currentRow);
3954 return true;
3955 }
3956 else
3957 return false;
3958 }
3959
3960 int idx = GetOwner()->GetColumnIndex(m_currentCol) + (forward ? +1 : -1);
3961
3962 if ( idx >= (int)GetOwner()->GetColumnCount() )
3963 return false;
3964
78e18e8d
VS
3965 GetOwner()->EnsureVisible(m_currentRow, idx);
3966
1c8e71c9 3967 if ( idx < 1 )
cfc21881 3968 {
1c8e71c9
VS
3969 // We are going to the left of the second column. Reset to whole-row
3970 // focus (which means first column would be edited).
3971 m_currentCol = NULL;
3972 RefreshRow(m_currentRow);
3973 return true;
cfc21881 3974 }
1c8e71c9
VS
3975
3976 m_currentCol = GetOwner()->GetColumnAt(idx);
3977 m_currentColSetByKeyboard = true;
3978 RefreshRow(m_currentRow);
3979 return true;
cfc21881
VS
3980}
3981
4ed7af08
RR
3982void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
3983{
e21f75bd
RR
3984 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
3985 {
3986 // let the base handle mouse wheel events.
3987 event.Skip();
3988 return;
3989 }
3990
a386b9c6 3991 if(event.ButtonDown())
6ceda4f5 3992 {
a386b9c6
VZ
3993 // Not skipping button down events would prevent the system from
3994 // setting focus to this window as most (all?) of them do by default,
3995 // so skip it to enable default handling.
6ceda4f5
VZ
3996 event.Skip();
3997 }
3d9bff2f 3998
0fdc2321
RR
3999 int x = event.GetX();
4000 int y = event.GetY();
4001 m_owner->CalcUnscrolledPosition( x, y, &x, &y );
0fdc2321 4002 wxDataViewColumn *col = NULL;
b7fe2261 4003
0fdc2321 4004 int xpos = 0;
9861f022 4005 unsigned int cols = GetOwner()->GetColumnCount();
0a71f9e9 4006 unsigned int i;
0fdc2321
RR
4007 for (i = 0; i < cols; i++)
4008 {
702f5349 4009 wxDataViewColumn *c = GetOwner()->GetColumnAt( i );
9861f022
RR
4010 if (c->IsHidden())
4011 continue; // skip it!
4012
0fdc2321
RR
4013 if (x < xpos + c->GetWidth())
4014 {
4015 col = c;
4016 break;
4017 }
4018 xpos += c->GetWidth();
4019 }
7ed24cb6
VZ
4020
4021 wxDataViewModel* const model = GetModel();
4022
4023 const unsigned int current = GetLineAt( y );
4024 const wxDataViewItem item = GetItemByRow(current);
4025
4026 // Handle right clicking here, before everything else as context menu
4027 // events should be sent even when we click outside of any item, unlike all
4028 // the other ones.
4029 if (event.RightUp())
4030 {
4031 wxWindow *parent = GetParent();
4032 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU, parent->GetId());
4033 le.SetEventObject(parent);
4034 le.SetModel(model);
4035
4036 if ( item.IsOk() && col )
4037 {
4038 le.SetItem( item );
4039 le.SetColumn( col->GetModelColumn() );
4040 le.SetDataViewColumn( col );
7ed24cb6
VZ
4041 }
4042
4043 parent->ProcessWindowEvent(le);
4044 return;
4045 }
4046
11e3c6ef
VZ
4047 // Check if we clicked outside the item area.
4048 if ((current >= GetRowCount()) || !col)
737883f2 4049 {
11e3c6ef
VZ
4050 // Follow Windows convention here: clicking either left or right (but
4051 // not middle) button clears the existing selection.
4052 if (m_owner && (event.LeftDown() || event.RightDown()))
4053 {
4054 if (!GetSelections().empty())
4055 {
4056 m_owner->UnselectAll();
4057 SendSelectionChangedEvent(wxDataViewItem());
4058 }
4059 }
737883f2 4060 event.Skip();
0fdc2321 4061 return;
737883f2 4062 }
f554a14b 4063
24c4a50f 4064 wxDataViewRenderer *cell = col->GetRenderer();
1841f079
VZ
4065 wxDataViewColumn* const
4066 expander = GetExpanderColumnOrFirstOne(GetOwner());
4067
902334c8
VZ
4068 // Test whether the mouse is hovering over the expander (a.k.a tree "+"
4069 // button) and also determine the offset of the real cell start, skipping
4070 // the indentation and the expander itself.
abdb8c18 4071 bool hoverOverExpander = false;
c46ecbe3 4072 int itemOffset = 0;
1841f079 4073 if ((!IsList()) && (expander == col))
24c4a50f
RR
4074 {
4075 wxDataViewTreeNode * node = GetTreeNodeByRow(current);
abdb8c18 4076
c46ecbe3
VZ
4077 int indent = node->GetIndentLevel();
4078 itemOffset = GetOwner()->GetIndent()*indent;
4079
4080 if ( node->HasChildren() )
4081 {
977a41ec
FM
4082 // we make the rectangle we are looking in a bit bigger than the actual
4083 // visual expander so the user can hit that little thing reliably
c46ecbe3 4084 wxRect rect(itemOffset,
977a41ec
FM
4085 GetLineStart( current ) + (GetLineHeight(current) - m_lineHeight)/2,
4086 m_lineHeight, m_lineHeight);
902334c8 4087
abdb8c18 4088 if( rect.Contains(x, y) )
24c4a50f 4089 {
bc4f1ff2 4090 // So the mouse is over the expander
abdb8c18 4091 hoverOverExpander = true;
24c4a50f
RR
4092 if (m_underMouse && m_underMouse != node)
4093 {
bc4f1ff2 4094 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
df2c23e7 4095 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
24c4a50f
RR
4096 }
4097 if (m_underMouse != node)
4098 {
bc4f1ff2 4099 // wxLogMessage("Do the row: %d", current);
df2c23e7 4100 RefreshRow(current);
24c4a50f
RR
4101 }
4102 m_underMouse = node;
4103 }
4104 }
c46ecbe3
VZ
4105
4106 // Account for the expander as well, even if this item doesn't have it,
4107 // its parent does so it still counts for the offset.
4108 itemOffset += m_lineHeight;
24c4a50f 4109 }
abdb8c18 4110 if (!hoverOverExpander)
24c4a50f
RR
4111 {
4112 if (m_underMouse != NULL)
4113 {
bc4f1ff2 4114 // wxLogMessage("Undo the row: %d", GetRowByItem(m_underMouse->GetItem()));
df2c23e7 4115 RefreshRow(GetRowByItem(m_underMouse->GetItem()));
24c4a50f
RR
4116 m_underMouse = NULL;
4117 }
4118 }
4119
9adeb77a 4120#if wxUSE_DRAG_AND_DROP
e21f75bd
RR
4121 if (event.Dragging())
4122 {
4123 if (m_dragCount == 0)
4124 {
4125 // we have to report the raw, physical coords as we want to be
4126 // able to call HitTest(event.m_pointDrag) from the user code to
4127 // get the item being dragged
4128 m_dragStart = event.GetPosition();
4129 }
4130
4131 m_dragCount++;
4132
4133 if (m_dragCount != 3)
4134 return;
4135
4136 if (event.LeftIsDown())
4137 {
4cef3873 4138 m_owner->CalcUnscrolledPosition( m_dragStart.x, m_dragStart.y,
bc4f1ff2 4139 &m_dragStart.x, &m_dragStart.y );
821baf7d 4140 unsigned int drag_item_row = GetLineAt( m_dragStart.y );
7ed24cb6 4141 wxDataViewItem itemDragged = GetItemByRow( drag_item_row );
821baf7d 4142
e21f75bd 4143 // Notify cell about drag
821baf7d
RR
4144 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_BEGIN_DRAG, m_owner->GetId() );
4145 event.SetEventObject( m_owner );
7ed24cb6 4146 event.SetItem( itemDragged );
821baf7d
RR
4147 event.SetModel( model );
4148 if (!m_owner->HandleWindowEvent( event ))
4149 return;
9adeb77a 4150
821baf7d
RR
4151 if (!event.IsAllowed())
4152 return;
9adeb77a 4153
821baf7d
RR
4154 wxDataObject *obj = event.GetDataObject();
4155 if (!obj)
4156 return;
9adeb77a 4157
818d91a9 4158 wxDataViewDropSource drag( this, drag_item_row );
592883ed 4159 drag.SetData( *obj );
c04be1a2 4160 /* wxDragResult res = */ drag.DoDragDrop(event.GetDragFlags());
592883ed 4161 delete obj;
e21f75bd
RR
4162 }
4163 return;
4164 }
4165 else
4166 {
4167 m_dragCount = 0;
4168 }
9adeb77a 4169#endif // wxUSE_DRAG_AND_DROP
e21f75bd 4170
abdb8c18 4171 bool simulateClick = false;
e21f75bd 4172
0fcce6b9
RR
4173 if (event.ButtonDClick())
4174 {
4175 m_renameTimer->Stop();
4176 m_lastOnSame = false;
4177 }
4178
438fb233 4179 bool ignore_other_columns =
1841f079 4180 ((expander != col) &&
977a41ec
FM
4181 (model->IsContainer(item)) &&
4182 (!model->HasContainerColumns(item)));
b5ec7dd6 4183
0fdc2321
RR
4184 if (event.LeftDClick())
4185 {
977a41ec
FM
4186 if(hoverOverExpander)
4187 {
4188 // a double click on the expander will be converted into a "simulated" normal click
4189 simulateClick = true;
4190 }
4191 else if ( current == m_lineLastClicked )
0fdc2321 4192 {
dc73d7f5
VS
4193 wxWindow *parent = GetParent();
4194 wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
4195 le.SetItem( item );
4196 le.SetColumn( col->GetModelColumn() );
4197 le.SetDataViewColumn( col );
4198 le.SetEventObject(parent);
4199 le.SetModel(GetModel());
b7e9f8b1 4200
dc73d7f5 4201 parent->ProcessWindowEvent(le);
e21f75bd
RR
4202 return;
4203 }
4204 else
4205 {
4206 // The first click was on another item, so don't interpret this as
4207 // a double click, but as a simple click instead
abdb8c18 4208 simulateClick = true;
0fdc2321 4209 }
120b9b05 4210 }
f554a14b 4211
abdb8c18 4212 if (event.LeftUp() && !hoverOverExpander)
0fcce6b9 4213 {
0a71f9e9 4214 if (m_lineSelectSingleOnUp != (unsigned int)-1)
e21f75bd
RR
4215 {
4216 // select single line
4217 SelectAllRows( false );
4218 SelectRow( m_lineSelectSingleOnUp, true );
4a745e76 4219 SendSelectionChangedEvent( GetItemByRow(m_lineSelectSingleOnUp) );
e21f75bd 4220 }
120b9b05 4221
4cef3873 4222 // If the user click the expander, we do not do editing even if the column
bc4f1ff2 4223 // with expander are editable
abdb8c18 4224 if (m_lastOnSame && !ignore_other_columns)
0fcce6b9 4225 {
a8461d31 4226 if ((col == m_currentCol) && (current == m_currentRow) &&
cf5d4c76 4227 IsCellEditableInMode(item, col, wxDATAVIEW_CELL_EDITABLE) )
0fcce6b9
RR
4228 {
4229 m_renameTimer->Start( 100, true );
4230 }
4231 }
4232
4233 m_lastOnSame = false;
0a71f9e9 4234 m_lineSelectSingleOnUp = (unsigned int)-1;
120b9b05 4235 }
abdb8c18 4236 else if(!event.LeftUp())
e21f75bd
RR
4237 {
4238 // This is necessary, because after a DnD operation in
4239 // from and to ourself, the up event is swallowed by the
4240 // DnD code. So on next non-up event (which means here and
4241 // now) m_lineSelectSingleOnUp should be reset.
0a71f9e9 4242 m_lineSelectSingleOnUp = (unsigned int)-1;
e21f75bd
RR
4243 }
4244
4245 if (event.RightDown())
4246 {
4247 m_lineBeforeLastClicked = m_lineLastClicked;
4248 m_lineLastClicked = current;
4249
4250 // If the item is already selected, do not update the selection.
4251 // Multi-selections should not be cleared if a selected item is clicked.
4252 if (!IsRowSelected(current))
4253 {
4254 SelectAllRows(false);
a09da78b 4255 const unsigned oldCurrent = m_currentRow;
e21f75bd
RR
4256 ChangeCurrentRow(current);
4257 SelectRow(m_currentRow,true);
a09da78b 4258 RefreshRow(oldCurrent);
526e19e2 4259 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
e21f75bd 4260 }
0a807957 4261 }
e21f75bd
RR
4262 else if (event.MiddleDown())
4263 {
e21f75bd 4264 }
abdb8c18 4265
977a41ec
FM
4266 if((event.LeftDown() || simulateClick) && hoverOverExpander)
4267 {
4268 wxDataViewTreeNode* node = GetTreeNodeByRow(current);
bc4f1ff2 4269
4cef3873 4270 // hoverOverExpander being true tells us that our node must be
bc4f1ff2 4271 // valid and have children.
977a41ec
FM
4272 // So we don't need any extra checks.
4273 if( node->IsOpen() )
235d5f88 4274 Collapse(current);
977a41ec 4275 else
235d5f88 4276 Expand(current);
977a41ec
FM
4277 }
4278 else if ((event.LeftDown() || simulateClick) && !hoverOverExpander)
0fcce6b9 4279 {
e21f75bd
RR
4280 m_lineBeforeLastClicked = m_lineLastClicked;
4281 m_lineLastClicked = current;
4282
0a71f9e9 4283 unsigned int oldCurrentRow = m_currentRow;
e21f75bd
RR
4284 bool oldWasSelected = IsRowSelected(m_currentRow);
4285
4286 bool cmdModifierDown = event.CmdDown();
4287 if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
4288 {
4289 if ( IsSingleSel() || !IsRowSelected(current) )
4290 {
4291 SelectAllRows( false );
e21f75bd 4292 ChangeCurrentRow(current);
e21f75bd 4293 SelectRow(m_currentRow,true);
526e19e2 4294 SendSelectionChangedEvent(GetItemByRow( m_currentRow ) );
e21f75bd
RR
4295 }
4296 else // multi sel & current is highlighted & no mod keys
4297 {
4298 m_lineSelectSingleOnUp = current;
4299 ChangeCurrentRow(current); // change focus
4300 }
4301 }
4302 else // multi sel & either ctrl or shift is down
4303 {
4304 if (cmdModifierDown)
4305 {
4306 ChangeCurrentRow(current);
e21f75bd 4307 ReverseRowSelection(m_currentRow);
9439bc69 4308 SendSelectionChangedEvent(GetItemByRow(m_currentRow));
e21f75bd
RR
4309 }
4310 else if (event.ShiftDown())
4311 {
4312 ChangeCurrentRow(current);
4313
0a71f9e9 4314 unsigned int lineFrom = oldCurrentRow,
977a41ec 4315 lineTo = current;
e21f75bd
RR
4316
4317 if ( lineTo < lineFrom )
4318 {
4319 lineTo = lineFrom;
4320 lineFrom = m_currentRow;
4321 }
4322
4323 SelectRows(lineFrom, lineTo, true);
526e19e2 4324 SendSelectionChangedEvent(GetItemByRow(m_selection[0]) );
e21f75bd
RR
4325 }
4326 else // !ctrl, !shift
4327 {
4328 // test in the enclosing if should make it impossible
9a83f860 4329 wxFAIL_MSG( wxT("how did we get here?") );
e21f75bd
RR
4330 }
4331 }
777f9cef 4332
72664514
RR
4333 if (m_currentRow != oldCurrentRow)
4334 RefreshRow( oldCurrentRow );
e21f75bd 4335
0fcce6b9 4336 wxDataViewColumn *oldCurrentCol = m_currentCol;
120b9b05 4337
0fcce6b9
RR
4338 // Update selection here...
4339 m_currentCol = col;
1c8e71c9 4340 m_currentColSetByKeyboard = false;
120b9b05 4341
15ec266a
VZ
4342 // This flag is used to decide whether we should start editing the item
4343 // label. We do it if the user clicks twice (but not double clicks,
4344 // i.e. simulateClick is false) on the same item but not if the click
4345 // was used for something else already, e.g. selecting the item (so it
4346 // must have been already selected) or giving the focus to the control
4347 // (so it must have had focus already).
abdb8c18 4348 m_lastOnSame = !simulateClick && ((col == oldCurrentCol) &&
15ec266a
VZ
4349 (current == oldCurrentRow)) && oldWasSelected &&
4350 HasFocus();
0bdfa388 4351
dc73d7f5 4352 // Call ActivateCell() after everything else as under GTK+
cf5d4c76 4353 if ( IsCellEditableInMode(item, col, wxDATAVIEW_CELL_ACTIVATABLE) )
0bdfa388 4354 {
dbc3aec1
VS
4355 // notify cell about click
4356 cell->PrepareForItem(model, item, col->GetModelColumn());
86755098 4357
c46ecbe3 4358 wxRect cell_rect( xpos + itemOffset,
902334c8 4359 GetLineStart( current ),
c46ecbe3 4360 col->GetWidth() - itemOffset,
902334c8 4361 GetLineHeight( current ) );
a3a8d81d 4362
dbc3aec1
VS
4363 // Report position relative to the cell's custom area, i.e.
4364 // no the entire space as given by the control but the one
4365 // used by the renderer after calculation of alignment etc.
a3a8d81d 4366
dbc3aec1
VS
4367 // adjust the rectangle ourselves to account for the alignment
4368 wxRect rectItem = cell_rect;
4369 const int align = cell->GetAlignment();
4370 if ( align != wxDVR_DEFAULT_ALIGNMENT )
4371 {
4372 const wxSize size = cell->GetSize();
a3a8d81d 4373
dbc3aec1
VS
4374 if ( size.x >= 0 && size.x < cell_rect.width )
4375 {
4376 if ( align & wxALIGN_CENTER_HORIZONTAL )
4377 rectItem.x += (cell_rect.width - size.x)/2;
4378 else if ( align & wxALIGN_RIGHT )
4379 rectItem.x += cell_rect.width - size.x;
4380 // else: wxALIGN_LEFT is the default
4381 }
a3a8d81d 4382
dbc3aec1
VS
4383 if ( size.y >= 0 && size.y < cell_rect.height )
4384 {
4385 if ( align & wxALIGN_CENTER_VERTICAL )
4386 rectItem.y += (cell_rect.height - size.y)/2;
4387 else if ( align & wxALIGN_BOTTOM )
4388 rectItem.y += cell_rect.height - size.y;
4389 // else: wxALIGN_TOP is the default
a3a8d81d 4390 }
dbc3aec1 4391 }
a3a8d81d 4392
dc73d7f5
VS
4393 wxMouseEvent event2(event);
4394 event2.m_x -= rectItem.x;
4395 event2.m_y -= rectItem.y;
4396 m_owner->CalcUnscrolledPosition(event2.m_x, event2.m_y, &event2.m_x, &event2.m_y);
a3a8d81d 4397
dc73d7f5
VS
4398 /* ignore ret */ cell->WXActivateCell
4399 (
4400 cell_rect,
4401 model,
4402 item,
4403 col->GetModelColumn(),
4404 &event2
4405 );
0bdfa388 4406 }
0fdc2321 4407 }
4ed7af08
RR
4408}
4409
4410void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
4411{
cab07038 4412 m_hasFocus = true;
120b9b05 4413
cab07038
RR
4414 if (HasCurrentRow())
4415 Refresh();
120b9b05 4416
cab07038
RR
4417 event.Skip();
4418}
4419
4420void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
4421{
4422 m_hasFocus = false;
120b9b05 4423
cab07038
RR
4424 if (HasCurrentRow())
4425 Refresh();
120b9b05 4426
4ed7af08
RR
4427 event.Skip();
4428}
4429
1c8e71c9
VS
4430void wxDataViewMainWindow::OnColumnsCountChanged()
4431{
4432 int editableCount = 0;
4433
4434 const unsigned cols = GetOwner()->GetColumnCount();
4435 for ( unsigned i = 0; i < cols; i++ )
4436 {
4437 wxDataViewColumn *c = GetOwner()->GetColumnAt(i);
4438 if ( c->IsHidden() )
4439 continue;
4440 if ( c->GetRenderer()->GetMode() != wxDATAVIEW_CELL_INERT )
4441 editableCount++;
4442 }
4443
4444 m_useCellFocus = (editableCount > 1);
4445
4446 UpdateDisplay();
4447}
4448
4ed7af08
RR
4449//-----------------------------------------------------------------------------
4450// wxDataViewCtrl
4451//-----------------------------------------------------------------------------
bc4f1ff2 4452
a76c2f37 4453WX_DEFINE_LIST(wxDataViewColumnList)
4ed7af08
RR
4454
4455IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
4b3feaa7
RR
4456BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
4457 EVT_SIZE(wxDataViewCtrl::OnSize)
4458END_EVENT_TABLE()
4459
4ed7af08
RR
4460wxDataViewCtrl::~wxDataViewCtrl()
4461{
4462 if (m_notifier)
4463 GetModel()->RemoveNotifier( m_notifier );
74123073 4464
b3a3c9d8 4465 m_cols.Clear();
d0154e3a 4466 m_colsBestWidths.clear();
4ed7af08
RR
4467}
4468
4469void wxDataViewCtrl::Init()
4470{
b3a3c9d8 4471 m_cols.DeleteContents(true);
4ed7af08 4472 m_notifier = NULL;
e822d1bd 4473
236a34ef 4474 // No sorting column at start
46234a03
VZ
4475 m_sortingColumnIdx = wxNOT_FOUND;
4476
236a34ef 4477 m_headerArea = NULL;
09f84bc7 4478 m_clientArea = NULL;
bed74e48
VS
4479
4480 m_colsDirty = false;
4ed7af08
RR
4481}
4482
62e9285a
VZ
4483bool wxDataViewCtrl::Create(wxWindow *parent,
4484 wxWindowID id,
4485 const wxPoint& pos,
4486 const wxSize& size,
4487 long style,
4488 const wxValidator& validator,
4489 const wxString& name)
4ed7af08 4490{
3fdf86f9
RR
4491// if ( (style & wxBORDER_MASK) == 0)
4492// style |= wxBORDER_SUNKEN;
777f9cef 4493
236a34ef
RR
4494 Init();
4495
c741d33f 4496 if (!wxControl::Create( parent, id, pos, size,
62e9285a 4497 style | wxScrolledWindowStyle, validator, name))
4b3feaa7
RR
4498 return false;
4499
b89cac3f 4500 SetInitialSize(size);
b5ec7dd6 4501
4ed7af08 4502#ifdef __WXMAC__
59e60167 4503 MacSetClipChildren( true );
4ed7af08
RR
4504#endif
4505
f554a14b 4506 m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
9861f022 4507
63ced01b
VZ
4508 // We use the cursor keys for moving the selection, not scrolling, so call
4509 // this method to ensure wxScrollHelperEvtHandler doesn't catch all
4510 // keyboard events forwarded to us from wxListMainWindow.
4511 DisableKeyboardScrolling();
4512
9861f022
RR
4513 if (HasFlag(wxDV_NO_HEADER))
4514 m_headerArea = NULL;
4515 else
56873923 4516 m_headerArea = new wxDataViewHeaderWindow(this);
f554a14b 4517
4ed7af08 4518 SetTargetWindow( m_clientArea );
f554a14b 4519
4ed7af08 4520 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
9861f022
RR
4521 if (m_headerArea)
4522 sizer->Add( m_headerArea, 0, wxGROW );
4ed7af08
RR
4523 sizer->Add( m_clientArea, 1, wxGROW );
4524 SetSizer( sizer );
b5ec7dd6 4525
4ed7af08
RR
4526 return true;
4527}
4528
3fdf86f9
RR
4529wxBorder wxDataViewCtrl::GetDefaultBorder() const
4530{
4531 return wxBORDER_THEME;
4532}
4533
4ed7af08
RR
4534#ifdef __WXMSW__
4535WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
bc4f1ff2
FM
4536 WXWPARAM wParam,
4537 WXLPARAM lParam)
4ed7af08 4538{
b910a8ad 4539 WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
4ed7af08
RR
4540
4541#ifndef __WXWINCE__
4542 // we need to process arrows ourselves for scrolling
4543 if ( nMsg == WM_GETDLGCODE )
4544 {
4545 rc |= DLGC_WANTARROWS;
4546 }
4547#endif
4548
4549 return rc;
4550}
4551#endif
4552
2571a33f
RR
4553wxSize wxDataViewCtrl::GetSizeAvailableForScrollTarget(const wxSize& size)
4554{
4555 wxSize newsize = size;
d7cda9b2 4556 if (!HasFlag(wxDV_NO_HEADER) && (m_headerArea))
977a41ec 4557 newsize.y -= m_headerArea->GetSize().y;
e822d1bd 4558
2571a33f
RR
4559 return newsize;
4560}
4561
f554a14b 4562void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
4ed7af08 4563{
4b3feaa7
RR
4564 // We need to override OnSize so that our scrolled
4565 // window a) does call Layout() to use sizers for
4566 // positioning the controls but b) does not query
4567 // the sizer for their size and use that for setting
4568 // the scrollable area as set that ourselves by
4569 // calling SetScrollbar() further down.
4570
4571 Layout();
4572
4573 AdjustScrollbars();
cd9b34ef
VZ
4574
4575 // We must redraw the headers if their height changed. Normally this
4576 // shouldn't happen as the control shouldn't let itself be resized beneath
4577 // its minimal height but avoid the display artefacts that appear if it
4578 // does happen, e.g. because there is really not enough vertical space.
4579 if ( !HasFlag(wxDV_NO_HEADER) && m_headerArea &&
4580 m_headerArea->GetSize().y <= m_headerArea->GetBestSize(). y )
4581 {
4582 m_headerArea->Refresh();
4583 }
4ed7af08
RR
4584}
4585
788432e3
RR
4586void wxDataViewCtrl::SetFocus()
4587{
4588 if (m_clientArea)
4589 m_clientArea->SetFocus();
4590}
4591
1af67319
VZ
4592bool wxDataViewCtrl::SetFont(const wxFont & font)
4593{
4594 if (!wxControl::SetFont(font))
4595 return false;
4596
4597 if (m_headerArea)
4598 m_headerArea->SetFont(font);
4599
4600 if (m_clientArea)
4601 {
4602 m_clientArea->SetFont(font);
4603 m_clientArea->SetRowHeight(m_clientArea->GetDefaultRowHeight());
4604 }
4605
4606 if (m_headerArea || m_clientArea)
4607 {
4608 InvalidateColBestWidths();
4609 Layout();
4610 }
4611
4612 return true;
4613}
4614
4615
4616
aba9bfd0 4617bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
4ed7af08
RR
4618{
4619 if (!wxDataViewCtrlBase::AssociateModel( model ))
4620 return false;
4621
be416221
VZ
4622 if (model)
4623 {
4624 m_notifier = new wxGenericDataViewModelNotifier( m_clientArea );
4625 model->AddNotifier( m_notifier );
4626 }
4627 else if (m_notifier)
4628 {
4629 m_notifier->Cleared();
4630 m_notifier = NULL;
4631 }
4ed7af08 4632
51bdecff 4633 m_clientArea->DestroyTree();
cfa42cb8 4634
be416221
VZ
4635 if (model)
4636 {
4637 m_clientArea->BuildTree(model);
4638 }
aba9bfd0 4639
4b3feaa7 4640 m_clientArea->UpdateDisplay();
f554a14b 4641
4ed7af08
RR
4642 return true;
4643}
4644
9adeb77a
VZ
4645#if wxUSE_DRAG_AND_DROP
4646
821baf7d
RR
4647bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format )
4648{
4649 return m_clientArea->EnableDragSource( format );
4650}
4651
4652bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format )
4653{
4654 return m_clientArea->EnableDropTarget( format );
4655}
4656
9adeb77a
VZ
4657#endif // wxUSE_DRAG_AND_DROP
4658
4ed7af08
RR
4659bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
4660{
4661 if (!wxDataViewCtrlBase::AppendColumn(col))
4662 return false;
f554a14b 4663
afebb87b 4664 m_cols.Append( col );
840fc4d1 4665 m_colsBestWidths.push_back(CachedColWidthInfo());
aef252d9 4666 OnColumnsCountChanged();
736fe67c
RR
4667 return true;
4668}
4669
4670bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
4671{
4672 if (!wxDataViewCtrlBase::PrependColumn(col))
4673 return false;
4674
4675 m_cols.Insert( col );
840fc4d1 4676 m_colsBestWidths.insert(m_colsBestWidths.begin(), CachedColWidthInfo());
aef252d9 4677 OnColumnsCountChanged();
4ed7af08
RR
4678 return true;
4679}
4680
19723525
RR
4681bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
4682{
4683 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
4684 return false;
4685
4686 m_cols.Insert( pos, col );
840fc4d1 4687 m_colsBestWidths.insert(m_colsBestWidths.begin() + pos, CachedColWidthInfo());
aef252d9 4688 OnColumnsCountChanged();
19723525
RR
4689 return true;
4690}
4691
aef252d9
VZ
4692void wxDataViewCtrl::OnColumnChange(unsigned int idx)
4693{
4694 if ( m_headerArea )
4695 m_headerArea->UpdateColumn(idx);
4696
4697 m_clientArea->UpdateDisplay();
4698}
4699
4700void wxDataViewCtrl::OnColumnsCountChanged()
9861f022
RR
4701{
4702 if (m_headerArea)
e2bfe673 4703 m_headerArea->SetColumnCount(GetColumnCount());
9861f022 4704
1c8e71c9 4705 m_clientArea->OnColumnsCountChanged();
9861f022 4706}
3b6280be
RR
4707
4708void wxDataViewCtrl::DoSetExpanderColumn()
4709{
4710 m_clientArea->UpdateDisplay();
4711}
4712
4713void wxDataViewCtrl::DoSetIndent()
4714{
4715 m_clientArea->UpdateDisplay();
4716}
4717
afebb87b
RR
4718unsigned int wxDataViewCtrl::GetColumnCount() const
4719{
4720 return m_cols.GetCount();
4721}
4722
bbfd4548
VZ
4723bool wxDataViewCtrl::SetRowHeight( int lineHeight )
4724{
4725 if ( !m_clientArea )
4726 return false;
4727
4728 m_clientArea->SetRowHeight(lineHeight);
4729
4730 return true;
4731}
4732
aef252d9 4733wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int idx ) const
afebb87b 4734{
aef252d9 4735 return m_cols[idx];
afebb87b
RR
4736}
4737
702f5349 4738wxDataViewColumn *wxDataViewCtrl::GetColumnAt(unsigned int pos) const
fc8c1a15 4739{
702f5349
VZ
4740 // columns can't be reordered if there is no header window which allows
4741 // to do this
4742 const unsigned idx = m_headerArea ? m_headerArea->GetColumnsOrder()[pos]
977a41ec 4743 : pos;
59e60167 4744
702f5349
VZ
4745 return GetColumn(idx);
4746}
cfa42cb8 4747
702f5349
VZ
4748int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn *column) const
4749{
4750 const unsigned count = m_cols.size();
4751 for ( unsigned n = 0; n < count; n++ )
4752 {
4753 if ( m_cols[n] == column )
4754 return n;
4755 }
4756
4757 return wxNOT_FOUND;
4758}
4759
d0154e3a
VS
4760unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
4761{
840fc4d1
VS
4762 if ( m_colsBestWidths[idx].width != 0 )
4763 return m_colsBestWidths[idx].width;
d0154e3a 4764
0645bd38 4765 const int count = m_clientArea->GetRowCount();
d0154e3a
VS
4766 wxDataViewColumn *column = GetColumn(idx);
4767 wxDataViewRenderer *renderer =
4768 const_cast<wxDataViewRenderer*>(column->GetRenderer());
4769
0645bd38
VS
4770 class MaxWidthCalculator
4771 {
4772 public:
0fdeaabe
VS
4773 MaxWidthCalculator(const wxDataViewCtrl *dvc,
4774 wxDataViewMainWindow *clientArea,
0645bd38
VS
4775 wxDataViewRenderer *renderer,
4776 const wxDataViewModel *model,
0fdeaabe
VS
4777 unsigned column,
4778 int expanderSize)
0645bd38 4779 : m_width(0),
0fdeaabe 4780 m_dvc(dvc),
0645bd38
VS
4781 m_clientArea(clientArea),
4782 m_renderer(renderer),
4783 m_model(model),
0fdeaabe
VS
4784 m_column(column),
4785 m_expanderSize(expanderSize)
4786
0645bd38 4787 {
0fdeaabe
VS
4788 m_isExpanderCol =
4789 !clientArea->IsList() &&
4790 (column == 0 ||
4791 GetExpanderColumnOrFirstOne(const_cast<wxDataViewCtrl*>(dvc)) == dvc->GetColumnAt(column));
0645bd38
VS
4792 }
4793
4794 void UpdateWithWidth(int width)
4795 {
4796 m_width = wxMax(m_width, width);
4797 }
4798
4799 void UpdateWithRow(int row)
4800 {
0fdeaabe
VS
4801 int indent = 0;
4802 wxDataViewItem item;
4803
4804 if ( m_isExpanderCol )
4805 {
4806 wxDataViewTreeNode *node = m_clientArea->GetTreeNodeByRow(row);
4807 item = node->GetItem();
4808 indent = m_dvc->GetIndent() * node->GetIndentLevel() + m_expanderSize;
4809 }
4810 else
4811 {
4812 item = m_clientArea->GetItemByRow(row);
4813 }
4814
0645bd38 4815 m_renderer->PrepareForItem(m_model, item, m_column);
0fdeaabe 4816 m_width = wxMax(m_width, m_renderer->GetSize().x + indent);
0645bd38
VS
4817 }
4818
4819 int GetMaxWidth() const { return m_width; }
4820
4821 private:
4822 int m_width;
0fdeaabe 4823 const wxDataViewCtrl *m_dvc;
0645bd38
VS
4824 wxDataViewMainWindow *m_clientArea;
4825 wxDataViewRenderer *m_renderer;
4826 const wxDataViewModel *m_model;
4827 unsigned m_column;
0fdeaabe
VS
4828 bool m_isExpanderCol;
4829 int m_expanderSize;
0645bd38
VS
4830 };
4831
0fdeaabe
VS
4832 MaxWidthCalculator calculator(this, m_clientArea, renderer,
4833 GetModel(), column->GetModelColumn(),
4834 m_clientArea->GetRowHeight());
d0154e3a 4835
f0630187
VS
4836 calculator.UpdateWithWidth(column->GetMinWidth());
4837
d0154e3a 4838 if ( m_headerArea )
53137278 4839 calculator.UpdateWithWidth(m_headerArea->GetColumnTitleWidth(*column));
0645bd38
VS
4840
4841 // The code below deserves some explanation. For very large controls, we
4842 // simply can't afford to calculate sizes for all items, it takes too
4843 // long. So the best we can do is to check the first and the last N/2
4844 // items in the control for some sufficiently large N and calculate best
4845 // sizes from that. That can result in the calculated best width being too
4846 // small for some outliers, but it's better to get slightly imperfect
4847 // result than to wait several seconds after every update. To avoid highly
4848 // visible miscalculations, we also include all currently visible items
4849 // no matter what. Finally, the value of N is determined dynamically by
4850 // measuring how much time we spent on the determining item widths so far.
4851
4852#if wxUSE_STOPWATCH
4853 int top_part_end = count;
4854 static const long CALC_TIMEOUT = 20/*ms*/;
4855 // don't call wxStopWatch::Time() too often
4856 static const unsigned CALC_CHECK_FREQ = 100;
4857 wxStopWatch timer;
4858#else
4859 // use some hard-coded limit, that's the best we can do without timer
4860 int top_part_end = wxMin(500, count);
4861#endif // wxUSE_STOPWATCH/!wxUSE_STOPWATCH
4862
4863 int row = 0;
4864
4865 for ( row = 0; row < top_part_end; row++ )
4866 {
4867#if wxUSE_STOPWATCH
4868 if ( row % CALC_CHECK_FREQ == CALC_CHECK_FREQ-1 &&
4869 timer.Time() > CALC_TIMEOUT )
4870 break;
4871#endif // wxUSE_STOPWATCH
4872 calculator.UpdateWithRow(row);
d0154e3a
VS
4873 }
4874
40fc5b2f 4875 // row is the first unmeasured item now; that's our value of N/2
0645bd38
VS
4876
4877 if ( row < count )
d0154e3a 4878 {
0645bd38 4879 top_part_end = row;
d0154e3a 4880
0645bd38
VS
4881 // add bottom N/2 items now:
4882 const int bottom_part_start = wxMax(row, count - row);
4883 for ( row = bottom_part_start; row < count; row++ )
4884 {
4885 calculator.UpdateWithRow(row);
4886 }
86755098 4887
0645bd38
VS
4888 // finally, include currently visible items in the calculation:
4889 const wxPoint origin = CalcUnscrolledPosition(wxPoint(0, 0));
4890 int first_visible = m_clientArea->GetLineAt(origin.y);
4891 int last_visible = m_clientArea->GetLineAt(origin.y + GetClientSize().y);
4892
4893 first_visible = wxMax(first_visible, top_part_end);
4894 last_visible = wxMin(bottom_part_start, last_visible);
4895
4896 for ( row = first_visible; row < last_visible; row++ )
4897 {
4898 calculator.UpdateWithRow(row);
4899 }
4900
4901 wxLogTrace("dataview",
4902 "determined best size from %d top, %d bottom plus %d more visible items out of %d total",
4903 top_part_end,
4904 count - bottom_part_start,
4905 wxMax(0, last_visible - first_visible),
4906 count);
d0154e3a
VS
4907 }
4908
0645bd38 4909 int max_width = calculator.GetMaxWidth();
d0154e3a
VS
4910 if ( max_width > 0 )
4911 max_width += 2 * PADDING_RIGHTLEFT;
4912
840fc4d1 4913 const_cast<wxDataViewCtrl*>(this)->m_colsBestWidths[idx].width = max_width;
d0154e3a
VS
4914 return max_width;
4915}
4916
702f5349 4917void wxDataViewCtrl::ColumnMoved(wxDataViewColumn * WXUNUSED(col),
977a41ec 4918 unsigned int WXUNUSED(new_pos))
702f5349
VZ
4919{
4920 // do _not_ reorder m_cols elements here, they should always be in the
4921 // order in which columns were added, we only display the columns in
4922 // different order
fc8c1a15
RR
4923 m_clientArea->UpdateDisplay();
4924}
4925
afebb87b
RR
4926bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
4927{
b3a3c9d8 4928 wxDataViewColumnList::compatibility_iterator ret = m_cols.Find( column );
4264606e 4929 if (!ret)
afebb87b
RR
4930 return false;
4931
d0154e3a 4932 m_colsBestWidths.erase(m_colsBestWidths.begin() + GetColumnIndex(column));
b7fe2261 4933 m_cols.Erase(ret);
f52f3795
VS
4934
4935 if ( m_clientArea->GetCurrentColumn() == column )
4936 m_clientArea->ClearCurrentColumn();
4937
aef252d9 4938 OnColumnsCountChanged();
afebb87b
RR
4939
4940 return true;
4941}
4942
4943bool wxDataViewCtrl::ClearColumns()
4944{
42cbde51 4945 SetExpanderColumn(NULL);
b3a3c9d8 4946 m_cols.Clear();
d0154e3a 4947 m_colsBestWidths.clear();
f52f3795
VS
4948
4949 m_clientArea->ClearCurrentColumn();
4950
aef252d9 4951 OnColumnsCountChanged();
f52f3795 4952
afebb87b
RR
4953 return true;
4954}
4955
bed74e48 4956void wxDataViewCtrl::InvalidateColBestWidth(int idx)
d0154e3a 4957{
840fc4d1
VS
4958 m_colsBestWidths[idx].width = 0;
4959 m_colsBestWidths[idx].dirty = true;
bed74e48 4960 m_colsDirty = true;
d0154e3a
VS
4961}
4962
bed74e48 4963void wxDataViewCtrl::InvalidateColBestWidths()
d0154e3a 4964{
840fc4d1 4965 // mark all columns as dirty:
d0154e3a
VS
4966 m_colsBestWidths.clear();
4967 m_colsBestWidths.resize(m_cols.size());
bed74e48
VS
4968 m_colsDirty = true;
4969}
d0154e3a 4970
bed74e48
VS
4971void wxDataViewCtrl::UpdateColWidths()
4972{
840fc4d1
VS
4973 m_colsDirty = false;
4974
bed74e48
VS
4975 if ( !m_headerArea )
4976 return;
4977
76b92fa5
VS
4978 const unsigned len = m_colsBestWidths.size();
4979 for ( unsigned i = 0; i < len; i++ )
bed74e48 4980 {
840fc4d1
VS
4981 // Note that we have to have an explicit 'dirty' flag here instead of
4982 // checking if the width==0, as is done in GetBestColumnWidth().
4983 //
4984 // Testing width==0 wouldn't work correctly if some code called
4985 // GetWidth() after col. width invalidation but before
4986 // wxDataViewCtrl::UpdateColWidths() was called at idle time. This
4987 // would result in the header's column width getting out of sync with
4988 // the control itself.
4989 if ( m_colsBestWidths[i].dirty )
4990 {
76b92fa5 4991 m_headerArea->UpdateColumn(i);
840fc4d1
VS
4992 m_colsBestWidths[i].dirty = false;
4993 }
bed74e48
VS
4994 }
4995}
4996
4997void wxDataViewCtrl::OnInternalIdle()
4998{
4999 wxDataViewCtrlBase::OnInternalIdle();
5000
5001 if ( m_colsDirty )
bed74e48 5002 UpdateColWidths();
d0154e3a
VS
5003}
5004
453091c2
RR
5005int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
5006{
5d9e1605
RR
5007 unsigned int len = GetColumnCount();
5008 for ( unsigned int i = 0; i < len; i++ )
5009 {
5010 wxDataViewColumn * col = GetColumnAt(i);
5011 if (column==col)
5012 return i;
5013 }
ce00f59b 5014
5d9e1605 5015 return wxNOT_FOUND;
453091c2
RR
5016}
5017
21f47fb9
RR
5018wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
5019{
46234a03 5020 return m_sortingColumnIdx == wxNOT_FOUND ? NULL
977a41ec 5021 : GetColumn(m_sortingColumnIdx);
21f47fb9
RR
5022}
5023
80ce465c
VZ
5024wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const
5025{
5026 return GetItemByRow(m_clientArea->GetCurrentRow());
5027}
5028
5029void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item)
5030{
5031 const int row = m_clientArea->GetRowByItem(item);
5032
5033 const unsigned oldCurrent = m_clientArea->GetCurrentRow();
5034 if ( static_cast<unsigned>(row) != oldCurrent )
5035 {
5036 m_clientArea->ChangeCurrentRow(row);
5037 m_clientArea->RefreshRow(oldCurrent);
5038 m_clientArea->RefreshRow(row);
5039 }
5040}
5041
ee1377e1
VS
5042wxDataViewColumn *wxDataViewCtrl::GetCurrentColumn() const
5043{
5044 return m_clientArea->GetCurrentColumn();
5045}
5046
fa93d732 5047int wxDataViewCtrl::GetSelectedItemsCount() const
66e09788 5048{
fa93d732 5049 return m_clientArea->GetSelections().size();
66e09788
RR
5050}
5051
b7e9f8b1
RR
5052int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
5053{
5054 sel.Empty();
f23b8c0d 5055 const wxDataViewSelection& selections = m_clientArea->GetSelections();
373a4816 5056
f23b8c0d 5057 const size_t len = selections.size();
eceb6af1 5058 for ( size_t i = 0; i < len; i++ )
b7e9f8b1 5059 {
f23b8c0d 5060 wxDataViewItem item = m_clientArea->GetItemByRow(selections[i]);
373a4816
VS
5061 if ( item.IsOk() )
5062 {
5063 sel.Add(item);
5064 }
5065 else
5066 {
5067 wxFAIL_MSG( "invalid item in selection - bad internal state" );
5068 }
b7e9f8b1 5069 }
373a4816
VS
5070
5071 return sel.size();
b7e9f8b1
RR
5072}
5073
5074void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
5075{
59e60167 5076 wxDataViewSelection selection(wxDataViewSelectionCmp);
4219d8b0
RR
5077
5078 wxDataViewItem last_parent;
5079
b7e9f8b1
RR
5080 int len = sel.GetCount();
5081 for( int i = 0; i < len; i ++ )
5082 {
4219d8b0
RR
5083 wxDataViewItem item = sel[i];
5084 wxDataViewItem parent = GetModel()->GetParent( item );
5085 if (parent)
5086 {
5087 if (parent != last_parent)
5088 ExpandAncestors(item);
5089 }
9adeb77a 5090
4219d8b0
RR
5091 last_parent = parent;
5092 int row = m_clientArea->GetRowByItem( item );
b7e9f8b1
RR
5093 if( row >= 0 )
5094 selection.Add( static_cast<unsigned int>(row) );
5095 }
9adeb77a 5096
b7e9f8b1
RR
5097 m_clientArea->SetSelections( selection );
5098}
5099
5100void wxDataViewCtrl::Select( const wxDataViewItem & item )
704c3490 5101{
4219d8b0 5102 ExpandAncestors( item );
9adeb77a 5103
b7e9f8b1
RR
5104 int row = m_clientArea->GetRowByItem( item );
5105 if( row >= 0 )
57f2a652 5106 {
bc4f1ff2 5107 // Unselect all rows before select another in the single select mode
57f2a652
RR
5108 if (m_clientArea->IsSingleSel())
5109 m_clientArea->SelectAllRows(false);
ce00f59b 5110
b7e9f8b1 5111 m_clientArea->SelectRow(row, true);
ce00f59b 5112
de67922e
RR
5113 // Also set focus to the selected item
5114 m_clientArea->ChangeCurrentRow( row );
57f2a652 5115 }
704c3490 5116}
3b6280be 5117
b7e9f8b1 5118void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
6ff7eee7 5119{
b7e9f8b1
RR
5120 int row = m_clientArea->GetRowByItem( item );
5121 if( row >= 0 )
5122 m_clientArea->SelectRow(row, false);
6ff7eee7
RR
5123}
5124
b7e9f8b1 5125bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
6ff7eee7 5126{
b7e9f8b1
RR
5127 int row = m_clientArea->GetRowByItem( item );
5128 if( row >= 0 )
5129 {
5130 return m_clientArea->IsRowSelected(row);
5131 }
5132 return false;
6ff7eee7
RR
5133}
5134
4bdc891f
VZ
5135void wxDataViewCtrl::SetAlternateRowColour(const wxColour& colour)
5136{
5137 m_alternateRowColour = colour;
5138}
5139
b7e9f8b1 5140void wxDataViewCtrl::SelectAll()
6ff7eee7 5141{
b7e9f8b1
RR
5142 m_clientArea->SelectAllRows(true);
5143}
df387169 5144
b7e9f8b1
RR
5145void wxDataViewCtrl::UnselectAll()
5146{
5147 m_clientArea->SelectAllRows(false);
6ff7eee7 5148}
b7e9f8b1 5149
fbda518c 5150void wxDataViewCtrl::EnsureVisible( int row, int column )
b7e9f8b1 5151{
fbda518c
RR
5152 if( row < 0 )
5153 row = 0;
67be459b 5154 if( row > (int) m_clientArea->GetRowCount() )
fbda518c
RR
5155 row = m_clientArea->GetRowCount();
5156
5157 int first = m_clientArea->GetFirstVisibleRow();
5158 int last = m_clientArea->GetLastVisibleRow();
5159 if( row < first )
5160 m_clientArea->ScrollTo( row, column );
5161 else if( row > last )
5162 m_clientArea->ScrollTo( row - last + first, column );
5163 else
5164 m_clientArea->ScrollTo( first, column );
b7e9f8b1
RR
5165}
5166
fbda518c 5167void wxDataViewCtrl::EnsureVisible( const wxDataViewItem & item, const wxDataViewColumn * column )
b7e9f8b1 5168{
4219d8b0 5169 ExpandAncestors( item );
9adeb77a 5170
13499080
RR
5171 m_clientArea->RecalculateDisplay();
5172
b7e9f8b1
RR
5173 int row = m_clientArea->GetRowByItem(item);
5174 if( row >= 0 )
fbda518c
RR
5175 {
5176 if( column == NULL )
9bcc8016 5177 EnsureVisible(row, -1);
fbda518c 5178 else
702f5349 5179 EnsureVisible( row, GetColumnIndex(column) );
fbda518c 5180 }
b7fe2261 5181
b7e9f8b1
RR
5182}
5183
4cef3873 5184void wxDataViewCtrl::HitTest( const wxPoint & point, wxDataViewItem & item,
bc4f1ff2 5185 wxDataViewColumn* &column ) const
66e09788
RR
5186{
5187 m_clientArea->HitTest(point, item, column);
5188}
5189
4cef3873 5190wxRect wxDataViewCtrl::GetItemRect( const wxDataViewItem & item,
bc4f1ff2 5191 const wxDataViewColumn* column ) const
66e09788
RR
5192{
5193 return m_clientArea->GetItemRect(item, column);
5194}
5195
b7e9f8b1
RR
5196wxDataViewItem wxDataViewCtrl::GetItemByRow( unsigned int row ) const
5197{
5198 return m_clientArea->GetItemByRow( row );
5199}
5200
5201int wxDataViewCtrl::GetRowByItem( const wxDataViewItem & item ) const
5202{
5203 return m_clientArea->GetRowByItem( item );
5204}
5205
afebb87b
RR
5206void wxDataViewCtrl::Expand( const wxDataViewItem & item )
5207{
e3d358bb
RR
5208 ExpandAncestors( item );
5209
afebb87b
RR
5210 int row = m_clientArea->GetRowByItem( item );
5211 if (row != -1)
b6b17152 5212 {
afebb87b 5213 m_clientArea->Expand(row);
b6b17152
VS
5214 InvalidateColBestWidths();
5215 }
afebb87b
RR
5216}
5217
5218void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
5219{
5220 int row = m_clientArea->GetRowByItem( item );
5221 if (row != -1)
b6b17152 5222 {
b7fe2261 5223 m_clientArea->Collapse(row);
b6b17152
VS
5224 InvalidateColBestWidths();
5225 }
afebb87b
RR
5226}
5227
739a8399
RR
5228bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
5229{
5230 int row = m_clientArea->GetRowByItem( item );
5231 if (row != -1)
5232 return m_clientArea->IsExpanded(row);
5233 return false;
5234}
5235
907f09f4 5236void wxDataViewCtrl::EditItem(const wxDataViewItem& item, const wxDataViewColumn *column)
c937344c 5237{
907f09f4
VS
5238 wxCHECK_RET( item.IsOk(), "invalid item" );
5239 wxCHECK_RET( column, "no column provided" );
4cef3873 5240
907f09f4 5241 m_clientArea->StartEditing(item, column);
c937344c
RR
5242}
5243
4cef3873 5244#endif // !wxUSE_GENERICDATAVIEWCTRL
4ed7af08 5245
4cef3873 5246#endif // wxUSE_DATAVIEWCTRL