]> git.saurik.com Git - wxWidgets.git/blame - src/generic/treectlg.cpp
extend wxXLocale with wxStrto[d,l,ul] functions; make wxXLocale::Init() a little...
[wxWidgets.git] / src / generic / treectlg.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
ed4b0fdc 2// Name: src/generic/treectlg.cpp
f135ff73 3// Purpose: generic tree control implementation
c801d85f
KB
4// Author: Robert Roebling
5// Created: 01/02/97
f135ff73 6// Modified: 22/10/98 - almost total rewrite, simpler interface (VZ)
389cdc7a 7// Id: $Id$
6aa89a22 8// Copyright: (c) 1998 Robert Roebling and Julian Smart
65571936 9// Licence: wxWindows licence
c801d85f
KB
10/////////////////////////////////////////////////////////////////////////////
11
f135ff73
VZ
12// =============================================================================
13// declarations
14// =============================================================================
15
16// -----------------------------------------------------------------------------
17// headers
18// -----------------------------------------------------------------------------
19
1e6d9499
JS
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
1e6feb95 24 #pragma hdrstop
1e6d9499
JS
25#endif
26
1e6feb95
VZ
27#if wxUSE_TREECTRL
28
8cee4a30 29#include "wx/treectrl.h"
ed4b0fdc
WS
30
31#ifndef WX_PRECOMP
32 #include "wx/dcclient.h"
c0badb70 33 #include "wx/timer.h"
9eddec69 34 #include "wx/settings.h"
2a673eb1 35 #include "wx/listbox.h"
fec9cc08 36 #include "wx/textctrl.h"
ed4b0fdc
WS
37#endif
38
941830cb
JS
39#include "wx/generic/treectlg.h"
40#include "wx/imaglist.h"
c801d85f 41
9c7f49f5 42#include "wx/renderer.h"
44c44c82 43
f89d65ea 44#ifdef __WXMAC__
33ddeaba 45 #include "wx/osx/private.h"
f89d65ea 46#endif
ca65c044 47
f135ff73
VZ
48// -----------------------------------------------------------------------------
49// array types
50// -----------------------------------------------------------------------------
c801d85f 51
b5dbe15d 52class WXDLLIMPEXP_FWD_CORE wxGenericTreeItem;
1e6d9499 53
ec64d5ca 54WX_DEFINE_ARRAY_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems);
c801d85f 55
8dc99046
VZ
56// ----------------------------------------------------------------------------
57// constants
58// ----------------------------------------------------------------------------
59
60static const int NO_IMAGE = -1;
61
cb59313c 62static const int PIXELS_PER_UNIT = 10;
3dbeaa52 63
03966fcb
RR
64// the margin between the item state image and the item normal image
65static const int MARGIN_BETWEEN_STATE_AND_IMAGE = 2;
66
3e4f8ee2
VZ
67// the margin between the item image and the item text
68static const int MARGIN_BETWEEN_IMAGE_AND_TEXT = 4;
69
f135ff73
VZ
70// -----------------------------------------------------------------------------
71// private classes
72// -----------------------------------------------------------------------------
73
3dbeaa52
VZ
74// timer used for enabling in-place edit
75class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
76{
77public:
cb59313c
VZ
78 // start editing the current item after half a second (if the mouse hasn't
79 // been clicked/moved)
7a944d2f 80 enum { DELAY = 500 };
cb59313c 81
941830cb 82 wxTreeRenameTimer( wxGenericTreeCtrl *owner );
3dbeaa52 83
cb59313c 84 virtual void Notify();
3dbeaa52
VZ
85
86private:
cb59313c 87 wxGenericTreeCtrl *m_owner;
22f3361e 88
c0c133e1 89 wxDECLARE_NO_COPY_CLASS(wxTreeRenameTimer);
3dbeaa52
VZ
90};
91
92// control used for in-place edit
93class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl
94{
95public:
edb8f298 96 wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item);
3dbeaa52 97
a8a89154 98 void EndEdit( bool discardChanges );
6621957f 99
0cf3b542 100 const wxGenericTreeItem* item() const { return m_itemEdited; }
eb7f24c1 101
edb8f298 102protected:
3dbeaa52 103 void OnChar( wxKeyEvent &event );
c13cace1 104 void OnKeyUp( wxKeyEvent &event );
3dbeaa52
VZ
105 void OnKillFocus( wxFocusEvent &event );
106
edb8f298 107 bool AcceptChanges();
a8a89154 108 void Finish( bool setfocus );
edb8f298 109
3dbeaa52 110private:
618a5e38 111 wxGenericTreeCtrl *m_owner;
edb8f298 112 wxGenericTreeItem *m_itemEdited;
3dbeaa52 113 wxString m_startValue;
33fe475a 114 bool m_aboutToFinish;
3dbeaa52
VZ
115
116 DECLARE_EVENT_TABLE()
c0c133e1 117 wxDECLARE_NO_COPY_CLASS(wxTreeTextCtrl);
3dbeaa52
VZ
118};
119
cb59313c
VZ
120// timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
121// for a sufficiently long time
122class WXDLLEXPORT wxTreeFindTimer : public wxTimer
123{
124public:
125 // reset the current prefix after half a second of inactivity
7a944d2f 126 enum { DELAY = 500 };
cb59313c
VZ
127
128 wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; }
129
130 virtual void Notify() { m_owner->m_findPrefix.clear(); }
131
132private:
133 wxGenericTreeCtrl *m_owner;
22f3361e 134
c0c133e1 135 wxDECLARE_NO_COPY_CLASS(wxTreeFindTimer);
cb59313c
VZ
136};
137
f135ff73
VZ
138// a tree item
139class WXDLLEXPORT wxGenericTreeItem
c801d85f 140{
f135ff73 141public:
9ec64fa7 142 // ctors & dtor
cac09ce8
VZ
143 wxGenericTreeItem()
144 {
145 m_data = NULL;
146 m_widthText =
147 m_heightText = -1;
148 }
149
9ec64fa7 150 wxGenericTreeItem( wxGenericTreeItem *parent,
618a5e38 151 const wxString& text,
618a5e38
RR
152 int image,
153 int selImage,
154 wxTreeItemData *data );
f135ff73 155
9ec64fa7 156 ~wxGenericTreeItem();
f135ff73 157
9ec64fa7
VZ
158 // trivial accessors
159 wxArrayGenericTreeItems& GetChildren() { return m_children; }
f135ff73 160
9ec64fa7
VZ
161 const wxString& GetText() const { return m_text; }
162 int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const
3dbeaa52 163 { return m_images[which]; }
9ec64fa7 164 wxTreeItemData *GetData() const { return m_data; }
03966fcb 165 int GetState() const { return m_state; }
f135ff73 166
9ec64fa7
VZ
167 // returns the current image for the item (depending on its
168 // selected/expanded/whatever state)
169 int GetCurrentImage() const;
8dc99046 170
cac09ce8
VZ
171 void SetText(const wxString& text)
172 {
173 m_text = text;
174
175 ResetTextSize();
176 }
177
178 void SetImage(int image, wxTreeItemIcon which)
179 {
180 m_images[which] = image;
181 m_width = 0;
182 }
183
9ec64fa7 184 void SetData(wxTreeItemData *data) { m_data = data; }
cac09ce8 185 void SetState(int state) { m_state = state; m_width = 0; }
f135ff73 186
ca65c044 187 void SetHasPlus(bool has = true) { m_hasPlus = has; }
f135ff73 188
cac09ce8
VZ
189 void SetBold(bool bold)
190 {
191 m_isBold = bold;
192
193 ResetTextSize();
194 }
ef44a621 195
9ec64fa7
VZ
196 int GetX() const { return m_x; }
197 int GetY() const { return m_y; }
f135ff73 198
9ec64fa7
VZ
199 void SetX(int x) { m_x = x; }
200 void SetY(int y) { m_y = y; }
f135ff73 201
cac09ce8
VZ
202 int GetHeight() const { return m_height; }
203 int GetWidth() const { return m_width; }
204
205 int GetTextHeight() const
206 {
207 wxASSERT_MSG( m_heightText != -1, "must call CalculateSize() first" );
208
209 return m_heightText;
210 }
211
212 int GetTextWidth() const
213 {
214 wxASSERT_MSG( m_widthText != -1, "must call CalculateSize() first" );
91b8de8d 215
cac09ce8
VZ
216 return m_widthText;
217 }
91b8de8d 218
9ec64fa7 219 wxGenericTreeItem *GetParent() const { return m_parent; }
c801d85f 220
cac09ce8
VZ
221 // sets the items font for the specified DC if it uses any special font or
222 // simply returns false otherwise
223 bool SetFont(wxGenericTreeCtrl *control, wxDC& dc) const
224 {
225 wxFont font;
226
227 wxTreeItemAttr * const attr = GetAttributes();
228 if ( attr && attr->HasFont() )
229 font = attr->GetFont();
230 else if ( IsBold() )
231 font = control->m_boldFont;
232 else
233 return false;
234
235 dc.SetFont(font);
236 return true;
237 }
238
9ec64fa7 239 // operations
66619ee5
VZ
240
241 // deletes all children notifying the treectrl about it
242 void DeleteChildren(wxGenericTreeCtrl *tree);
f135ff73 243
9ec64fa7 244 // get count of all children (and grand children if 'recursively')
ca65c044 245 size_t GetChildrenCount(bool recursively = true) const;
f135ff73 246
9ec64fa7 247 void Insert(wxGenericTreeItem *child, size_t index)
8239070b 248 { m_children.Insert(child, index); }
f135ff73 249
cac09ce8
VZ
250 // calculate and cache the item size using either the provided DC (which is
251 // supposed to have wxGenericTreeCtrl::m_normalFont selected into it!) or a
252 // wxClientDC on the control window
253 void CalculateSize(wxGenericTreeCtrl *control, wxDC& dc)
254 { DoCalculateSize(control, dc, true /* dc uses normal font */); }
255 void CalculateSize(wxGenericTreeCtrl *control);
256
941830cb 257 void GetSize( int &x, int &y, const wxGenericTreeCtrl* );
f135ff73 258
cac09ce8
VZ
259 void ResetSize() { m_width = 0; }
260 void ResetTextSize() { m_width = 0; m_widthText = -1; }
261 void RecursiveResetSize();
262 void RecursiveResetTextSize();
263
9ec64fa7 264 // return the item at given position (or NULL if no item), onButton is
ca65c044 265 // true if the point belongs to the item's button, otherwise it lies
f8b043e7 266 // on the item's label
618a5e38
RR
267 wxGenericTreeItem *HitTest( const wxPoint& point,
268 const wxGenericTreeCtrl *,
269 int &flags,
270 int level );
f135ff73 271
ca65c044
WS
272 void Expand() { m_isCollapsed = false; }
273 void Collapse() { m_isCollapsed = true; }
f135ff73 274
ca65c044 275 void SetHilight( bool set = true ) { m_hasHilight = set; }
f135ff73 276
9ec64fa7
VZ
277 // status inquiries
278 bool HasChildren() const { return !m_children.IsEmpty(); }
ef8698d6 279 bool IsSelected() const { return m_hasHilight != 0; }
9ec64fa7
VZ
280 bool IsExpanded() const { return !m_isCollapsed; }
281 bool HasPlus() const { return m_hasPlus || HasChildren(); }
ef8698d6 282 bool IsBold() const { return m_isBold != 0; }
9ec64fa7
VZ
283
284 // attributes
285 // get them - may be NULL
286 wxTreeItemAttr *GetAttributes() const { return m_attr; }
287 // get them ensuring that the pointer is not NULL
288 wxTreeItemAttr& Attr()
289 {
290 if ( !m_attr )
618a5e38 291 {
9ec64fa7 292 m_attr = new wxTreeItemAttr;
ca65c044 293 m_ownsAttr = true;
618a5e38 294 }
9ec64fa7
VZ
295 return *m_attr;
296 }
618a5e38
RR
297 // set them
298 void SetAttributes(wxTreeItemAttr *attr)
299 {
300 if ( m_ownsAttr ) delete m_attr;
301 m_attr = attr;
ca65c044 302 m_ownsAttr = false;
cac09ce8
VZ
303 m_width = 0;
304 m_widthText = -1;
618a5e38
RR
305 }
306 // set them and delete when done
307 void AssignAttributes(wxTreeItemAttr *attr)
308 {
309 SetAttributes(attr);
ca65c044 310 m_ownsAttr = true;
cac09ce8
VZ
311 m_width = 0;
312 m_widthText = -1;
618a5e38 313 }
f135ff73
VZ
314
315private:
cac09ce8
VZ
316 // calculate the size of this item, i.e. set m_width, m_height and
317 // m_widthText and m_heightText properly
318 //
319 // if dcUsesNormalFont is true, the current dc font must be the normal tree
320 // control font
321 void DoCalculateSize(wxGenericTreeCtrl *control,
322 wxDC& dc,
323 bool dcUsesNormalFont);
324
618a5e38
RR
325 // since there can be very many of these, we save size by chosing
326 // the smallest representation for the elements and by ordering
327 // the members to avoid padding.
328 wxString m_text; // label to be rendered for item
cac09ce8
VZ
329 int m_widthText;
330 int m_heightText;
618a5e38
RR
331
332 wxTreeItemData *m_data; // user-provided data
333
03966fcb
RR
334 int m_state; // item state
335
618a5e38
RR
336 wxArrayGenericTreeItems m_children; // list of children
337 wxGenericTreeItem *m_parent; // parent of this item
338
339 wxTreeItemAttr *m_attr; // attributes???
9ec64fa7
VZ
340
341 // tree ctrl images for the normal, selected, expanded and
342 // expanded+selected states
8253f2e0 343 int m_images[wxTreeItemIcon_Max];
9ec64fa7 344
618a5e38 345 wxCoord m_x; // (virtual) offset from top
e549bec4 346 wxCoord m_y; // (virtual) offset from left
8253f2e0
WS
347 int m_width; // width of this item
348 int m_height; // height of this item
9ec64fa7
VZ
349
350 // use bitfields to save size
9bb50fd0
VZ
351 unsigned int m_isCollapsed :1;
352 unsigned int m_hasHilight :1; // same as focused
353 unsigned int m_hasPlus :1; // used for item which doesn't have
9ec64fa7 354 // children but has a [+] button
9bb50fd0
VZ
355 unsigned int m_isBold :1; // render the label in bold font
356 unsigned int m_ownsAttr :1; // delete attribute when done
22f3361e 357
c0c133e1 358 wxDECLARE_NO_COPY_CLASS(wxGenericTreeItem);
f135ff73
VZ
359};
360
361// =============================================================================
362// implementation
363// =============================================================================
364
06b466c7
VZ
365// ----------------------------------------------------------------------------
366// private functions
367// ----------------------------------------------------------------------------
368
369// translate the key or mouse event flags to the type of selection we're
370// dealing with
371static void EventFlagsToSelType(long style,
372 bool shiftDown,
373 bool ctrlDown,
dfc6cd93
SB
374 bool &is_multiple,
375 bool &extended_select,
376 bool &unselect_others)
06b466c7 377{
dfc6cd93
SB
378 is_multiple = (style & wxTR_MULTIPLE) != 0;
379 extended_select = shiftDown && is_multiple;
380 unselect_others = !(extended_select || (ctrlDown && is_multiple));
06b466c7 381}
e179bd65 382
7475e8a3 383// check if the given item is under another one
8239070b
VZ
384static bool
385IsDescendantOf(const wxGenericTreeItem *parent, const wxGenericTreeItem *item)
7475e8a3
VZ
386{
387 while ( item )
388 {
389 if ( item == parent )
390 {
391 // item is a descendant of parent
ca65c044 392 return true;
7475e8a3
VZ
393 }
394
395 item = item->GetParent();
396 }
397
ca65c044 398 return false;
7475e8a3
VZ
399}
400
e179bd65
RR
401// -----------------------------------------------------------------------------
402// wxTreeRenameTimer (internal)
403// -----------------------------------------------------------------------------
404
941830cb 405wxTreeRenameTimer::wxTreeRenameTimer( wxGenericTreeCtrl *owner )
e179bd65
RR
406{
407 m_owner = owner;
408}
409
410void wxTreeRenameTimer::Notify()
411{
412 m_owner->OnRenameTimer();
413}
414
415//-----------------------------------------------------------------------------
416// wxTreeTextCtrl (internal)
417//-----------------------------------------------------------------------------
418
e179bd65
RR
419BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl)
420 EVT_CHAR (wxTreeTextCtrl::OnChar)
c13cace1 421 EVT_KEY_UP (wxTreeTextCtrl::OnKeyUp)
e179bd65
RR
422 EVT_KILL_FOCUS (wxTreeTextCtrl::OnKillFocus)
423END_EVENT_TABLE()
424
edb8f298
VZ
425wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner,
426 wxGenericTreeItem *item)
427 : m_itemEdited(item), m_startValue(item->GetText())
428{
e179bd65 429 m_owner = owner;
33fe475a 430 m_aboutToFinish = false;
e179bd65 431
ce43f9e1
VZ
432 wxRect rect;
433 m_owner->GetBoundingRect(m_itemEdited, rect, true);
434
ec676e4f 435 // corrects position and size for better appearance
ce43f9e1
VZ
436#ifdef __WXMSW__
437 rect.x -= 5;
438 rect.width += 10;
ec676e4f 439#elif defined(__WXGTK__)
ce43f9e1
VZ
440 rect.x -= 5;
441 rect.y -= 2;
442 rect.width += 8;
443 rect.height += 4;
ec676e4f
VZ
444#elif defined(__WXMAC__)
445 int bestHeight = GetBestSize().y - 8;
446 if ( rect.height > bestHeight )
447 {
448 int diff = rect.height - bestHeight;
ce43f9e1
VZ
449 rect.height -= diff;
450 rect.y += diff / 2;
ec676e4f
VZ
451 }
452#endif // platforms
33ac7e6f 453
edb8f298 454 (void)Create(m_owner, wxID_ANY, m_startValue,
ce43f9e1
VZ
455 rect.GetPosition(), rect.GetSize());
456
457 SetSelection(-1, -1);
edb8f298 458}
574c939e 459
d0e2c75c
RR
460void wxTreeTextCtrl::EndEdit(bool discardChanges)
461{
462 m_aboutToFinish = true;
6621957f 463
d0e2c75c
RR
464 if ( discardChanges )
465 {
466 m_owner->OnRenameCancelled(m_itemEdited);
6621957f 467
a8a89154 468 Finish( true );
d0e2c75c
RR
469 }
470 else
471 {
472 // Notify the owner about the changes
473 AcceptChanges();
474
475 // Even if vetoed, close the control (consistent with MSW)
a8a89154 476 Finish( true );
d0e2c75c
RR
477 }
478}
479
edb8f298
VZ
480bool wxTreeTextCtrl::AcceptChanges()
481{
482 const wxString value = GetValue();
618a5e38 483
edb8f298
VZ
484 if ( value == m_startValue )
485 {
486 // nothing changed, always accept
c145a47c
RD
487 // when an item remains unchanged, the owner
488 // needs to be notified that the user decided
489 // not to change the tree item label, and that
490 // the edit has been cancelled
491
492 m_owner->OnRenameCancelled(m_itemEdited);
ca65c044 493 return true;
e179bd65 494 }
edb8f298
VZ
495
496 if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
e179bd65 497 {
edb8f298 498 // vetoed by the user
ca65c044 499 return false;
edb8f298
VZ
500 }
501
502 // accepted, do rename the item
503 m_owner->SetItemText(m_itemEdited, value);
33ac7e6f 504
ca65c044 505 return true;
edb8f298
VZ
506}
507
d0e2c75c 508void wxTreeTextCtrl::Finish( bool setfocus )
edb8f298 509{
d0e2c75c 510 m_owner->ResetTextControl();
33ac7e6f 511
d0e2c75c 512 wxPendingDelete.Append(this);
9548f380 513
d0e2c75c 514 if (setfocus)
8cee4a30 515 m_owner->SetFocus();
edb8f298 516}
dd5a32cc 517
edb8f298
VZ
518void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
519{
520 switch ( event.m_keyCode )
521 {
522 case WXK_RETURN:
d0e2c75c 523 EndEdit( false );
2b4f324a 524 break;
edb8f298
VZ
525
526 case WXK_ESCAPE:
d0e2c75c 527 EndEdit( true );
edb8f298
VZ
528 break;
529
530 default:
531 event.Skip();
e179bd65 532 }
c13cace1
VS
533}
534
535void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event )
536{
d0e2c75c 537 if ( !m_aboutToFinish )
dd5a32cc 538 {
edb8f298
VZ
539 // auto-grow the textctrl:
540 wxSize parentSize = m_owner->GetSize();
541 wxPoint myPos = GetPosition();
542 wxSize mySize = GetSize();
543 int sx, sy;
544 GetTextExtent(GetValue() + _T("M"), &sx, &sy);
545 if (myPos.x + sx > parentSize.x)
546 sx = parentSize.x - myPos.x;
547 if (mySize.x > sx)
548 sx = mySize.x;
422d0ff0 549 SetSize(sx, wxDefaultCoord);
dd5a32cc
RR
550 }
551
c13cace1 552 event.Skip();
e179bd65
RR
553}
554
dd5a32cc 555void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event )
e179bd65 556{
d0e2c75c 557 if ( !m_aboutToFinish )
edb8f298 558 {
33fe475a
RR
559 if ( !AcceptChanges() )
560 m_owner->OnRenameCancelled( m_itemEdited );
6621957f 561
d0e2c75c 562 Finish( false );
edb8f298 563 }
9146872c 564
d0e2c75c 565 // We should let the native text control handle focus, too.
9146872c 566 event.Skip();
e179bd65
RR
567}
568
f135ff73 569// -----------------------------------------------------------------------------
c801d85f 570// wxGenericTreeItem
f135ff73 571// -----------------------------------------------------------------------------
c801d85f 572
f135ff73
VZ
573wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent,
574 const wxString& text,
f135ff73
VZ
575 int image, int selImage,
576 wxTreeItemData *data)
577 : m_text(text)
c801d85f 578{
00e12320
RR
579 m_images[wxTreeItemIcon_Normal] = image;
580 m_images[wxTreeItemIcon_Selected] = selImage;
581 m_images[wxTreeItemIcon_Expanded] = NO_IMAGE;
582 m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE;
8dc99046 583
00e12320 584 m_data = data;
03966fcb 585 m_state = wxTREE_ITEMSTATE_NONE;
00e12320 586 m_x = m_y = 0;
f135ff73 587
ca65c044
WS
588 m_isCollapsed = true;
589 m_hasHilight = false;
590 m_hasPlus = false;
591 m_isBold = false;
c801d85f 592
00e12320 593 m_parent = parent;
f135ff73 594
d3b9f782 595 m_attr = NULL;
ca65c044 596 m_ownsAttr = false;
06b466c7 597
00e12320
RR
598 // We don't know the height here yet.
599 m_width = 0;
600 m_height = 0;
cac09ce8
VZ
601
602 m_widthText = -1;
603 m_heightText = -1;
edaa81ae 604}
c801d85f 605
f135ff73 606wxGenericTreeItem::~wxGenericTreeItem()
c801d85f 607{
00e12320 608 delete m_data;
4832f7c0 609
618a5e38 610 if (m_ownsAttr) delete m_attr;
9ec64fa7 611
00e12320 612 wxASSERT_MSG( m_children.IsEmpty(),
8239070b 613 "must call DeleteChildren() before deleting the item" );
372edb9d
VZ
614}
615
941830cb 616void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree)
372edb9d 617{
b4a980f4 618 size_t count = m_children.GetCount();
00e12320 619 for ( size_t n = 0; n < count; n++ )
a43a4f9d 620 {
00e12320 621 wxGenericTreeItem *child = m_children[n];
66619ee5 622 tree->SendDeleteEvent(child);
a43a4f9d 623
00e12320 624 child->DeleteChildren(tree);
66619ee5 625 if ( child == tree->m_select_me )
5117f9bf 626 tree->m_select_me = NULL;
00e12320
RR
627 delete child;
628 }
372edb9d 629
00e12320 630 m_children.Empty();
edaa81ae 631}
c801d85f 632
4832f7c0 633size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const
c801d85f 634{
b4a980f4 635 size_t count = m_children.GetCount();
00e12320
RR
636 if ( !recursively )
637 return count;
4832f7c0 638
00e12320 639 size_t total = count;
f2593d0d 640 for (size_t n = 0; n < count; ++n)
00e12320
RR
641 {
642 total += m_children[n]->GetChildrenCount();
643 }
c801d85f 644
00e12320 645 return total;
edaa81ae 646}
c801d85f 647
618a5e38
RR
648void wxGenericTreeItem::GetSize( int &x, int &y,
649 const wxGenericTreeCtrl *theButton )
c801d85f 650{
618a5e38 651 int bottomY=m_y+theButton->GetLineHeight(this);
00e12320
RR
652 if ( y < bottomY ) y = bottomY;
653 int width = m_x + m_width;
654 if ( x < width ) x = width;
f135ff73 655
00e12320 656 if (IsExpanded())
4832f7c0 657 {
b4a980f4 658 size_t count = m_children.GetCount();
00e12320
RR
659 for ( size_t n = 0; n < count; ++n )
660 {
618a5e38 661 m_children[n]->GetSize( x, y, theButton );
00e12320 662 }
df875e59 663 }
edaa81ae 664}
c801d85f 665
618a5e38
RR
666wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point,
667 const wxGenericTreeCtrl *theCtrl,
668 int &flags,
669 int level)
c801d85f 670{
618a5e38
RR
671 // for a hidden root node, don't evaluate it, but do evaluate children
672 if ( !(level == 0 && theCtrl->HasFlag(wxTR_HIDE_ROOT)) )
c801d85f 673 {
618a5e38
RR
674 // evaluate the item
675 int h = theCtrl->GetLineHeight(this);
676 if ((point.y > m_y) && (point.y < m_y + h))
f2593d0d 677 {
618a5e38
RR
678 int y_mid = m_y + h/2;
679 if (point.y < y_mid )
680 flags |= wxTREE_HITTEST_ONITEMUPPERPART;
681 else
682 flags |= wxTREE_HITTEST_ONITEMLOWERPART;
d3a9f4af 683
618a5e38 684 int xCross = m_x - theCtrl->GetSpacing();
a6b0ca75
SC
685#ifdef __WXMAC__
686 // according to the drawing code the triangels are drawn
687 // at -4 , -4 from the position up to +10/+10 max
688 if ((point.x > xCross-4) && (point.x < xCross+10) &&
689 (point.y > y_mid-4) && (point.y < y_mid+10) &&
690 HasPlus() && theCtrl->HasButtons() )
691#else
692 // 5 is the size of the plus sign
f8b043e7
RR
693 if ((point.x > xCross-6) && (point.x < xCross+6) &&
694 (point.y > y_mid-6) && (point.y < y_mid+6) &&
618a5e38 695 HasPlus() && theCtrl->HasButtons() )
a6b0ca75 696#endif
618a5e38
RR
697 {
698 flags |= wxTREE_HITTEST_ONITEMBUTTON;
699 return this;
700 }
0ae7f2a2 701
618a5e38
RR
702 if ((point.x >= m_x) && (point.x <= m_x+m_width))
703 {
704 int image_w = -1;
705 int image_h;
91b8de8d 706
618a5e38
RR
707 // assuming every image (normal and selected) has the same size!
708 if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal )
8239070b
VZ
709 {
710 theCtrl->m_imageListNormal->GetSize(GetImage(),
711 image_w, image_h);
712 }
03966fcb
RR
713
714 int state_w = -1;
715 int state_h;
618a5e38 716
8239070b
VZ
717 if ( (GetState() != wxTREE_ITEMSTATE_NONE) &&
718 theCtrl->m_imageListState )
719 {
720 theCtrl->m_imageListState->GetSize(GetState(),
721 state_w, state_h);
722 }
03966fcb
RR
723
724 if ((state_w != -1) && (point.x <= m_x + state_w + 1))
725 flags |= wxTREE_HITTEST_ONITEMSTATEICON;
726 else if ((image_w != -1) &&
727 (point.x <= m_x +
8239070b
VZ
728 (state_w != -1 ? state_w +
729 MARGIN_BETWEEN_STATE_AND_IMAGE
730 : 0)
03966fcb 731 + image_w + 1))
618a5e38
RR
732 flags |= wxTREE_HITTEST_ONITEMICON;
733 else
734 flags |= wxTREE_HITTEST_ONITEMLABEL;
735
736 return this;
737 }
738
739 if (point.x < m_x)
740 flags |= wxTREE_HITTEST_ONITEMINDENT;
741 if (point.x > m_x+m_width)
742 flags |= wxTREE_HITTEST_ONITEMRIGHT;
91b8de8d 743
f2593d0d
RR
744 return this;
745 }
91b8de8d 746
618a5e38 747 // if children are expanded, fall through to evaluate them
d3b9f782 748 if (m_isCollapsed) return NULL;
f2593d0d 749 }
618a5e38
RR
750
751 // evaluate children
b4a980f4 752 size_t count = m_children.GetCount();
618a5e38 753 for ( size_t n = 0; n < count; n++ )
c801d85f 754 {
618a5e38
RR
755 wxGenericTreeItem *res = m_children[n]->HitTest( point,
756 theCtrl,
757 flags,
758 level + 1 );
759 if ( res != NULL )
760 return res;
edaa81ae 761 }
f135ff73 762
d3b9f782 763 return NULL;
edaa81ae 764}
c801d85f 765
8dc99046
VZ
766int wxGenericTreeItem::GetCurrentImage() const
767{
768 int image = NO_IMAGE;
769 if ( IsExpanded() )
770 {
771 if ( IsSelected() )
772 {
773 image = GetImage(wxTreeItemIcon_SelectedExpanded);
774 }
775
776 if ( image == NO_IMAGE )
777 {
778 // we usually fall back to the normal item, but try just the
779 // expanded one (and not selected) first in this case
780 image = GetImage(wxTreeItemIcon_Expanded);
781 }
782 }
783 else // not expanded
784 {
785 if ( IsSelected() )
786 image = GetImage(wxTreeItemIcon_Selected);
787 }
788
618a5e38
RR
789 // maybe it doesn't have the specific image we want,
790 // try the default one instead
791 if ( image == NO_IMAGE ) image = GetImage();
8dc99046
VZ
792
793 return image;
794}
795
cac09ce8
VZ
796void wxGenericTreeItem::CalculateSize(wxGenericTreeCtrl* control)
797{
798 // check if we need to do anything before creating the DC
799 if ( m_width != 0 )
800 return;
801
802 wxClientDC dc(control);
803 DoCalculateSize(control, dc, false /* normal font not used */);
804}
805
806void
807wxGenericTreeItem::DoCalculateSize(wxGenericTreeCtrl* control,
808 wxDC& dc,
809 bool dcUsesNormalFont)
810{
811 if ( m_width != 0 ) // Size known, nothing to do
812 return;
813
814 if ( m_widthText == -1 )
815 {
816 bool fontChanged;
817 if ( SetFont(control, dc) )
818 {
819 fontChanged = true;
820 }
821 else // we have no special font
822 {
823 if ( !dcUsesNormalFont )
824 {
825 // but we do need to ensure that the normal font is used: notice
826 // that this doesn't count as changing the font as we don't need
827 // to restore it
828 dc.SetFont(control->m_normalFont);
829 }
830
831 fontChanged = false;
832 }
833
834 dc.GetTextExtent( GetText(), &m_widthText, &m_heightText );
835
836 // restore normal font if the DC used it previously and we changed it
837 if ( fontChanged )
838 dc.SetFont(control->m_normalFont);
839 }
840
841 int text_h = m_heightText + 2;
842
136b7e61 843 int image_h = 0, image_w = 0;
cac09ce8 844 int image = GetCurrentImage();
136b7e61 845 if ( image != NO_IMAGE && control->m_imageListNormal )
cac09ce8 846 {
136b7e61
VZ
847 control->m_imageListNormal->GetSize(image, image_w, image_h);
848 image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
cac09ce8
VZ
849 }
850
851 int state_h = 0, state_w = 0;
852 int state = GetState();
136b7e61 853 if ( state != wxTREE_ITEMSTATE_NONE && control->m_imageListState )
cac09ce8 854 {
136b7e61
VZ
855 control->m_imageListState->GetSize(state, state_w, state_h);
856 if ( image_w != 0 )
857 state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
858 else
859 state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
cac09ce8
VZ
860 }
861
136b7e61
VZ
862 int img_h = wxMax(state_h, image_h);
863 m_height = wxMax(img_h, text_h);
cac09ce8
VZ
864
865 if (m_height < 30)
866 m_height += 2; // at least 2 pixels
867 else
868 m_height += m_height / 10; // otherwise 10% extra spacing
869
870 if (m_height > control->m_lineHeight)
871 control->m_lineHeight = m_height;
872
873 m_width = state_w + image_w + m_widthText + 2;
874}
875
876void wxGenericTreeItem::RecursiveResetSize()
877{
878 m_width = 0;
879
880 const size_t count = m_children.Count();
881 for (size_t i = 0; i < count; i++ )
882 m_children[i]->RecursiveResetSize();
883}
884
885void wxGenericTreeItem::RecursiveResetTextSize()
886{
887 m_width = 0;
888 m_widthText = -1;
889
890 const size_t count = m_children.Count();
891 for (size_t i = 0; i < count; i++ )
892 m_children[i]->RecursiveResetTextSize();
893}
894
f135ff73 895// -----------------------------------------------------------------------------
941830cb 896// wxGenericTreeCtrl implementation
f135ff73
VZ
897// -----------------------------------------------------------------------------
898
8cee4a30 899IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeCtrl, wxControl)
f135ff73 900
8cee4a30 901BEGIN_EVENT_TABLE(wxGenericTreeCtrl, wxTreeCtrlBase)
941830cb 902 EVT_PAINT (wxGenericTreeCtrl::OnPaint)
ccdbdc89 903 EVT_SIZE (wxGenericTreeCtrl::OnSize)
941830cb
JS
904 EVT_MOUSE_EVENTS (wxGenericTreeCtrl::OnMouse)
905 EVT_CHAR (wxGenericTreeCtrl::OnChar)
906 EVT_SET_FOCUS (wxGenericTreeCtrl::OnSetFocus)
907 EVT_KILL_FOCUS (wxGenericTreeCtrl::OnKillFocus)
ca65c044 908 EVT_TREE_ITEM_GETTOOLTIP(wxID_ANY, wxGenericTreeCtrl::OnGetToolTip)
f135ff73
VZ
909END_EVENT_TABLE()
910
3a5bcc4d 911#if !defined(__WXMSW__) || defined(__WXUNIVERSAL__)
233058c7
JS
912/*
913 * wxTreeCtrl has to be a real class or we have problems with
914 * the run-time information.
915 */
916
917IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl)
918#endif
919
f135ff73
VZ
920// -----------------------------------------------------------------------------
921// construction/destruction
922// -----------------------------------------------------------------------------
91b8de8d 923
941830cb 924void wxGenericTreeCtrl::Init()
c801d85f 925{
8cee4a30
VZ
926 m_current =
927 m_key_current =
928 m_anchor =
d3b9f782 929 m_select_me = NULL;
ca65c044
WS
930 m_hasFocus = false;
931 m_dirty = false;
f135ff73 932
00e12320
RR
933 m_lineHeight = 10;
934 m_indent = 15;
935 m_spacing = 18;
f135ff73 936
b771aa29
VZ
937 m_hilightBrush = new wxBrush
938 (
a756f210 939 wxSystemSettings::GetColour
b771aa29
VZ
940 (
941 wxSYS_COLOUR_HIGHLIGHT
942 ),
04ee05f9 943 wxBRUSHSTYLE_SOLID
b771aa29
VZ
944 );
945
946 m_hilightUnfocusedBrush = new wxBrush
947 (
a756f210 948 wxSystemSettings::GetColour
b771aa29
VZ
949 (
950 wxSYS_COLOUR_BTNSHADOW
951 ),
04ee05f9 952 wxBRUSHSTYLE_SOLID
b771aa29 953 );
a4609ab8 954
8cee4a30
VZ
955 m_imageListButtons = NULL;
956 m_ownsImageListButtons = false;
978f38c2 957
00e12320 958 m_dragCount = 0;
ca65c044 959 m_isDragging = false;
f8b043e7
RR
960 m_dropTarget = m_oldSelection = NULL;
961 m_underMouse = NULL;
fbb12260 962 m_textCtrl = NULL;
9dfbf520 963
cb59313c 964 m_renameTimer = NULL;
e1983ab5 965
cb59313c
VZ
966 m_findTimer = NULL;
967
e3d64157 968 m_dropEffectAboveItem = false;
ec676e4f 969
c0d2308b
RR
970 m_dndEffect = NoEffect;
971 m_dndEffectItem = NULL;
e3d64157 972
ca65c044 973 m_lastOnSame = false;
f38374d0 974
cac09ce8 975#if defined( __WXMAC__ )
7ac5e1c9 976#if wxOSX_USE_ATSU_TEXT
e51bc1ec 977 m_normalFont.MacCreateFromThemeFont( kThemeViewsFont ) ;
530a427a
SC
978#else
979 m_normalFont.MacCreateFromUIFont( kCTFontViewsFontType ) ;
980#endif
f89d65ea 981#else
a756f210 982 m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
f89d65ea 983#endif
2b5f62a0
VZ
984 m_boldFont = wxFont(m_normalFont.GetPointSize(),
985 m_normalFont.GetFamily(),
986 m_normalFont.GetStyle(),
987 wxBOLD,
988 m_normalFont.GetUnderlined(),
989 m_normalFont.GetFaceName(),
990 m_normalFont.GetEncoding());
edaa81ae 991}
c801d85f 992
618a5e38
RR
993bool wxGenericTreeCtrl::Create(wxWindow *parent,
994 wxWindowID id,
995 const wxPoint& pos,
996 const wxSize& size,
997 long style,
8cee4a30 998 const wxValidator& validator,
618a5e38 999 const wxString& name )
c801d85f 1000{
2593f033 1001#ifdef __WXMAC__
6621957f
VZ
1002 int major, minor;
1003 wxGetOsVersion(&major, &minor);
574c939e 1004
2593f033
RR
1005 if (major < 10)
1006 style |= wxTR_ROW_LINES;
ec2f1752
RR
1007
1008 if (style & wxTR_HAS_BUTTONS)
1009 style |= wxTR_NO_LINES;
9c7f49f5 1010#endif // __WXMAC__
17d5bdf9 1011
ec2f1752
RR
1012#ifdef __WXGTK20__
1013 if (style & wxTR_HAS_BUTTONS)
1014 style |= wxTR_NO_LINES;
1015#endif
1016
8cee4a30
VZ
1017 if ( !wxControl::Create( parent, id, pos, size,
1018 style|wxHSCROLL|wxVSCROLL,
1019 validator,
1020 name ) )
1021 return false;
618a5e38 1022
6f0dd6b2
VZ
1023 // If the tree display has no buttons, but does have
1024 // connecting lines, we can use a narrower layout.
1025 // It may not be a good idea to force this...
618a5e38
RR
1026 if (!HasButtons() && !HasFlag(wxTR_NO_LINES))
1027 {
1028 m_indent= 10;
1029 m_spacing = 10;
1030 }
978f38c2 1031
ca65c044 1032 wxVisualAttributes attr = GetDefaultAttributes();
fa47d7a7
VS
1033 SetOwnForegroundColour( attr.colFg );
1034 SetOwnBackgroundColour( attr.colBg );
92a4e4de
VZ
1035 if (!m_hasFont)
1036 SetOwnFont(attr.font);
c22886bd 1037
f0eebb39
VZ
1038 // this is a misnomer: it's called "dotted pen" but uses (default) wxSOLID
1039 // style because we apparently get performance problems when using dotted
1040 // pen for drawing in some ports -- but under MSW it seems to work fine
1041#ifdef __WXMSW__
04ee05f9 1042 m_dottedPen = wxPen(*wxLIGHT_GREY, 0, wxPENSTYLE_DOT);
f0eebb39
VZ
1043#else
1044 m_dottedPen = *wxGREY_PEN;
1045#endif
f135ff73 1046
170acdc9 1047 SetInitialSize(size);
ca65c044
WS
1048
1049 return true;
edaa81ae 1050}
c801d85f 1051
941830cb 1052wxGenericTreeCtrl::~wxGenericTreeCtrl()
c801d85f 1053{
b771aa29
VZ
1054 delete m_hilightBrush;
1055 delete m_hilightUnfocusedBrush;
574c939e 1056
00e12320 1057 DeleteAllItems();
9dfbf520 1058
00e12320 1059 delete m_renameTimer;
cb59313c
VZ
1060 delete m_findTimer;
1061
cb59313c
VZ
1062 if (m_ownsImageListButtons)
1063 delete m_imageListButtons;
edaa81ae 1064}
c801d85f 1065
f135ff73
VZ
1066// -----------------------------------------------------------------------------
1067// accessors
1068// -----------------------------------------------------------------------------
1069
027d45e8 1070unsigned int wxGenericTreeCtrl::GetCount() const
c801d85f 1071{
ffda4dac
VZ
1072 if ( !m_anchor )
1073 {
1074 // the tree is empty
1075 return 0;
1076 }
1077
027d45e8 1078 unsigned int count = m_anchor->GetChildrenCount();
ffda4dac
VZ
1079 if ( !HasFlag(wxTR_HIDE_ROOT) )
1080 {
1081 // take the root itself into account
1082 count++;
1083 }
1084
1085 return count;
edaa81ae 1086}
c801d85f 1087
941830cb 1088void wxGenericTreeCtrl::SetIndent(unsigned int indent)
c801d85f 1089{
574c939e 1090 m_indent = (unsigned short) indent;
ca65c044 1091 m_dirty = true;
cf724bce
RR
1092}
1093
df74fdea
VZ
1094size_t
1095wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item,
1096 bool recursively) const
4832f7c0 1097{
f2593d0d 1098 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
4832f7c0 1099
941830cb 1100 return ((wxGenericTreeItem*) item.m_pItem)->GetChildrenCount(recursively);
4832f7c0
VZ
1101}
1102
618a5e38
RR
1103void wxGenericTreeCtrl::SetWindowStyle(const long styles)
1104{
867f2ca4
KH
1105 // Do not try to expand the root node if it hasn't been created yet
1106 if (m_anchor && !HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT))
374a8e5c
VS
1107 {
1108 // if we will hide the root, make sure children are visible
1109 m_anchor->SetHasPlus();
1110 m_anchor->Expand();
1111 CalculatePositions();
1112 }
1113
1114 // right now, just sets the styles. Eventually, we may
1115 // want to update the inherited styles, but right now
1116 // none of the parents has updatable styles
618a5e38 1117 m_windowStyle = styles;
ca65c044 1118 m_dirty = true;
618a5e38
RR
1119}
1120
f135ff73
VZ
1121// -----------------------------------------------------------------------------
1122// functions to work with tree items
1123// -----------------------------------------------------------------------------
74bedbeb 1124
941830cb 1125wxString wxGenericTreeCtrl::GetItemText(const wxTreeItemId& item) const
f135ff73 1126{
9548f380 1127 wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") );
4832f7c0 1128
941830cb 1129 return ((wxGenericTreeItem*) item.m_pItem)->GetText();
edaa81ae 1130}
74bedbeb 1131
941830cb 1132int wxGenericTreeCtrl::GetItemImage(const wxTreeItemId& item,
8dc99046 1133 wxTreeItemIcon which) const
74bedbeb 1134{
f2593d0d 1135 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
4832f7c0 1136
941830cb 1137 return ((wxGenericTreeItem*) item.m_pItem)->GetImage(which);
edaa81ae 1138}
c801d85f 1139
941830cb 1140wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const
c801d85f 1141{
f2593d0d 1142 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
4832f7c0 1143
941830cb 1144 return ((wxGenericTreeItem*) item.m_pItem)->GetData();
edaa81ae 1145}
c801d85f 1146
03966fcb
RR
1147int wxGenericTreeCtrl::DoGetItemState(const wxTreeItemId& item) const
1148{
1149 wxCHECK_MSG( item.IsOk(), wxTREE_ITEMSTATE_NONE, wxT("invalid tree item") );
1150
1151 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1152 return pItem->GetState();
1153}
1154
2b5f62a0
VZ
1155wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const
1156{
1157 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1158
1159 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1160 return pItem->Attr().GetTextColour();
1161}
1162
8239070b
VZ
1163wxColour
1164wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const
2b5f62a0
VZ
1165{
1166 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1167
1168 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1169 return pItem->Attr().GetBackgroundColour();
1170}
1171
1172wxFont wxGenericTreeCtrl::GetItemFont(const wxTreeItemId& item) const
1173{
1174 wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
1175
1176 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1177 return pItem->Attr().GetFont();
1178}
1179
8239070b
VZ
1180void
1181wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
f135ff73 1182{
f2593d0d 1183 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 1184
941830cb 1185 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
f2593d0d 1186 pItem->SetText(text);
cac09ce8 1187 pItem->CalculateSize(this);
f2593d0d 1188 RefreshLine(pItem);
f135ff73 1189}
c801d85f 1190
941830cb 1191void wxGenericTreeCtrl::SetItemImage(const wxTreeItemId& item,
8dc99046
VZ
1192 int image,
1193 wxTreeItemIcon which)
f135ff73 1194{
223d09f6 1195 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 1196
941830cb 1197 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
8dc99046 1198 pItem->SetImage(image, which);
cac09ce8 1199 pItem->CalculateSize(this);
8dc99046 1200 RefreshLine(pItem);
edaa81ae 1201}
c801d85f 1202
8239070b
VZ
1203void
1204wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
c801d85f 1205{
f2593d0d 1206 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 1207
74707d69
RR
1208 if (data)
1209 data->SetId( item );
ca65c044 1210
941830cb 1211 ((wxGenericTreeItem*) item.m_pItem)->SetData(data);
edaa81ae 1212}
c801d85f 1213
03966fcb
RR
1214void wxGenericTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state)
1215{
1216 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1217
1218 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1219 pItem->SetState(state);
cac09ce8 1220 pItem->CalculateSize(this);
03966fcb
RR
1221 RefreshLine(pItem);
1222}
1223
941830cb 1224void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
c801d85f 1225{
f2593d0d 1226 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 1227
941830cb 1228 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
f2593d0d
RR
1229 pItem->SetHasPlus(has);
1230 RefreshLine(pItem);
edaa81ae 1231}
c801d85f 1232
941830cb 1233void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
ef44a621 1234{
9ec64fa7 1235 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
ef44a621 1236
9ec64fa7 1237 // avoid redrawing the tree if no real change
941830cb 1238 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
9ec64fa7
VZ
1239 if ( pItem->IsBold() != bold )
1240 {
1241 pItem->SetBold(bold);
3a1a76a8
VZ
1242
1243 // recalculate the item size as bold and non bold fonts have different
1244 // widths
cac09ce8 1245 pItem->CalculateSize(this);
9ec64fa7
VZ
1246 RefreshLine(pItem);
1247 }
1248}
1249
9fce43b7
RR
1250void wxGenericTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item,
1251 bool highlight)
1252{
1253 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1254
1255 wxColour fg, bg;
1256
1257 if (highlight)
1258 {
1259 bg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
1260 fg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
1261 }
1262
1263 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1264 pItem->Attr().SetTextColour(fg);
1265 pItem->Attr().SetBackgroundColour(bg);
1266 RefreshLine(pItem);
1267}
1268
941830cb 1269void wxGenericTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
9ec64fa7
VZ
1270 const wxColour& col)
1271{
1272 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1273
941830cb 1274 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
9ec64fa7
VZ
1275 pItem->Attr().SetTextColour(col);
1276 RefreshLine(pItem);
1277}
1278
941830cb 1279void wxGenericTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
9ec64fa7
VZ
1280 const wxColour& col)
1281{
1282 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1283
941830cb 1284 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
9ec64fa7
VZ
1285 pItem->Attr().SetBackgroundColour(col);
1286 RefreshLine(pItem);
1287}
1288
8239070b
VZ
1289void
1290wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
9ec64fa7
VZ
1291{
1292 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1293
941830cb 1294 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
9ec64fa7 1295 pItem->Attr().SetFont(font);
cac09ce8
VZ
1296 pItem->ResetTextSize();
1297 pItem->CalculateSize(this);
ef44a621 1298 RefreshLine(pItem);
ef44a621
VZ
1299}
1300
3da2715f
JS
1301bool wxGenericTreeCtrl::SetFont( const wxFont &font )
1302{
8cee4a30 1303 wxTreeCtrlBase::SetFont(font);
3da2715f
JS
1304
1305 m_normalFont = font ;
2b5f62a0
VZ
1306 m_boldFont = wxFont(m_normalFont.GetPointSize(),
1307 m_normalFont.GetFamily(),
1308 m_normalFont.GetStyle(),
1309 wxBOLD,
1310 m_normalFont.GetUnderlined(),
1311 m_normalFont.GetFaceName(),
1312 m_normalFont.GetEncoding());
3da2715f 1313
cac09ce8
VZ
1314 if (m_anchor)
1315 m_anchor->RecursiveResetTextSize();
1316
ca65c044 1317 return true;
3da2715f
JS
1318}
1319
1320
f135ff73
VZ
1321// -----------------------------------------------------------------------------
1322// item status inquiries
1323// -----------------------------------------------------------------------------
1324
1ed01484 1325bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const
c801d85f 1326{
ca65c044 1327 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1ed01484 1328
c22886bd 1329 // An item is only visible if it's not a descendant of a collapsed item
1ed01484 1330 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
c22886bd
RR
1331 wxGenericTreeItem* parent = pItem->GetParent();
1332 while (parent)
1333 {
1334 if (!parent->IsExpanded())
ca65c044 1335 return false;
c22886bd
RR
1336 parent = parent->GetParent();
1337 }
1ed01484
JS
1338
1339 int startX, startY;
1340 GetViewStart(& startX, & startY);
1341
1342 wxSize clientSize = GetClientSize();
1343
1344 wxRect rect;
1345 if (!GetBoundingRect(item, rect))
ca65c044 1346 return false;
c22886bd 1347 if (rect.GetWidth() == 0 || rect.GetHeight() == 0)
ca65c044 1348 return false;
1ed01484 1349 if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y)
ca65c044 1350 return false;
1ed01484 1351 if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x)
ca65c044 1352 return false;
f135ff73 1353
ca65c044 1354 return true;
edaa81ae 1355}
c801d85f 1356
941830cb 1357bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
c801d85f 1358{
ca65c044 1359 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
4832f7c0 1360
59a2e635
VZ
1361 // consider that the item does have children if it has the "+" button: it
1362 // might not have them (if it had never been expanded yet) but then it
1363 // could have them as well and it's better to err on this side rather than
1364 // disabling some operations which are restricted to the items with
1365 // children for an item which does have them
607d9cfc 1366 return ((wxGenericTreeItem*) item.m_pItem)->HasPlus();
edaa81ae 1367}
c801d85f 1368
941830cb 1369bool wxGenericTreeCtrl::IsExpanded(const wxTreeItemId& item) const
c801d85f 1370{
ca65c044 1371 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
4832f7c0 1372
941830cb 1373 return ((wxGenericTreeItem*) item.m_pItem)->IsExpanded();
f135ff73 1374}
29d87bba 1375
941830cb 1376bool wxGenericTreeCtrl::IsSelected(const wxTreeItemId& item) const
f135ff73 1377{
ca65c044 1378 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
4832f7c0 1379
941830cb 1380 return ((wxGenericTreeItem*) item.m_pItem)->IsSelected();
f135ff73 1381}
29d87bba 1382
941830cb 1383bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const
ef44a621 1384{
ca65c044 1385 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
ef44a621 1386
941830cb 1387 return ((wxGenericTreeItem*) item.m_pItem)->IsBold();
ef44a621
VZ
1388}
1389
f135ff73
VZ
1390// -----------------------------------------------------------------------------
1391// navigation
1392// -----------------------------------------------------------------------------
29d87bba 1393
99006e44 1394wxTreeItemId wxGenericTreeCtrl::GetItemParent(const wxTreeItemId& item) const
f135ff73 1395{
618a5e38 1396 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
389cdc7a 1397
618a5e38 1398 return ((wxGenericTreeItem*) item.m_pItem)->GetParent();
f135ff73 1399}
29d87bba 1400
ee4b2721
VZ
1401wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item,
1402 wxTreeItemIdValue& cookie) const
f135ff73 1403{
618a5e38 1404 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 1405
618a5e38
RR
1406 cookie = 0;
1407 return GetNextChild(item, cookie);
f135ff73 1408}
29d87bba 1409
ee4b2721
VZ
1410wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item,
1411 wxTreeItemIdValue& cookie) const
1412{
1413 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1414
8239070b
VZ
1415 wxArrayGenericTreeItems&
1416 children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
ee4b2721
VZ
1417
1418 // it's ok to cast cookie to size_t, we never have indices big enough to
1419 // overflow "void *"
1420 size_t *pIndex = (size_t *)&cookie;
b4a980f4 1421 if ( *pIndex < children.GetCount() )
ee4b2721 1422 {
836543b8 1423 return children.Item((*pIndex)++);
ee4b2721
VZ
1424 }
1425 else
1426 {
1427 // there are no more of them
1428 return wxTreeItemId();
1429 }
1430}
1431
941830cb 1432wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const
978f38c2 1433{
618a5e38 1434 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
978f38c2 1435
8239070b
VZ
1436 wxArrayGenericTreeItems&
1437 children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
1438 return children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last());
978f38c2
VZ
1439}
1440
941830cb 1441wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
f135ff73 1442{
618a5e38 1443 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f135ff73 1444
618a5e38
RR
1445 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1446 wxGenericTreeItem *parent = i->GetParent();
1447 if ( parent == NULL )
1448 {
1449 // root item doesn't have any siblings
1450 return wxTreeItemId();
1451 }
4832f7c0 1452
618a5e38
RR
1453 wxArrayGenericTreeItems& siblings = parent->GetChildren();
1454 int index = siblings.Index(i);
1455 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 1456
618a5e38 1457 size_t n = (size_t)(index + 1);
8239070b
VZ
1458 return n == siblings.GetCount() ? wxTreeItemId()
1459 : wxTreeItemId(siblings[n]);
edaa81ae 1460}
c801d85f 1461
941830cb 1462wxTreeItemId wxGenericTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
c801d85f 1463{
618a5e38 1464 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f135ff73 1465
618a5e38
RR
1466 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1467 wxGenericTreeItem *parent = i->GetParent();
1468 if ( parent == NULL )
1469 {
1470 // root item doesn't have any siblings
1471 return wxTreeItemId();
1472 }
4832f7c0 1473
618a5e38
RR
1474 wxArrayGenericTreeItems& siblings = parent->GetChildren();
1475 int index = siblings.Index(i);
1476 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 1477
618a5e38
RR
1478 return index == 0 ? wxTreeItemId()
1479 : wxTreeItemId(siblings[(size_t)(index - 1)]);
f135ff73 1480}
389cdc7a 1481
1ed01484
JS
1482// Only for internal use right now, but should probably be public
1483wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const
f135ff73 1484{
618a5e38
RR
1485 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1486
1487 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1488
1489 // First see if there are any children.
1490 wxArrayGenericTreeItems& children = i->GetChildren();
1491 if (children.GetCount() > 0)
1492 {
1493 return children.Item(0);
1494 }
1495 else
1496 {
1497 // Try a sibling of this or ancestor instead
1498 wxTreeItemId p = item;
1499 wxTreeItemId toFind;
1500 do
1501 {
1502 toFind = GetNextSibling(p);
99006e44 1503 p = GetItemParent(p);
618a5e38
RR
1504 } while (p.IsOk() && !toFind.IsOk());
1505 return toFind;
1506 }
1ed01484
JS
1507}
1508
1ed01484
JS
1509wxTreeItemId wxGenericTreeCtrl::GetFirstVisibleItem() const
1510{
618a5e38
RR
1511 wxTreeItemId id = GetRootItem();
1512 if (!id.IsOk())
1ed01484 1513 return id;
1ed01484 1514
618a5e38
RR
1515 do
1516 {
1517 if (IsVisible(id))
1518 return id;
1519 id = GetNext(id);
1520 } while (id.IsOk());
1521
1522 return wxTreeItemId();
1ed01484
JS
1523}
1524
941830cb 1525wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
f135ff73 1526{
618a5e38 1527 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f73eddd2 1528 wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") );
29d87bba 1529
618a5e38
RR
1530 wxTreeItemId id = item;
1531 if (id.IsOk())
1532 {
1533 while (id = GetNext(id), id.IsOk())
1534 {
1535 if (IsVisible(id))
1536 return id;
1537 }
1538 }
1539 return wxTreeItemId();
f135ff73 1540}
29d87bba 1541
941830cb 1542wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
f135ff73 1543{
618a5e38 1544 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f73eddd2 1545 wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") );
29d87bba 1546
f73eddd2
VZ
1547 // find out the starting point
1548 wxTreeItemId prevItem = GetPrevSibling(item);
1549 if ( !prevItem.IsOk() )
1550 {
1551 prevItem = GetItemParent(item);
1552 }
29d87bba 1553
f73eddd2
VZ
1554 // find the first visible item after it
1555 while ( prevItem.IsOk() && !IsVisible(prevItem) )
1556 {
1557 prevItem = GetNext(prevItem);
1558 if ( !prevItem.IsOk() || prevItem == item )
1559 {
1560 // there are no visible items before item
1561 return wxTreeItemId();
1562 }
1563 }
1564
1565 // from there we must be able to navigate until this item
1566 while ( prevItem.IsOk() )
1567 {
1568 const wxTreeItemId nextItem = GetNextVisible(prevItem);
1569 if ( !nextItem.IsOk() || nextItem == item )
1570 break;
1571
1572 prevItem = nextItem;
1573 }
1574
1575 return prevItem;
edaa81ae 1576}
c801d85f 1577
fbb12260
JS
1578// called by wxTextTreeCtrl when it marks itself for deletion
1579void wxGenericTreeCtrl::ResetTextControl()
1580{
eb7f24c1 1581 m_textCtrl = NULL;
fbb12260
JS
1582}
1583
cb59313c
VZ
1584// find the first item starting with the given prefix after the given item
1585wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent,
1586 const wxString& prefixOrig) const
1587{
1588 // match is case insensitive as this is more convenient to the user: having
1589 // to press Shift-letter to go to the item starting with a capital letter
1590 // would be too bothersome
1591 wxString prefix = prefixOrig.Lower();
1592
526b8142
VZ
1593 // determine the starting point: we shouldn't take the current item (this
1594 // allows to switch between two items starting with the same letter just by
1595 // pressing it) but we shouldn't jump to the next one if the user is
1596 // continuing to type as otherwise he might easily skip the item he wanted
cb59313c 1597 wxTreeItemId id = idParent;
526b8142
VZ
1598 if ( prefix.length() == 1 )
1599 {
1600 id = GetNext(id);
1601 }
cb59313c 1602
526b8142 1603 // look for the item starting with the given prefix after it
cb59313c
VZ
1604 while ( id.IsOk() && !GetItemText(id).Lower().StartsWith(prefix) )
1605 {
1606 id = GetNext(id);
1607 }
1608
526b8142
VZ
1609 // if we haven't found anything...
1610 if ( !id.IsOk() )
1611 {
1612 // ... wrap to the beginning
1613 id = GetRootItem();
1614 if ( HasFlag(wxTR_HIDE_ROOT) )
1615 {
1616 // can't select virtual root
1617 id = GetNext(id);
1618 }
1619
1620 // and try all the items (stop when we get to the one we started from)
8239070b
VZ
1621 while ( id.IsOk() && id != idParent &&
1622 !GetItemText(id).Lower().StartsWith(prefix) )
526b8142
VZ
1623 {
1624 id = GetNext(id);
1625 }
b9d880d6
RR
1626 // If we haven't found the item, id.IsOk() will be false, as per
1627 // documentation
526b8142
VZ
1628 }
1629
cb59313c
VZ
1630 return id;
1631}
1632
f135ff73
VZ
1633// -----------------------------------------------------------------------------
1634// operations
1635// -----------------------------------------------------------------------------
1636
941830cb 1637wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
8cee4a30
VZ
1638 size_t previous,
1639 const wxString& text,
1640 int image,
1641 int selImage,
1642 wxTreeItemData *data)
c801d85f 1643{
941830cb 1644 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
f49f2b0c
RR
1645 if ( !parent )
1646 {
1647 // should we give a warning here?
1648 return AddRoot(text, image, selImage, data);
1649 }
4832f7c0 1650
ca65c044 1651 m_dirty = true; // do this first so stuff below doesn't cause flicker
618a5e38 1652
f38374d0 1653 wxGenericTreeItem *item =
2b84e565 1654 new wxGenericTreeItem( parent, text, image, selImage, data );
74bedbeb 1655
f49f2b0c
RR
1656 if ( data != NULL )
1657 {
ee4b2721 1658 data->m_pItem = item;
f49f2b0c 1659 }
74bedbeb 1660
8cee4a30
VZ
1661 parent->Insert( item, previous == (size_t)-1 ? parent->GetChildren().size()
1662 : previous );
ef44a621 1663
96aaaff8 1664 InvalidateBestSize();
f49f2b0c 1665 return item;
4c681997
RR
1666}
1667
941830cb 1668wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text,
8cee4a30
VZ
1669 int image,
1670 int selImage,
1671 wxTreeItemData *data)
4c681997 1672{
8239070b 1673 wxCHECK_MSG( !m_anchor, wxTreeItemId(), "tree can have only one root" );
389cdc7a 1674
ca65c044 1675 m_dirty = true; // do this first so stuff below doesn't cause flicker
618a5e38 1676
d3b9f782 1677 m_anchor = new wxGenericTreeItem(NULL, text,
f135ff73 1678 image, selImage, data);
cf31a1d7
VS
1679 if ( data != NULL )
1680 {
ee4b2721 1681 data->m_pItem = m_anchor;
cf31a1d7
VS
1682 }
1683
618a5e38
RR
1684 if (HasFlag(wxTR_HIDE_ROOT))
1685 {
1686 // if root is hidden, make sure we can navigate
1687 // into children
1688 m_anchor->SetHasPlus();
999723f3
VS
1689 m_anchor->Expand();
1690 CalculatePositions();
618a5e38 1691 }
f38374d0 1692
f49f2b0c
RR
1693 if (!HasFlag(wxTR_MULTIPLE))
1694 {
1695 m_current = m_key_current = m_anchor;
ca65c044 1696 m_current->SetHilight( true );
f49f2b0c 1697 }
389cdc7a 1698
96aaaff8 1699 InvalidateBestSize();
f49f2b0c 1700 return m_anchor;
edaa81ae 1701}
c801d85f 1702
8cee4a30
VZ
1703wxTreeItemId wxGenericTreeCtrl::DoInsertAfter(const wxTreeItemId& parentId,
1704 const wxTreeItemId& idPrevious,
1705 const wxString& text,
1706 int image, int selImage,
1707 wxTreeItemData *data)
c801d85f 1708{
941830cb 1709 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
f2593d0d
RR
1710 if ( !parent )
1711 {
1712 // should we give a warning here?
1713 return AddRoot(text, image, selImage, data);
1714 }
c801d85f 1715
4855a477
JS
1716 int index = -1;
1717 if (idPrevious.IsOk())
1718 {
8239070b
VZ
1719 index = parent->GetChildren().Index(
1720 (wxGenericTreeItem*) idPrevious.m_pItem);
4855a477 1721 wxASSERT_MSG( index != wxNOT_FOUND,
8239070b
VZ
1722 "previous item in wxGenericTreeCtrl::InsertItem() "
1723 "is not a sibling" );
4855a477 1724 }
06b466c7 1725
f2593d0d
RR
1726 return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
1727}
1728
74bedbeb 1729
941830cb 1730void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
a43a4f9d 1731{
09f277d6 1732 wxTreeEvent event(wxEVT_COMMAND_TREE_DELETE_ITEM, this, item);
eafd76b0 1733 GetEventHandler()->ProcessEvent( event );
a43a4f9d
VZ
1734}
1735
0cf3b542
RR
1736// Don't leave edit or selection on a child which is about to disappear
1737void wxGenericTreeCtrl::ChildrenClosing(wxGenericTreeItem* item)
1738{
8239070b
VZ
1739 if ( m_textCtrl && item != m_textCtrl->item() &&
1740 IsDescendantOf(item, m_textCtrl->item()) )
1741 {
d0e2c75c 1742 m_textCtrl->EndEdit( true );
0cf3b542 1743 }
8239070b
VZ
1744
1745 if ( item != m_key_current && IsDescendantOf(item, m_key_current) )
1746 {
0cf3b542
RR
1747 m_key_current = NULL;
1748 }
8239070b
VZ
1749
1750 if ( IsDescendantOf(item, m_select_me) )
1751 {
0cf3b542
RR
1752 m_select_me = item;
1753 }
8239070b
VZ
1754
1755 if ( item != m_current && IsDescendantOf(item, m_current) )
1756 {
af3961b3 1757 m_current->SetHilight( false );
0cf3b542
RR
1758 m_current = NULL;
1759 m_select_me = item;
1760 }
1761}
1762
941830cb 1763void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
372edb9d 1764{
ca65c044 1765 m_dirty = true; // do this first so stuff below doesn't cause flicker
618a5e38 1766
941830cb 1767 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
0cf3b542 1768 ChildrenClosing(item);
a43a4f9d 1769 item->DeleteChildren(this);
96aaaff8 1770 InvalidateBestSize();
372edb9d
VZ
1771}
1772
941830cb 1773void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId)
c801d85f 1774{
ca65c044 1775 m_dirty = true; // do this first so stuff below doesn't cause flicker
618a5e38 1776
941830cb 1777 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
ff5bf259 1778
0cf3b542 1779 if (m_textCtrl != NULL && IsDescendantOf(item, m_textCtrl->item()))
eb7f24c1
VZ
1780 {
1781 // can't delete the item being edited, cancel editing it first
d0e2c75c 1782 m_textCtrl->EndEdit( true );
eb7f24c1
VZ
1783 }
1784
7475e8a3 1785 wxGenericTreeItem *parent = item->GetParent();
668674c1
RR
1786
1787 // if the selected item will be deleted, select the parent ...
1788 wxGenericTreeItem *to_be_selected = parent;
1789 if (parent)
1790 {
1791 // .. unless there is a next sibling like wxMSW does it
1792 int pos = parent->GetChildren().Index( item );
1793 if ((int)(parent->GetChildren().GetCount()) > pos+1)
1794 to_be_selected = parent->GetChildren().Item( pos+1 );
1795 }
7475e8a3
VZ
1796
1797 // don't keep stale pointers around!
1798 if ( IsDescendantOf(item, m_key_current) )
aaa2f297 1799 {
3e3a7b97
JS
1800 // Don't silently change the selection:
1801 // do it properly in idle time, so event
1802 // handlers get called.
ca65c044 1803
3e3a7b97
JS
1804 // m_key_current = parent;
1805 m_key_current = NULL;
1806 }
1807
1808 // m_select_me records whether we need to select
1809 // a different item, in idle time.
1810 if ( m_select_me && IsDescendantOf(item, m_select_me) )
1811 {
668674c1 1812 m_select_me = to_be_selected;
aaa2f297
VZ
1813 }
1814
7475e8a3
VZ
1815 if ( IsDescendantOf(item, m_current) )
1816 {
3e3a7b97
JS
1817 // Don't silently change the selection:
1818 // do it properly in idle time, so event
1819 // handlers get called.
ca65c044 1820
3e3a7b97
JS
1821 // m_current = parent;
1822 m_current = NULL;
668674c1 1823 m_select_me = to_be_selected;
7475e8a3
VZ
1824 }
1825
1826 // remove the item from the tree
97d7bfb8
RR
1827 if ( parent )
1828 {
1829 parent->GetChildren().Remove( item ); // remove by value
1830 }
7475e8a3 1831 else // deleting the root
aaa2f297 1832 {
7475e8a3
VZ
1833 // nothing will be left in the tree
1834 m_anchor = NULL;
aaa2f297
VZ
1835 }
1836
7475e8a3 1837 // and delete all of its children and the item itself now
97d7bfb8
RR
1838 item->DeleteChildren(this);
1839 SendDeleteEvent(item);
5117f9bf
JS
1840
1841 if (item == m_select_me)
1842 m_select_me = NULL;
1729813a 1843
97d7bfb8 1844 delete item;
96aaaff8
RD
1845
1846 InvalidateBestSize();
edaa81ae 1847}
c801d85f 1848
941830cb 1849void wxGenericTreeCtrl::DeleteAllItems()
c801d85f 1850{
97d7bfb8
RR
1851 if ( m_anchor )
1852 {
7475e8a3 1853 Delete(m_anchor);
97d7bfb8 1854 }
edaa81ae
RR
1855}
1856
941830cb 1857void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
edaa81ae 1858{
941830cb 1859 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
f135ff73 1860
941830cb 1861 wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") );
7a944d2f 1862 wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
999723f3 1863 _T("can't expand hidden root") );
f6bcfd97 1864
f2593d0d
RR
1865 if ( !item->HasPlus() )
1866 return;
978f38c2 1867
f2593d0d
RR
1868 if ( item->IsExpanded() )
1869 return;
f135ff73 1870
09f277d6 1871 wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_EXPANDING, this, item);
004fd0c8 1872
eafd76b0 1873 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
f2593d0d
RR
1874 {
1875 // cancelled by program
1876 return;
1877 }
4832f7c0 1878
f2593d0d 1879 item->Expand();
17808a75 1880 if ( !IsFrozen() )
624f89c2
VZ
1881 {
1882 CalculatePositions();
f135ff73 1883
624f89c2
VZ
1884 RefreshSubtree(item);
1885 }
1886 else // frozen
1887 {
1888 m_dirty = true;
1889 }
f135ff73 1890
f2593d0d 1891 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
eafd76b0 1892 GetEventHandler()->ProcessEvent( event );
edaa81ae
RR
1893}
1894
941830cb 1895void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
edaa81ae 1896{
7a944d2f 1897 wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
999723f3
VS
1898 _T("can't collapse hidden root") );
1899
941830cb 1900 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
f135ff73 1901
f2593d0d
RR
1902 if ( !item->IsExpanded() )
1903 return;
f135ff73 1904
09f277d6 1905 wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_COLLAPSING, this, item);
eafd76b0 1906 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
f2593d0d
RR
1907 {
1908 // cancelled by program
1909 return;
1910 }
4832f7c0 1911
0cf3b542 1912 ChildrenClosing(item);
f2593d0d 1913 item->Collapse();
f135ff73 1914
618a5e38 1915#if 0 // TODO why should items be collapsed recursively?
f2593d0d 1916 wxArrayGenericTreeItems& children = item->GetChildren();
b4a980f4 1917 size_t count = children.GetCount();
f2593d0d
RR
1918 for ( size_t n = 0; n < count; n++ )
1919 {
1920 Collapse(children[n]);
1921 }
618a5e38 1922#endif
f135ff73 1923
f2593d0d 1924 CalculatePositions();
f135ff73 1925
f2593d0d 1926 RefreshSubtree(item);
f135ff73 1927
f2593d0d 1928 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
eafd76b0 1929 GetEventHandler()->ProcessEvent( event );
edaa81ae 1930}
c801d85f 1931
941830cb 1932void wxGenericTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
c801d85f 1933{
00e12320
RR
1934 Collapse(item);
1935 DeleteChildren(item);
edaa81ae 1936}
c801d85f 1937
941830cb 1938void wxGenericTreeCtrl::Toggle(const wxTreeItemId& itemId)
c801d85f 1939{
941830cb 1940 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
389cdc7a 1941
00e12320
RR
1942 if (item->IsExpanded())
1943 Collapse(itemId);
1944 else
1945 Expand(itemId);
f135ff73 1946}
389cdc7a 1947
941830cb 1948void wxGenericTreeCtrl::Unselect()
f135ff73 1949{
00e12320
RR
1950 if (m_current)
1951 {
ca65c044 1952 m_current->SetHilight( false );
00e12320 1953 RefreshLine( m_current );
02fe25c2
VZ
1954
1955 m_current = NULL;
3e3a7b97 1956 m_select_me = NULL;
00e12320 1957 }
edaa81ae 1958}
c801d85f 1959
941830cb 1960void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
389cdc7a 1961{
00e12320
RR
1962 if (item->IsSelected())
1963 {
ca65c044 1964 item->SetHilight(false);
00e12320
RR
1965 RefreshLine(item);
1966 }
d3a9f4af 1967
00e12320 1968 if (item->HasChildren())
88ac883a 1969 {
00e12320 1970 wxArrayGenericTreeItems& children = item->GetChildren();
b4a980f4 1971 size_t count = children.GetCount();
00e12320
RR
1972 for ( size_t n = 0; n < count; ++n )
1973 {
1974 UnselectAllChildren(children[n]);
1975 }
88ac883a
VZ
1976 }
1977}
f135ff73 1978
941830cb 1979void wxGenericTreeCtrl::UnselectAll()
88ac883a 1980{
0a33446c
VZ
1981 wxTreeItemId rootItem = GetRootItem();
1982
1983 // the tree might not have the root item at all
1984 if ( rootItem )
1985 {
1986 UnselectAllChildren((wxGenericTreeItem*) rootItem.m_pItem);
1987 }
88ac883a
VZ
1988}
1989
1990// Recursive function !
1991// To stop we must have crt_item<last_item
91b8de8d 1992// Algorithm :
88ac883a 1993// Tag all next children, when no more children,
d3a9f4af 1994// Move to parent (not to tag)
91b8de8d 1995// Keep going... if we found last_item, we stop.
8239070b
VZ
1996bool
1997wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item,
1998 wxGenericTreeItem *last_item,
1999 bool select)
88ac883a
VZ
2000{
2001 wxGenericTreeItem *parent = crt_item->GetParent();
2002
00e12320
RR
2003 if (parent == NULL) // This is root item
2004 return TagAllChildrenUntilLast(crt_item, last_item, select);
88ac883a 2005
91b8de8d 2006 wxArrayGenericTreeItems& children = parent->GetChildren();
88ac883a
VZ
2007 int index = children.Index(crt_item);
2008 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
2009
b4a980f4 2010 size_t count = children.GetCount();
88ac883a 2011 for (size_t n=(size_t)(index+1); n<count; ++n)
00e12320 2012 {
8239070b
VZ
2013 if ( TagAllChildrenUntilLast(children[n], last_item, select) )
2014 return true;
00e12320 2015 }
88ac883a
VZ
2016
2017 return TagNextChildren(parent, last_item, select);
2018}
2019
8239070b
VZ
2020bool
2021wxGenericTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item,
2022 wxGenericTreeItem *last_item,
2023 bool select)
88ac883a 2024{
00e12320
RR
2025 crt_item->SetHilight(select);
2026 RefreshLine(crt_item);
d3a9f4af 2027
06b466c7 2028 if (crt_item==last_item)
ca65c044 2029 return true;
88ac883a 2030
00e12320 2031 if (crt_item->HasChildren())
88ac883a 2032 {
00e12320 2033 wxArrayGenericTreeItems& children = crt_item->GetChildren();
b4a980f4 2034 size_t count = children.GetCount();
00e12320
RR
2035 for ( size_t n = 0; n < count; ++n )
2036 {
06b466c7 2037 if (TagAllChildrenUntilLast(children[n], last_item, select))
ca65c044 2038 return true;
06b466c7 2039 }
88ac883a 2040 }
d3a9f4af 2041
ca65c044 2042 return false;
88ac883a
VZ
2043}
2044
8239070b
VZ
2045void
2046wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1,
2047 wxGenericTreeItem *item2)
88ac883a 2048{
3e3a7b97 2049 m_select_me = NULL;
88ac883a 2050
999836aa 2051 // item2 is not necessary after item1
f2593d0d 2052 // choice first' and 'last' between item1 and item2
999836aa
VZ
2053 wxGenericTreeItem *first= (item1->GetY()<item2->GetY()) ? item1 : item2;
2054 wxGenericTreeItem *last = (item1->GetY()<item2->GetY()) ? item2 : item1;
88ac883a 2055
f2593d0d 2056 bool select = m_current->IsSelected();
d3a9f4af 2057
f2593d0d
RR
2058 if ( TagAllChildrenUntilLast(first,last,select) )
2059 return;
88ac883a 2060
f2593d0d 2061 TagNextChildren(first,last,select);
88ac883a
VZ
2062}
2063
78f12104
VZ
2064void wxGenericTreeCtrl::DoSelectItem(const wxTreeItemId& itemId,
2065 bool unselect_others,
2066 bool extended_select)
d3a9f4af 2067{
223d09f6 2068 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
8b04a037 2069
3e3a7b97 2070 m_select_me = NULL;
ca65c044 2071
88ac883a 2072 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
941830cb 2073 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
88ac883a
VZ
2074
2075 //wxCHECK_RET( ( (!unselect_others) && is_single),
223d09f6 2076 // wxT("this is a single selection tree") );
88ac883a
VZ
2077
2078 // to keep going anyhow !!!
d3a9f4af 2079 if (is_single)
8dc99046
VZ
2080 {
2081 if (item->IsSelected())
2082 return; // nothing to do
ca65c044
WS
2083 unselect_others = true;
2084 extended_select = false;
8dc99046
VZ
2085 }
2086 else if ( unselect_others && item->IsSelected() )
2087 {
2088 // selection change if there is more than one item currently selected
2089 wxArrayTreeItemIds selected_items;
2090 if ( GetSelections(selected_items) == 1 )
2091 return;
2092 }
d3a9f4af 2093
09f277d6 2094 wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
ee4b2721 2095 event.m_itemOld = m_current;
91b8de8d 2096 // TODO : Here we don't send any selection mode yet !
d3a9f4af 2097
f98e2558 2098 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
618a5e38 2099 return;
f135ff73 2100
99006e44 2101 wxTreeItemId parent = GetItemParent( itemId );
2cc78389
RR
2102 while (parent.IsOk())
2103 {
2104 if (!IsExpanded(parent))
2105 Expand( parent );
cdb3cffe 2106
99006e44 2107 parent = GetItemParent( parent );
2cc78389 2108 }
cdb3cffe 2109
88ac883a
VZ
2110 // ctrl press
2111 if (unselect_others)
389cdc7a 2112 {
88ac883a 2113 if (is_single) Unselect(); // to speed up thing
c193b707 2114 else UnselectAll();
edaa81ae 2115 }
f135ff73 2116
88ac883a 2117 // shift press
d3a9f4af 2118 if (extended_select)
88ac883a 2119 {
aaa2f297
VZ
2120 if ( !m_current )
2121 {
8239070b
VZ
2122 m_current =
2123 m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem;
aaa2f297
VZ
2124 }
2125
88ac883a
VZ
2126 // don't change the mark (m_current)
2127 SelectItemRange(m_current, item);
2128 }
2129 else
2130 {
ca65c044 2131 bool select = true; // the default
88ac883a 2132
c193b707
VZ
2133 // Check if we need to toggle hilight (ctrl mode)
2134 if (!unselect_others)
618a5e38 2135 select=!item->IsSelected();
88ac883a 2136
91b8de8d 2137 m_current = m_key_current = item;
c193b707
VZ
2138 m_current->SetHilight(select);
2139 RefreshLine( m_current );
88ac883a 2140 }
389cdc7a 2141
7ede7389
JS
2142 // This can cause idle processing to select the root
2143 // if no item is selected, so it must be after the
2144 // selection is set
2145 EnsureVisible( itemId );
2146
f135ff73 2147 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
6daa0637 2148 GetEventHandler()->ProcessEvent( event );
389cdc7a
VZ
2149}
2150
78f12104
VZ
2151void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, bool select)
2152{
2153 if ( select )
2154 {
fced5066 2155 DoSelectItem(itemId, !HasFlag(wxTR_MULTIPLE));
78f12104
VZ
2156 }
2157 else // deselect
2158 {
2159 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
2160 wxCHECK_RET( item, wxT("SelectItem(): invalid tree item") );
6621957f 2161
714dfaa6
RR
2162 wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
2163 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
2164 return;
2165
ca65c044 2166 item->SetHilight(false);
78f12104 2167 RefreshLine(item);
b9d880d6 2168
714dfaa6
RR
2169 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
2170 GetEventHandler()->ProcessEvent( event );
78f12104
VZ
2171 }
2172}
2173
941830cb 2174void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
cb59313c 2175 wxArrayTreeItemIds &array) const
c801d85f 2176{
8dc99046 2177 if ( item->IsSelected() )
9dfbf520 2178 array.Add(wxTreeItemId(item));
91b8de8d 2179
9dfbf520 2180 if ( item->HasChildren() )
91b8de8d 2181 {
9dfbf520
VZ
2182 wxArrayGenericTreeItems& children = item->GetChildren();
2183 size_t count = children.GetCount();
2184 for ( size_t n = 0; n < count; ++n )
f6bcfd97 2185 FillArray(children[n], array);
91b8de8d
RR
2186 }
2187}
2188
941830cb 2189size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
91b8de8d 2190{
618a5e38
RR
2191 array.Empty();
2192 wxTreeItemId idRoot = GetRootItem();
2193 if ( idRoot.IsOk() )
2194 {
2195 FillArray((wxGenericTreeItem*) idRoot.m_pItem, array);
2196 }
2197 //else: the tree is empty, so no selections
91b8de8d 2198
b4a980f4 2199 return array.GetCount();
91b8de8d
RR
2200}
2201
941830cb 2202void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item)
d3a9f4af 2203{
05b2a432
RD
2204 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2205
91b8de8d
RR
2206 if (!item.IsOk()) return;
2207
941830cb 2208 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
ef44a621 2209
f65635b5
VZ
2210 // first expand all parent branches
2211 wxGenericTreeItem *parent = gitem->GetParent();
999723f3
VS
2212
2213 if ( HasFlag(wxTR_HIDE_ROOT) )
f65635b5 2214 {
ff38281a 2215 while ( parent && parent != m_anchor )
999723f3
VS
2216 {
2217 Expand(parent);
2218 parent = parent->GetParent();
2219 }
2220 }
2221 else
2222 {
2223 while ( parent )
2224 {
2225 Expand(parent);
2226 parent = parent->GetParent();
2227 }
f65635b5
VZ
2228 }
2229
5391f772 2230 //if (parent) CalculatePositions();
91b8de8d
RR
2231
2232 ScrollTo(item);
2233}
2234
941830cb 2235void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item)
91b8de8d
RR
2236{
2237 if (!item.IsOk()) return;
2238
dc6c62a9
RR
2239 // We have to call this here because the label in
2240 // question might just have been added and no screen
2241 // update taken place.
ca65c044 2242 if (m_dirty)
f89d65ea
SC
2243#if defined( __WXMSW__ ) || defined(__WXMAC__)
2244 Update();
2245#else
27f8357f 2246 DoDirtyProcessing();
f89d65ea 2247#endif
941830cb 2248 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
91b8de8d 2249
f65635b5 2250 // now scroll to the item
0659e7ee 2251 int item_y = gitem->GetY();
8dc99046 2252
0659e7ee
RR
2253 int start_x = 0;
2254 int start_y = 0;
1e6feb95 2255 GetViewStart( &start_x, &start_y );
91b8de8d 2256 start_y *= PIXELS_PER_UNIT;
978f38c2 2257
a93109d5
RR
2258 int client_h = 0;
2259 int client_w = 0;
2260 GetClientSize( &client_w, &client_h );
ef44a621 2261
0659e7ee
RR
2262 if (item_y < start_y+3)
2263 {
91b8de8d 2264 // going down
0659e7ee
RR
2265 int x = 0;
2266 int y = 0;
91b8de8d
RR
2267 m_anchor->GetSize( x, y, this );
2268 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
c7a9fa36 2269 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
0659e7ee 2270 int x_pos = GetScrollPos( wxHORIZONTAL );
c193b707 2271 // Item should appear at top
8239070b
VZ
2272 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT,
2273 x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT,
2274 x_pos, item_y/PIXELS_PER_UNIT );
0659e7ee 2275 }
91b8de8d 2276 else if (item_y+GetLineHeight(gitem) > start_y+client_h)
0659e7ee 2277 {
c7a9fa36
RR
2278 // going up
2279 int x = 0;
2280 int y = 0;
2281 m_anchor->GetSize( x, y, this );
2282 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2283 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2284 item_y += PIXELS_PER_UNIT+2;
2285 int x_pos = GetScrollPos( wxHORIZONTAL );
c193b707 2286 // Item should appear at bottom
8239070b
VZ
2287 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT,
2288 x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT,
2289 x_pos,
2290 (item_y+GetLineHeight(gitem)-client_h)/PIXELS_PER_UNIT );
0659e7ee 2291 }
edaa81ae 2292}
c801d85f 2293
e1ee62bd 2294// FIXME: tree sorting functions are not reentrant and not MT-safe!
941830cb 2295static wxGenericTreeCtrl *s_treeBeingSorted = NULL;
0659e7ee 2296
004fd0c8 2297static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
e1ee62bd 2298 wxGenericTreeItem **item2)
edaa81ae 2299{
8239070b
VZ
2300 wxCHECK_MSG( s_treeBeingSorted, 0,
2301 "bug in wxGenericTreeCtrl::SortChildren()" );
e1ee62bd
VZ
2302
2303 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
0659e7ee
RR
2304}
2305
941830cb 2306void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId)
e1ee62bd 2307{
223d09f6 2308 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
e1ee62bd 2309
941830cb 2310 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
978f38c2 2311
e1ee62bd 2312 wxCHECK_RET( !s_treeBeingSorted,
941830cb 2313 wxT("wxGenericTreeCtrl::SortChildren is not reentrant") );
e1ee62bd 2314
91b8de8d 2315 wxArrayGenericTreeItems& children = item->GetChildren();
b4a980f4 2316 if ( children.GetCount() > 1 )
e1ee62bd 2317 {
ca65c044 2318 m_dirty = true;
618a5e38 2319
e1ee62bd
VZ
2320 s_treeBeingSorted = this;
2321 children.Sort(tree_ctrl_compare_func);
2322 s_treeBeingSorted = NULL;
e1ee62bd
VZ
2323 }
2324 //else: don't make the tree dirty as nothing changed
edaa81ae
RR
2325}
2326
618a5e38 2327void wxGenericTreeCtrl::CalculateLineHeight()
e2414cbe 2328{
f2593d0d
RR
2329 wxClientDC dc(this);
2330 m_lineHeight = (int)(dc.GetCharHeight() + 4);
06b466c7 2331
618a5e38
RR
2332 if ( m_imageListNormal )
2333 {
2334 // Calculate a m_lineHeight value from the normal Image sizes.
2335 // May be toggle off. Then wxGenericTreeCtrl will spread when
2336 // necessary (which might look ugly).
2337 int n = m_imageListNormal->GetImageCount();
2338 for (int i = 0; i < n ; i++)
2339 {
2340 int width = 0, height = 0;
2341 m_imageListNormal->GetSize(i, width, height);
2342 if (height > m_lineHeight) m_lineHeight = height;
2343 }
2344 }
2345
4754ab16
RR
2346 if ( m_imageListState )
2347 {
2348 // Calculate a m_lineHeight value from the state Image sizes.
2349 // May be toggle off. Then wxGenericTreeCtrl will spread when
2350 // necessary (which might look ugly).
2351 int n = m_imageListState->GetImageCount();
2352 for (int i = 0; i < n ; i++)
2353 {
2354 int width = 0, height = 0;
2355 m_imageListState->GetSize(i, width, height);
2356 if (height > m_lineHeight) m_lineHeight = height;
2357 }
2358 }
2359
618a5e38 2360 if (m_imageListButtons)
f2593d0d 2361 {
618a5e38
RR
2362 // Calculate a m_lineHeight value from the Button image sizes.
2363 // May be toggle off. Then wxGenericTreeCtrl will spread when
2364 // necessary (which might look ugly).
2365 int n = m_imageListButtons->GetImageCount();
2366 for (int i = 0; i < n ; i++)
2367 {
2368 int width = 0, height = 0;
2369 m_imageListButtons->GetSize(i, width, height);
2370 if (height > m_lineHeight) m_lineHeight = height;
2371 }
f2593d0d 2372 }
91b8de8d 2373
618a5e38 2374 if (m_lineHeight < 30)
f2593d0d 2375 m_lineHeight += 2; // at least 2 pixels
06b466c7 2376 else
f2593d0d 2377 m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
edaa81ae 2378}
e2414cbe 2379
618a5e38
RR
2380void wxGenericTreeCtrl::SetImageList(wxImageList *imageList)
2381{
2382 if (m_ownsImageListNormal) delete m_imageListNormal;
2383 m_imageListNormal = imageList;
ca65c044
WS
2384 m_ownsImageListNormal = false;
2385 m_dirty = true;
cac09ce8
VZ
2386
2387 if (m_anchor)
2388 m_anchor->RecursiveResetSize();
2389
f4bd6759
JS
2390 // Don't do any drawing if we're setting the list to NULL,
2391 // since we may be in the process of deleting the tree control.
2392 if (imageList)
2393 CalculateLineHeight();
618a5e38
RR
2394}
2395
941830cb 2396void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList)
e2414cbe 2397{
46cd520d 2398 if (m_ownsImageListState) delete m_imageListState;
f135ff73 2399 m_imageListState = imageList;
ca65c044 2400 m_ownsImageListState = false;
4754ab16 2401 m_dirty = true;
cac09ce8
VZ
2402
2403 if (m_anchor)
2404 m_anchor->RecursiveResetSize();
2405
4754ab16
RR
2406 // Don't do any drawing if we're setting the list to NULL,
2407 // since we may be in the process of deleting the tree control.
2408 if (imageList)
2409 CalculateLineHeight();
46cd520d
VS
2410}
2411
618a5e38
RR
2412void wxGenericTreeCtrl::SetButtonsImageList(wxImageList *imageList)
2413{
2414 if (m_ownsImageListButtons) delete m_imageListButtons;
2415 m_imageListButtons = imageList;
ca65c044
WS
2416 m_ownsImageListButtons = false;
2417 m_dirty = true;
cac09ce8
VZ
2418
2419 if (m_anchor)
2420 m_anchor->RecursiveResetSize();
2421
618a5e38
RR
2422 CalculateLineHeight();
2423}
2424
618a5e38
RR
2425void wxGenericTreeCtrl::AssignButtonsImageList(wxImageList *imageList)
2426{
2427 SetButtonsImageList(imageList);
ca65c044 2428 m_ownsImageListButtons = true;
618a5e38
RR
2429}
2430
f135ff73
VZ
2431// -----------------------------------------------------------------------------
2432// helpers
2433// -----------------------------------------------------------------------------
0659e7ee 2434
941830cb 2435void wxGenericTreeCtrl::AdjustMyScrollbars()
c801d85f 2436{
0659e7ee
RR
2437 if (m_anchor)
2438 {
618a5e38 2439 int x = 0, y = 0;
91b8de8d 2440 m_anchor->GetSize( x, y, this );
c193b707 2441 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
c7a9fa36 2442 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
0659e7ee
RR
2443 int x_pos = GetScrollPos( wxHORIZONTAL );
2444 int y_pos = GetScrollPos( wxVERTICAL );
8239070b
VZ
2445 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT,
2446 x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT,
2447 x_pos, y_pos );
0659e7ee
RR
2448 }
2449 else
2450 {
2451 SetScrollbars( 0, 0, 0, 0 );
2452 }
edaa81ae 2453}
c801d85f 2454
941830cb 2455int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
91b8de8d 2456{
dc6c62a9
RR
2457 if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
2458 return item->GetHeight();
2459 else
2460 return m_lineHeight;
91b8de8d
RR
2461}
2462
941830cb 2463void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
ef44a621 2464{
cac09ce8 2465 item->SetFont(this, dc);
e253521d 2466 item->CalculateSize(this, dc);
ef44a621 2467
cac09ce8 2468 wxCoord text_h = item->GetTextHeight();
ef44a621 2469
618a5e38 2470 int image_h = 0, image_w = 0;
8dc99046
VZ
2471 int image = item->GetCurrentImage();
2472 if ( image != NO_IMAGE )
bbe0af5b 2473 {
2c8e4738
VZ
2474 if ( m_imageListNormal )
2475 {
136b7e61 2476 m_imageListNormal->GetSize(image, image_w, image_h);
3e4f8ee2 2477 image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
2c8e4738
VZ
2478 }
2479 else
2480 {
2481 image = NO_IMAGE;
2482 }
bbe0af5b 2483 }
ef44a621 2484
03966fcb
RR
2485 int state_h = 0, state_w = 0;
2486 int state = item->GetState();
2487 if ( state != wxTREE_ITEMSTATE_NONE )
2488 {
2489 if ( m_imageListState )
2490 {
136b7e61
VZ
2491 m_imageListState->GetSize(state, state_w, state_h);
2492 if ( image_w != 0 )
03966fcb
RR
2493 state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
2494 else
2495 state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
2496 }
2497 else
2498 {
2499 state = wxTREE_ITEMSTATE_NONE;
2500 }
2501 }
2502
91b8de8d 2503 int total_h = GetLineHeight(item);
cac09ce8
VZ
2504 bool drawItemBackground = false,
2505 hasBgColour = false;
91b8de8d 2506
b771aa29 2507 if ( item->IsSelected() )
cdb3cffe 2508 {
b771aa29 2509 dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
6c051af4 2510 drawItemBackground = true;
cdb3cffe 2511 }
afa6a1a1 2512 else
c0fba4d1
VS
2513 {
2514 wxColour colBg;
cac09ce8 2515 wxTreeItemAttr * const attr = item->GetAttributes();
c0fba4d1 2516 if ( attr && attr->HasBackgroundColour() )
902725ee 2517 {
cac09ce8
VZ
2518 drawItemBackground =
2519 hasBgColour = true;
c0fba4d1 2520 colBg = attr->GetBackgroundColour();
902725ee 2521 }
c0fba4d1 2522 else
902725ee 2523 {
8cee4a30 2524 colBg = GetBackgroundColour();
902725ee 2525 }
04ee05f9 2526 dc.SetBrush(wxBrush(colBg, wxBRUSHSTYLE_SOLID));
c0fba4d1 2527 }
afa6a1a1 2528
cdb3cffe 2529 int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0;
b77d9650 2530
c6f4913a 2531 if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) )
a69cb1cc 2532 {
9e435585
JS
2533 int x, w, h;
2534 x=0;
2535 GetVirtualSize(&w, &h);
ccdbdc89 2536 wxRect rect( x, item->GetY()+offset, w, total_h-offset);
ccdbdc89
RR
2537 if (!item->IsSelected())
2538 {
2539 dc.DrawRectangle(rect);
2540 }
2541 else
2542 {
05d97538 2543 int flags = wxCONTROL_SELECTED;
ed9a7a63 2544 if (m_hasFocus
530a427a 2545#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS
ed9a7a63
KO
2546 && IsControlActive( (ControlRef)GetHandle() )
2547#endif
2548 )
05d97538 2549 flags |= wxCONTROL_FOCUSED;
ccdbdc89 2550 if ((item == m_current) && (m_hasFocus))
05d97538 2551 flags |= wxCONTROL_CURRENT;
8239070b
VZ
2552
2553 wxRendererNative::Get().
2554 DrawItemSelectionRect(this, dc, rect, flags);
ccdbdc89 2555 }
a69cb1cc 2556 }
419875a6 2557 else // no full row highlight
b77d9650 2558 {
03966fcb
RR
2559 if ( item->IsSelected() &&
2560 (state != wxTREE_ITEMSTATE_NONE || image != NO_IMAGE) )
c6f4913a 2561 {
03966fcb
RR
2562 // If it's selected, and there's an state image or normal image,
2563 // then we should take care to leave the area under the image
2564 // painted in the background colour.
8239070b
VZ
2565 wxRect rect( item->GetX() + state_w + image_w - 2,
2566 item->GetY() + offset,
2567 item->GetWidth() - state_w - image_w + 2,
2568 total_h - offset );
a4609ab8 2569#if !defined(__WXGTK20__) && !defined(__WXMAC__)
ccdbdc89
RR
2570 dc.DrawRectangle( rect );
2571#else
ccdbdc89
RR
2572 rect.x -= 1;
2573 rect.width += 2;
6621957f 2574
05d97538
RR
2575 int flags = wxCONTROL_SELECTED;
2576 if (m_hasFocus)
2577 flags |= wxCONTROL_FOCUSED;
ccdbdc89 2578 if ((item == m_current) && (m_hasFocus))
05d97538 2579 flags |= wxCONTROL_CURRENT;
8239070b
VZ
2580 wxRendererNative::Get().
2581 DrawItemSelectionRect(this, dc, rect, flags);
ccdbdc89 2582#endif
c6f4913a 2583 }
602f0c99
JS
2584 // On GTK+ 2, drawing a 'normal' background is wrong for themes that
2585 // don't allow backgrounds to be customized. Not drawing the background,
2586 // except for custom item backgrounds, works for both kinds of theme.
6c051af4 2587 else if (drawItemBackground)
c6f4913a 2588 {
136b7e61
VZ
2589 wxRect rect( item->GetX() + state_w + image_w - 2,
2590 item->GetY() + offset,
2591 item->GetWidth() - state_w - image_w + 2,
2592 total_h - offset );
cac09ce8 2593 if ( hasBgColour )
ccdbdc89
RR
2594 {
2595 dc.DrawRectangle( rect );
2596 }
419875a6 2597 else // no specific background colour
ccdbdc89 2598 {
ccdbdc89
RR
2599 rect.x -= 1;
2600 rect.width += 2;
6621957f 2601
05d97538
RR
2602 int flags = wxCONTROL_SELECTED;
2603 if (m_hasFocus)
2604 flags |= wxCONTROL_FOCUSED;
ccdbdc89 2605 if ((item == m_current) && (m_hasFocus))
05d97538 2606 flags |= wxCONTROL_CURRENT;
8239070b
VZ
2607 wxRendererNative::Get().
2608 DrawItemSelectionRect(this, dc, rect, flags);
ccdbdc89 2609 }
c6f4913a 2610 }
b77d9650 2611 }
ef44a621 2612
03966fcb
RR
2613 if ( state != wxTREE_ITEMSTATE_NONE )
2614 {
2615 dc.SetClippingRegion( item->GetX(), item->GetY(), state_w, total_h );
2616 m_imageListState->Draw( state, dc,
2617 item->GetX(),
8239070b
VZ
2618 item->GetY() +
2619 (total_h > state_h ? (total_h-state_h)/2
2620 : 0),
03966fcb
RR
2621 wxIMAGELIST_DRAW_TRANSPARENT );
2622 dc.DestroyClippingRegion();
2623 }
2624
8dc99046 2625 if ( image != NO_IMAGE )
bbe0af5b 2626 {
8239070b
VZ
2627 dc.SetClippingRegion(item->GetX() + state_w, item->GetY(),
2628 image_w, total_h);
8dc99046 2629 m_imageListNormal->Draw( image, dc,
4754ab16 2630 item->GetX() + state_w,
8239070b
VZ
2631 item->GetY() +
2632 (total_h > image_h ? (total_h-image_h)/2
2633 : 0),
bbe0af5b
RR
2634 wxIMAGELIST_DRAW_TRANSPARENT );
2635 dc.DestroyClippingRegion();
2636 }
ef44a621 2637
04ee05f9 2638 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
f6bcfd97
BP
2639 int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0;
2640 dc.DrawText( item->GetText(),
03966fcb 2641 (wxCoord)(state_w + image_w + item->GetX()),
f6bcfd97 2642 (wxCoord)(item->GetY() + extraH));
ef44a621 2643
eff869aa
RR
2644 // restore normal font
2645 dc.SetFont( m_normalFont );
ec676e4f 2646
c0d2308b
RR
2647 if (item == m_dndEffectItem)
2648 {
2649 dc.SetPen( *wxBLACK_PEN );
2650 // DnD visual effects
2651 switch (m_dndEffect)
2652 {
2653 case BorderEffect:
2654 {
2655 dc.SetBrush(*wxTRANSPARENT_BRUSH);
2656 int w = item->GetWidth() + 2;
2657 int h = total_h + 2;
2658 dc.DrawRectangle( item->GetX() - 1, item->GetY() - 1, w, h);
2659 break;
2660 }
2661 case AboveEffect:
2662 {
2663 int x = item->GetX(),
2664 y = item->GetY();
2665 dc.DrawLine( x, y, x + item->GetWidth(), y);
2666 break;
2667 }
2668 case BelowEffect:
2669 {
2670 int x = item->GetX(),
2671 y = item->GetY();
2672 y += total_h - 1;
2673 dc.DrawLine( x, y, x + item->GetWidth(), y);
2674 break;
2675 }
2676 case NoEffect:
2677 break;
2678 }
2679 }
ef44a621
VZ
2680}
2681
8239070b
VZ
2682void
2683wxGenericTreeCtrl::PaintLevel(wxGenericTreeItem *item,
2684 wxDC &dc,
2685 int level,
2686 int &y)
c801d85f 2687{
618a5e38
RR
2688 int x = level*m_indent;
2689 if (!HasFlag(wxTR_HIDE_ROOT))
2690 {
2691 x += m_indent;
2692 }
2693 else if (level == 0)
2694 {
2b84e565
VS
2695 // always expand hidden root
2696 int origY = y;
618a5e38 2697 wxArrayGenericTreeItems& children = item->GetChildren();
b4a980f4 2698 int count = children.GetCount();
2b84e565
VS
2699 if (count > 0)
2700 {
2701 int n = 0, oldY;
2702 do {
2703 oldY = y;
2704 PaintLevel(children[n], dc, 1, y);
2705 } while (++n < count);
2706
8239070b
VZ
2707 if ( !HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT)
2708 && count > 0 )
2b84e565
VS
2709 {
2710 // draw line down to last child
2711 origY += GetLineHeight(children[0])>>1;
2712 oldY += GetLineHeight(children[n-1])>>1;
2713 dc.DrawLine(3, origY, 3, oldY);
2714 }
2715 }
618a5e38
RR
2716 return;
2717 }
91b8de8d 2718
618a5e38
RR
2719 item->SetX(x+m_spacing);
2720 item->SetY(y);
389cdc7a 2721
618a5e38
RR
2722 int h = GetLineHeight(item);
2723 int y_top = y;
2724 int y_mid = y_top + (h>>1);
2725 y += h;
4832f7c0 2726
618a5e38
RR
2727 int exposed_x = dc.LogicalToDeviceX(0);
2728 int exposed_y = dc.LogicalToDeviceY(y_top);
233058c7 2729
618a5e38 2730 if (IsExposed(exposed_x, exposed_y, 10000, h)) // 10000 = very much
bbe0af5b 2731 {
f516d986 2732 const wxPen *pen =
c6f4913a
VS
2733#ifndef __WXMAC__
2734 // don't draw rect outline if we already have the
2735 // background color under Mac
2736 (item->IsSelected() && m_hasFocus) ? wxBLACK_PEN :
2737#endif // !__WXMAC__
2738 wxTRANSPARENT_PEN;
2739
2740 wxColour colText;
633ecf26 2741 if ( item->IsSelected()
530a427a 2742#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS
633ecf26
RD
2743 // On wxMac, if the tree doesn't have the focus we draw an empty
2744 // rectangle, so we want to make sure that the text is visible
2745 // against the normal background, not the highlightbackground, so
2746 // don't use the highlight text colour unless we have the focus.
ed9a7a63 2747 && m_hasFocus && IsControlActive( (ControlRef)GetHandle() )
633ecf26
RD
2748#endif
2749 )
c6f4913a 2750 {
4b8fa634
KO
2751#ifdef __WXMAC__
2752 colText = *wxWHITE;
2753#else
a756f210 2754 colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
4b8fa634 2755#endif
c6f4913a
VS
2756 }
2757 else
2758 {
2759 wxTreeItemAttr *attr = item->GetAttributes();
2760 if (attr && attr->HasTextColour())
2761 colText = attr->GetTextColour();
2762 else
6f0dd6b2 2763 colText = GetForegroundColour();
c6f4913a
VS
2764 }
2765
2766 // prepare to draw
2767 dc.SetTextForeground(colText);
2768 dc.SetPen(*pen);
2769
2770 // draw
2771 PaintItem(item, dc);
2772
2773 if (HasFlag(wxTR_ROW_LINES))
2774 {
2775 // if the background colour is white, choose a
2776 // contrasting color for the lines
2777 dc.SetPen(*((GetBackgroundColour() == *wxWHITE)
2778 ? wxMEDIUM_GREY_PEN : wxWHITE_PEN));
2779 dc.DrawLine(0, y_top, 10000, y_top);
2780 dc.DrawLine(0, y, 10000, y);
2781 }
2782
2783 // restore DC objects
2784 dc.SetBrush(*wxWHITE_BRUSH);
2785 dc.SetPen(m_dottedPen);
2786 dc.SetTextForeground(*wxBLACK);
2787
9c7f49f5 2788 if ( !HasFlag(wxTR_NO_LINES) )
cdb3cffe 2789 {
9c7f49f5
VZ
2790 // draw the horizontal line here
2791 int x_start = x;
2792 if (x > (signed)m_indent)
2793 x_start -= m_indent;
2794 else if (HasFlag(wxTR_LINES_AT_ROOT))
2795 x_start = 3;
2796 dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
2797 }
618a5e38 2798
9c7f49f5
VZ
2799 // should the item show a button?
2800 if ( item->HasPlus() && HasButtons() )
2801 {
2802 if ( m_imageListButtons )
c22886bd 2803 {
618a5e38 2804 // draw the image button here
9c7f49f5
VZ
2805 int image_h = 0,
2806 image_w = 0;
2807 int image = item->IsExpanded() ? wxTreeItemIcon_Expanded
2808 : wxTreeItemIcon_Normal;
2809 if ( item->IsSelected() )
2b84e565 2810 image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal;
9c7f49f5 2811
618a5e38 2812 m_imageListButtons->GetSize(image, image_w, image_h);
9c7f49f5
VZ
2813 int xx = x - image_w/2;
2814 int yy = y_mid - image_h/2;
2815
2816 wxDCClipper clip(dc, xx, yy, image_w, image_h);
618a5e38
RR
2817 m_imageListButtons->Draw(image, dc, xx, yy,
2818 wxIMAGELIST_DRAW_TRANSPARENT);
c22886bd 2819 }
9c7f49f5 2820 else // no custom buttons
c22886bd 2821 {
0e7761fa
VZ
2822 static const int wImage = 9;
2823 static const int hImage = 9;
ca65c044 2824
f8b043e7
RR
2825 int flag = 0;
2826 if (item->IsExpanded())
2827 flag |= wxCONTROL_EXPANDED;
2828 if (item == m_underMouse)
2829 flag |= wxCONTROL_CURRENT;
ca65c044 2830
9c7f49f5
VZ
2831 wxRendererNative::Get().DrawTreeItemButton
2832 (
2833 this,
2834 dc,
2835 wxRect(x - wImage/2,
2836 y_mid - hImage/2,
2837 wImage, hImage),
f8b043e7 2838 flag
9c7f49f5 2839 );
c22886bd 2840 }
bbe0af5b 2841 }
f135ff73 2842 }
d3a9f4af 2843
bbe0af5b
RR
2844 if (item->IsExpanded())
2845 {
91b8de8d 2846 wxArrayGenericTreeItems& children = item->GetChildren();
b4a980f4 2847 int count = children.GetCount();
f65635b5 2848 if (count > 0)
9ec64fa7 2849 {
618a5e38
RR
2850 int n = 0, oldY;
2851 ++level;
2852 do {
2853 oldY = y;
2854 PaintLevel(children[n], dc, level, y);
2855 } while (++n < count);
2856
e76520fd 2857 if (!HasFlag(wxTR_NO_LINES) && count > 0)
618a5e38
RR
2858 {
2859 // draw line down to last child
2b84e565 2860 oldY += GetLineHeight(children[n-1])>>1;
618a5e38 2861 if (HasButtons()) y_mid += 5;
dd360466 2862
8239070b
VZ
2863 // Only draw the portion of the line that is visible, in case
2864 // it is huge
ca65c044 2865 wxCoord xOrigin=0, yOrigin=0, width, height;
dd360466
RD
2866 dc.GetDeviceOrigin(&xOrigin, &yOrigin);
2867 yOrigin = abs(yOrigin);
2868 GetClientSize(&width, &height);
2869
2870 // Move end points to the begining/end of the view?
2871 if (y_mid < yOrigin)
2872 y_mid = yOrigin;
2873 if (oldY > yOrigin + height)
2874 oldY = yOrigin + height;
2875
8239070b
VZ
2876 // after the adjustments if y_mid is larger than oldY then the
2877 // line isn't visible at all so don't draw anything
dd360466
RD
2878 if (y_mid < oldY)
2879 dc.DrawLine(x, y_mid, x, oldY);
618a5e38 2880 }
9ec64fa7 2881 }
bbe0af5b 2882 }
4c681997 2883}
c801d85f 2884
941830cb 2885void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item)
3dbeaa52
VZ
2886{
2887 if ( item )
2888 {
2889 if ( item->HasPlus() )
2890 {
2891 // it's a folder, indicate it by a border
2892 DrawBorder(item);
2893 }
2894 else
2895 {
2896 // draw a line under the drop target because the item will be
2897 // dropped there
e3d64157 2898 DrawLine(item, !m_dropEffectAboveItem );
3dbeaa52
VZ
2899 }
2900
c0d2308b 2901 SetCursor(*wxSTANDARD_CURSOR);
3dbeaa52
VZ
2902 }
2903 else
2904 {
2905 // can't drop here
2906 SetCursor(wxCURSOR_NO_ENTRY);
2907 }
2908}
2909
941830cb 2910void wxGenericTreeCtrl::DrawBorder(const wxTreeItemId &item)
91b8de8d 2911{
8239070b 2912 wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" );
91b8de8d 2913
941830cb 2914 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
91b8de8d 2915
c0d2308b
RR
2916 if (m_dndEffect == NoEffect)
2917 {
2918 m_dndEffect = BorderEffect;
2919 m_dndEffectItem = i;
2920 }
2921 else
2922 {
2923 m_dndEffect = NoEffect;
2924 m_dndEffectItem = NULL;
2925 }
ec676e4f 2926
c0d2308b
RR
2927 wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 );
2928 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2929 RefreshRect( rect );
91b8de8d
RR
2930}
2931
941830cb 2932void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below)
91b8de8d 2933{
8239070b 2934 wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" );
91b8de8d 2935
941830cb 2936 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
91b8de8d 2937
c0d2308b 2938 if (m_dndEffect == NoEffect)
3dbeaa52 2939 {
c0d2308b
RR
2940 if (below)
2941 m_dndEffect = BelowEffect;
2942 else
2943 m_dndEffect = AboveEffect;
2944 m_dndEffectItem = i;
3dbeaa52 2945 }
c0d2308b
RR
2946 else
2947 {
2948 m_dndEffect = NoEffect;
2949 m_dndEffectItem = NULL;
2950 }
ec676e4f 2951
c0d2308b
RR
2952 wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 );
2953 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2954 RefreshRect( rect );
91b8de8d
RR
2955}
2956
f135ff73 2957// -----------------------------------------------------------------------------
77ffb593 2958// wxWidgets callbacks
f135ff73
VZ
2959// -----------------------------------------------------------------------------
2960
ccdbdc89
RR
2961void wxGenericTreeCtrl::OnSize( wxSizeEvent &event )
2962{
2963#ifdef __WXGTK__
2964 if (HasFlag( wxTR_FULL_ROW_HIGHLIGHT) && m_current)
2965 RefreshLine( m_current );
2966#endif
2967
2968 event.Skip(true);
2969}
2970
941830cb 2971void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
c801d85f 2972{
0659e7ee
RR
2973 wxPaintDC dc(this);
2974 PrepareDC( dc );
29d87bba 2975
941830cb
JS
2976 if ( !m_anchor)
2977 return;
2978
eff869aa 2979 dc.SetFont( m_normalFont );
0659e7ee 2980 dc.SetPen( m_dottedPen );
f38374d0 2981
eff869aa 2982 // this is now done dynamically
91b8de8d
RR
2983 //if(GetImageList() == NULL)
2984 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 2985
91b8de8d 2986 int y = 2;
0659e7ee 2987 PaintLevel( m_anchor, dc, 0, y );
edaa81ae 2988}
c801d85f 2989
b771aa29 2990void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event )
c801d85f 2991{
ca65c044 2992 m_hasFocus = true;
978f38c2 2993
b771aa29
VZ
2994 RefreshSelected();
2995
2996 event.Skip();
edaa81ae 2997}
c801d85f 2998
b771aa29 2999void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event )
c801d85f 3000{
ca65c044 3001 m_hasFocus = false;
978f38c2 3002
b771aa29
VZ
3003 RefreshSelected();
3004
3005 event.Skip();
edaa81ae 3006}
c801d85f 3007
941830cb 3008void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
c801d85f 3009{
09f277d6 3010 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, this);
b09bda68 3011 te.m_evtKey = event;
68eb5f63
VZ
3012 if ( GetEventHandler()->ProcessEvent( te ) )
3013 {
3014 // intercepted by the user code
3015 return;
3016 }
435fe83e 3017
91b8de8d 3018 if ( (m_current == 0) || (m_key_current == 0) )
978f38c2
VZ
3019 {
3020 event.Skip();
3021 return;
3022 }
ef44a621 3023
06b466c7
VZ
3024 // how should the selection work for this event?
3025 bool is_multiple, extended_select, unselect_others;
3026 EventFlagsToSelType(GetWindowStyleFlag(),
3027 event.ShiftDown(),
105fc244 3028 event.CmdDown(),
dfc6cd93
SB
3029 is_multiple, extended_select, unselect_others);
3030
03703fc0
RR
3031 if (GetLayoutDirection() == wxLayout_RightToLeft)
3032 {
3033 if (event.GetKeyCode() == WXK_RIGHT)
3034 event.m_keyCode = WXK_LEFT;
3035 else if (event.GetKeyCode() == WXK_LEFT)
3036 event.m_keyCode = WXK_RIGHT;
3037 }
3038
dfc6cd93
SB
3039 // + : Expand
3040 // - : Collaspe
f6bcfd97 3041 // * : Expand all/Collapse all
dfc6cd93
SB
3042 // ' ' | return : activate
3043 // up : go up (not last children!)
3044 // down : go down
3045 // left : go to parent
3046 // right : open if parent and go next
3047 // home : go to root
3048 // end : go to last item without opening parents
cb59313c 3049 // alnum : start or continue searching for the item with this prefix
12a3f227 3050 int keyCode = event.GetKeyCode();
cb59313c 3051 switch ( keyCode )
978f38c2
VZ
3052 {
3053 case '+':
3054 case WXK_ADD:
3055 if (m_current->HasPlus() && !IsExpanded(m_current))
3056 {
3057 Expand(m_current);
3058 }
3059 break;
ef44a621 3060
f6bcfd97
BP
3061 case '*':
3062 case WXK_MULTIPLY:
3063 if ( !IsExpanded(m_current) )
3064 {
3065 // expand all
1a51c85f 3066 ExpandAllChildren(m_current);
f6bcfd97
BP
3067 break;
3068 }
3069 //else: fall through to Collapse() it
3070
978f38c2
VZ
3071 case '-':
3072 case WXK_SUBTRACT:
3073 if (IsExpanded(m_current))
3074 {
3075 Collapse(m_current);
3076 }
3077 break;
ef44a621 3078
f7c6f947
RR
3079 case WXK_MENU:
3080 {
8239070b
VZ
3081 // Use the item's bounding rectangle to determine position for
3082 // the event
39faaf39
KH
3083 wxRect ItemRect;
3084 GetBoundingRect(m_current, ItemRect, true);
3085
8239070b
VZ
3086 wxTreeEvent
3087 eventMenu(wxEVT_COMMAND_TREE_ITEM_MENU, this, m_current);
39faaf39 3088 // Use the left edge, vertical middle
4e115ed2 3089 eventMenu.m_pointDrag = wxPoint(ItemRect.GetX(),
8239070b
VZ
3090 ItemRect.GetY() +
3091 ItemRect.GetHeight() / 2);
4e115ed2 3092 GetEventHandler()->ProcessEvent( eventMenu );
f7c6f947 3093 }
09f277d6
VZ
3094 break;
3095
978f38c2
VZ
3096 case ' ':
3097 case WXK_RETURN:
6151e144 3098 if ( !event.HasModifiers() )
978f38c2 3099 {
8239070b
VZ
3100 wxTreeEvent
3101 eventAct(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, m_current);
4e115ed2 3102 GetEventHandler()->ProcessEvent( eventAct );
978f38c2 3103 }
6151e144
VZ
3104
3105 // in any case, also generate the normal key event for this key,
3106 // even if we generated the ACTIVATED event above: this is what
3107 // wxMSW does and it makes sense because you might not want to
3108 // process ACTIVATED event at all and handle Space and Return
3109 // directly (and differently) which would be impossible otherwise
3110 event.Skip();
978f38c2 3111 break;
ef44a621 3112
618a5e38
RR
3113 // up goes to the previous sibling or to the last
3114 // of its children if it's expanded
978f38c2
VZ
3115 case WXK_UP:
3116 {
91b8de8d 3117 wxTreeItemId prev = GetPrevSibling( m_key_current );
978f38c2
VZ
3118 if (!prev)
3119 {
99006e44 3120 prev = GetItemParent( m_key_current );
618a5e38
RR
3121 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
3122 {
3123 break; // don't go to root if it is hidden
3124 }
c193b707
VZ
3125 if (prev)
3126 {
2d75caaa 3127 wxTreeItemIdValue cookie;
91b8de8d 3128 wxTreeItemId current = m_key_current;
8239070b
VZ
3129 // TODO: Huh? If we get here, we'd better be the first
3130 // child of our parent. How else could it be?
618a5e38 3131 if (current == GetFirstChild( prev, cookie ))
90e58684
RR
3132 {
3133 // otherwise we return to where we came from
8239070b
VZ
3134 DoSelectItem(prev,
3135 unselect_others,
3136 extended_select);
3137 m_key_current = (wxGenericTreeItem*) prev.m_pItem;
90e58684 3138 break;
c193b707 3139 }
978f38c2
VZ
3140 }
3141 }
3142 if (prev)
3143 {
69a282d4 3144 while ( IsExpanded(prev) && HasChildren(prev) )
978f38c2 3145 {
69a282d4
VZ
3146 wxTreeItemId child = GetLastChild(prev);
3147 if ( child )
3148 {
3149 prev = child;
3150 }
978f38c2 3151 }
69a282d4 3152
78f12104 3153 DoSelectItem( prev, unselect_others, extended_select );
941830cb 3154 m_key_current=(wxGenericTreeItem*) prev.m_pItem;
978f38c2
VZ
3155 }
3156 }
3157 break;
ef44a621 3158
978f38c2
VZ
3159 // left arrow goes to the parent
3160 case WXK_LEFT:
3161 {
99006e44 3162 wxTreeItemId prev = GetItemParent( m_current );
618a5e38
RR
3163 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
3164 {
3165 // don't go to root if it is hidden
3166 prev = GetPrevSibling( m_current );
3167 }
978f38c2
VZ
3168 if (prev)
3169 {
78f12104 3170 DoSelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
3171 }
3172 }
3173 break;
ef44a621 3174
978f38c2 3175 case WXK_RIGHT:
618a5e38
RR
3176 // this works the same as the down arrow except that we
3177 // also expand the item if it wasn't expanded yet
1457ea31 3178 if (m_current != GetRootItem().m_pItem || !HasFlag(wxTR_HIDE_ROOT))
6f7f0d0a
VZ
3179 Expand(m_current);
3180 //else: don't try to expand hidden root item (which can be the
3181 // current one when the tree is empty)
3182
978f38c2
VZ
3183 // fall through
3184
3185 case WXK_DOWN:
ef44a621 3186 {
91b8de8d 3187 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
978f38c2 3188 {
2d75caaa 3189 wxTreeItemIdValue cookie;
91b8de8d 3190 wxTreeItemId child = GetFirstChild( m_key_current, cookie );
6f7f0d0a
VZ
3191 if ( !child )
3192 break;
3193
78f12104 3194 DoSelectItem( child, unselect_others, extended_select );
941830cb 3195 m_key_current=(wxGenericTreeItem*) child.m_pItem;
978f38c2
VZ
3196 }
3197 else
3198 {
91b8de8d 3199 wxTreeItemId next = GetNextSibling( m_key_current );
b62c3631 3200 if (!next)
978f38c2 3201 {
91b8de8d 3202 wxTreeItemId current = m_key_current;
a0fad723 3203 while (current.IsOk() && !next)
978f38c2 3204 {
99006e44 3205 current = GetItemParent( current );
978f38c2
VZ
3206 if (current) next = GetNextSibling( current );
3207 }
3208 }
b62c3631 3209 if (next)
978f38c2 3210 {
78f12104 3211 DoSelectItem( next, unselect_others, extended_select );
941830cb 3212 m_key_current=(wxGenericTreeItem*) next.m_pItem;
978f38c2
VZ
3213 }
3214 }
ef44a621 3215 }
978f38c2 3216 break;
ef44a621 3217
978f38c2
VZ
3218 // <End> selects the last visible tree item
3219 case WXK_END:
3220 {
3221 wxTreeItemId last = GetRootItem();
3222
3223 while ( last.IsOk() && IsExpanded(last) )
3224 {
3225 wxTreeItemId lastChild = GetLastChild(last);
3226
3227 // it may happen if the item was expanded but then all of
3228 // its children have been deleted - so IsExpanded() returned
ca65c044 3229 // true, but GetLastChild() returned invalid item
978f38c2
VZ
3230 if ( !lastChild )
3231 break;
3232
3233 last = lastChild;
3234 }
3235
3236 if ( last.IsOk() )
3237 {
78f12104 3238 DoSelectItem( last, unselect_others, extended_select );
978f38c2
VZ
3239 }
3240 }
3241 break;
3242
3243 // <Home> selects the root item
3244 case WXK_HOME:
3245 {
3246 wxTreeItemId prev = GetRootItem();
cb59313c
VZ
3247 if (!prev)
3248 break;
3249
3250 if ( HasFlag(wxTR_HIDE_ROOT) )
978f38c2 3251 {
2d75caaa
VZ
3252 wxTreeItemIdValue cookie;
3253 prev = GetFirstChild(prev, cookie);
cb59313c
VZ
3254 if (!prev)
3255 break;
978f38c2 3256 }
cb59313c 3257
78f12104 3258 DoSelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
3259 }
3260 break;
3261
3262 default:
cb59313c 3263 // do not use wxIsalnum() here
6151e144 3264 if ( !event.HasModifiers() &&
1ec3a984
RR
3265 ((keyCode >= '0' && keyCode <= '9') ||
3266 (keyCode >= 'a' && keyCode <= 'z') ||
3267 (keyCode >= 'A' && keyCode <= 'Z' )))
cb59313c
VZ
3268 {
3269 // find the next item starting with the given prefix
bef7a62d 3270 wxChar ch = (wxChar)keyCode;
6151e144 3271
bef7a62d 3272 wxTreeItemId id = FindItem(m_current, m_findPrefix + ch);
cb59313c
VZ
3273 if ( !id.IsOk() )
3274 {
3275 // no such item
3276 break;
3277 }
3278
3279 SelectItem(id);
3280
3281 m_findPrefix += ch;
3282
3283 // also start the timer to reset the current prefix if the user
3284 // doesn't press any more alnum keys soon -- we wouldn't want
3285 // to use this prefix for a new item search
3286 if ( !m_findTimer )
3287 {
3288 m_findTimer = new wxTreeFindTimer(this);
3289 }
3290
3291 m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT);
3292 }
3293 else
3294 {
3295 event.Skip();
3296 }
978f38c2 3297 }
edaa81ae 3298}
c801d85f 3299
be0e5d69
VZ
3300wxTreeItemId
3301wxGenericTreeCtrl::DoTreeHitTest(const wxPoint& point, int& flags) const
4f22cf8d 3302{
91b8de8d
RR
3303 int w, h;
3304 GetSize(&w, &h);
91b8de8d 3305 flags=0;
618a5e38
RR
3306 if (point.x<0) flags |= wxTREE_HITTEST_TOLEFT;
3307 if (point.x>w) flags |= wxTREE_HITTEST_TORIGHT;
3308 if (point.y<0) flags |= wxTREE_HITTEST_ABOVE;
3309 if (point.y>h) flags |= wxTREE_HITTEST_BELOW;
3310 if (flags) return wxTreeItemId();
d3a9f4af 3311
618a5e38
RR
3312 if (m_anchor == NULL)
3313 {
3314 flags = wxTREE_HITTEST_NOWHERE;
3315 return wxTreeItemId();
3316 }
5716a1ab 3317
d027179b
VS
3318 wxGenericTreeItem *hit = m_anchor->HitTest(CalcUnscrolledPosition(point),
3319 this, flags, 0);
618a5e38
RR
3320 if (hit == NULL)
3321 {
3322 flags = wxTREE_HITTEST_NOWHERE;
3323 return wxTreeItemId();
3324 }
3325 return hit;
4f22cf8d
RR
3326}
3327
8fb3bfe2
JS
3328// get the bounding rectangle of the item (or of its label only)
3329bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
edb8f298 3330 wxRect& rect,
3e4f8ee2 3331 bool textOnly) const
8fb3bfe2 3332{
8239070b
VZ
3333 wxCHECK_MSG( item.IsOk(), false,
3334 "invalid item in wxGenericTreeCtrl::GetBoundingRect" );
8fb3bfe2
JS
3335
3336 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
3337
3e4f8ee2
VZ
3338 if ( textOnly )
3339 {
136b7e61
VZ
3340 int image_h = 0, image_w = 0;
3341 int image = ((wxGenericTreeItem*) item.m_pItem)->GetCurrentImage();
3342 if ( image != NO_IMAGE && m_imageListNormal )
3343 {
3344 m_imageListNormal->GetSize( image, image_w, image_h );
3345 image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
3346 }
3e4f8ee2 3347
136b7e61
VZ
3348 int state_h = 0, state_w = 0;
3349 int state = ((wxGenericTreeItem*) item.m_pItem)->GetState();
3350 if ( state != wxTREE_ITEMSTATE_NONE && m_imageListState )
3e4f8ee2 3351 {
136b7e61
VZ
3352 m_imageListState->GetSize( state, state_w, state_h );
3353 if ( image_w != 0 )
3354 state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
3355 else
3356 state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
3e4f8ee2 3357 }
136b7e61
VZ
3358
3359 rect.x = i->GetX() + state_w + image_w;
3360 rect.width = i->GetWidth() - state_w - image_w;
3361
3e4f8ee2
VZ
3362 }
3363 else // the entire line
3364 {
3365 rect.x = 0;
3366 rect.width = GetClientSize().x;
3367 }
3368
402dfce7 3369 rect.y = i->GetY();
c22886bd 3370 rect.height = GetLineHeight(i);
8fb3bfe2 3371
402dfce7 3372 // we have to return the logical coordinates, not physical ones
619297ab 3373 rect.SetTopLeft(CalcScrolledPosition(rect.GetTopLeft()));
402dfce7 3374
ca65c044 3375 return true;
8fb3bfe2
JS
3376}
3377
8cee4a30
VZ
3378wxTextCtrl *wxGenericTreeCtrl::EditLabel(const wxTreeItemId& item,
3379 wxClassInfo * WXUNUSED(textCtrlClass))
e179bd65 3380{
8cee4a30 3381 wxCHECK_MSG( item.IsOk(), NULL, _T("can't edit an invalid item") );
e179bd65 3382
edb8f298 3383 wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem;
9dfbf520 3384
09f277d6 3385 wxTreeEvent te(wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, this, itemEdit);
edb8f298
VZ
3386 if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() )
3387 {
3388 // vetoed by user
8cee4a30 3389 return NULL;
edb8f298 3390 }
8dc99046 3391
dc6c62a9
RR
3392 // We have to call this here because the label in
3393 // question might just have been added and no screen
3394 // update taken place.
edb8f298 3395 if ( m_dirty )
f89d65ea
SC
3396#if defined( __WXMSW__ ) || defined(__WXMAC__)
3397 Update();
3398#else
27f8357f 3399 DoDirtyProcessing();
f89d65ea 3400#endif
9dfbf520 3401
8cee4a30 3402 // TODO: use textCtrlClass here to create the control of correct class
fbb12260 3403 m_textCtrl = new wxTreeTextCtrl(this, itemEdit);
8dc99046 3404
fbb12260 3405 m_textCtrl->SetFocus();
8cee4a30
VZ
3406
3407 return m_textCtrl;
fbb12260
JS
3408}
3409
3410// returns a pointer to the text edit control if the item is being
3411// edited, NULL otherwise (it's assumed that no more than one item may
3412// be edited simultaneously)
3413wxTextCtrl* wxGenericTreeCtrl::GetEditControl() const
3414{
3415 return m_textCtrl;
e179bd65
RR
3416}
3417
8cee4a30
VZ
3418void wxGenericTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item),
3419 bool discardChanges)
3420{
3421 wxCHECK_RET( m_textCtrl, _T("not editing label") );
3422
3423 m_textCtrl->EndEdit(discardChanges);
3424}
3425
edb8f298
VZ
3426bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item,
3427 const wxString& value)
e179bd65 3428{
09f277d6 3429 wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item);
edb8f298 3430 le.m_label = value;
ca65c044 3431 le.m_editCancelled = false;
9dfbf520 3432
edb8f298
VZ
3433 return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
3434}
9dfbf520 3435
dd23c25c
JS
3436void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item)
3437{
3438 // let owner know that the edit was cancelled
09f277d6 3439 wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item);
dd23c25c 3440 le.m_label = wxEmptyString;
ca65c044 3441 le.m_editCancelled = true;
dd23c25c
JS
3442
3443 GetEventHandler()->ProcessEvent( le );
3444}
3445
edb8f298
VZ
3446void wxGenericTreeCtrl::OnRenameTimer()
3447{
642446e3 3448 EditLabel( m_current );
e179bd65 3449}
9dfbf520 3450
941830cb 3451void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
c801d85f 3452{
09f277d6 3453 if ( !m_anchor )return;
978f38c2 3454
f8b043e7 3455 wxPoint pt = CalcUnscrolledPosition(event.GetPosition());
ca65c044 3456
f8b043e7
RR
3457 // Is the mouse over a tree item button?
3458 int flags = 0;
73bb6776
JS
3459 wxGenericTreeItem *thisItem = m_anchor->HitTest(pt, this, flags, 0);
3460 wxGenericTreeItem *underMouse = thisItem;
0efd5732 3461#if wxUSE_TOOLTIPS
73bb6776 3462 bool underMouseChanged = (underMouse != m_underMouse) ;
0efd5732 3463#endif // wxUSE_TOOLTIPS
73bb6776 3464
f8b043e7
RR
3465 if ((underMouse) &&
3466 (flags & wxTREE_HITTEST_ONITEMBUTTON) &&
3467 (!event.LeftIsDown()) &&
ca65c044 3468 (!m_isDragging) &&
f8b043e7
RR
3469 (!m_renameTimer || !m_renameTimer->IsRunning()))
3470 {
3471 }
3472 else
3473 {
3474 underMouse = NULL;
3475 }
ca65c044 3476
f8b043e7
RR
3477 if (underMouse != m_underMouse)
3478 {
3479 if (m_underMouse)
3480 {
3481 // unhighlight old item
3482 wxGenericTreeItem *tmp = m_underMouse;
3483 m_underMouse = NULL;
3484 RefreshLine( tmp );
3485 }
ca65c044 3486
f8b043e7
RR
3487 m_underMouse = underMouse;
3488 if (m_underMouse)
3489 RefreshLine( m_underMouse );
3490 }
3491
5b5ea466 3492#if wxUSE_TOOLTIPS
4a5d781c 3493 // Determines what item we are hovering over and need a tooltip for
73bb6776 3494 wxTreeItemId hoverItem = thisItem;
4a5d781c 3495
8239070b
VZ
3496 // We do not want a tooltip if we are dragging, or if the rename timer is
3497 // running
3498 if ( underMouseChanged &&
3499 hoverItem.IsOk() &&
3500 !m_isDragging &&
3501 (!m_renameTimer || !m_renameTimer->IsRunning()) )
4a5d781c
JS
3502 {
3503 // Ask the tree control what tooltip (if any) should be shown
8239070b
VZ
3504 wxTreeEvent
3505 hevent(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, this, hoverItem);
4a5d781c
JS
3506
3507 if ( GetEventHandler()->ProcessEvent(hevent) && hevent.IsAllowed() )
3508 {
3509 SetToolTip(hevent.m_label);
3510 }
3511 }
5b5ea466 3512#endif
ca65c044 3513
da87911d 3514 // we process left mouse up event (enables in-place edit), middle/right down
3dbeaa52
VZ
3515 // (pass to the user code), left dbl click (activate item) and
3516 // dragging/moving events for items drag-and-drop
f6bcfd97
BP
3517 if ( !(event.LeftDown() ||
3518 event.LeftUp() ||
da87911d 3519 event.MiddleDown() ||
3dbeaa52
VZ
3520 event.RightDown() ||
3521 event.LeftDClick() ||
3522 event.Dragging() ||
3523 ((event.Moving() || event.RightUp()) && m_isDragging)) )
3524 {
3525 event.Skip();
3526
3527 return;
3528 }
3529
29d87bba 3530
f8b043e7 3531 flags = 0;
d027179b 3532 wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0);
3dbeaa52 3533
3dbeaa52 3534 if ( event.Dragging() && !m_isDragging )
bbe0af5b 3535 {
fd9811b1 3536 if (m_dragCount == 0)
d027179b 3537 m_dragStart = pt;
8dc99046 3538
fd9811b1 3539 m_dragCount++;
8dc99046 3540
3dbeaa52
VZ
3541 if (m_dragCount != 3)
3542 {
3543 // wait until user drags a bit further...
3544 return;
3545 }
8dc99046 3546
3dbeaa52
VZ
3547 wxEventType command = event.RightIsDown()
3548 ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
3549 : wxEVT_COMMAND_TREE_BEGIN_DRAG;
8dc99046 3550
09f277d6 3551 wxTreeEvent nevent(command, this, m_current);
99411465 3552 nevent.SetPoint(CalcScrolledPosition(pt));
3dbeaa52
VZ
3553
3554 // by default the dragging is not supported, the user code must
3555 // explicitly allow the event for it to take place
3556 nevent.Veto();
3557
3558 if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() )
3559 {
3560 // we're going to drag this item
ca65c044 3561 m_isDragging = true;
3dbeaa52 3562
b3a7510d
VZ
3563 // remember the old cursor because we will change it while
3564 // dragging
3565 m_oldCursor = m_cursor;
a6fb8636 3566
b3a7510d
VZ
3567 // in a single selection control, hide the selection temporarily
3568 if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
3569 {
941830cb 3570 m_oldSelection = (wxGenericTreeItem*) GetSelection().m_pItem;
b3a7510d
VZ
3571
3572 if ( m_oldSelection )
3573 {
ca65c044 3574 m_oldSelection->SetHilight(false);
b3a7510d
VZ
3575 RefreshLine(m_oldSelection);
3576 }
3577 }
3578
3dbeaa52
VZ
3579 CaptureMouse();
3580 }
bbe0af5b 3581 }
daa7ae0c 3582 else if ( event.Dragging() )
fd9811b1 3583 {
3dbeaa52
VZ
3584 if ( item != m_dropTarget )
3585 {
3586 // unhighlight the previous drop target
3587 DrawDropEffect(m_dropTarget);
fd9811b1 3588
3dbeaa52 3589 m_dropTarget = item;
978f38c2 3590
3dbeaa52
VZ
3591 // highlight the current drop target if any
3592 DrawDropEffect(m_dropTarget);
fb882e1c 3593
daa7ae0c 3594#if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXGTK20__)
f89d65ea
SC
3595 Update();
3596#else
977a41ec
FM
3597 // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI)
3598 // instead (needs to be tested!)
cd66f45b 3599 wxYieldIfNeeded();
f89d65ea 3600#endif
3dbeaa52 3601 }
e179bd65 3602 }
3dbeaa52
VZ
3603 else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
3604 {
f0bc6afb
RR
3605 ReleaseMouse();
3606
3dbeaa52
VZ
3607 // erase the highlighting
3608 DrawDropEffect(m_dropTarget);
9dfbf520 3609
4f5fffcc
RR
3610 if ( m_oldSelection )
3611 {
ca65c044 3612 m_oldSelection->SetHilight(true);
4f5fffcc 3613 RefreshLine(m_oldSelection);
d3b9f782 3614 m_oldSelection = NULL;
4f5fffcc
RR
3615 }
3616
3dbeaa52 3617 // generate the drag end event
09f277d6 3618 wxTreeEvent eventEndDrag(wxEVT_COMMAND_TREE_END_DRAG, this, item);
88ac883a 3619
99411465 3620 eventEndDrag.m_pointDrag = CalcScrolledPosition(pt);
91b8de8d 3621
4e115ed2 3622 (void)GetEventHandler()->ProcessEvent(eventEndDrag);
29d87bba 3623
ca65c044 3624 m_isDragging = false;
d3b9f782 3625 m_dropTarget = NULL;
3dbeaa52 3626
b3a7510d 3627 SetCursor(m_oldCursor);
3dbeaa52 3628
977a41ec 3629#if defined( __WXMSW__ ) || defined(__WXMAC__) || defined(__WXGTK20__)
f89d65ea
SC
3630 Update();
3631#else
977a41ec
FM
3632 // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI)
3633 // instead (needs to be tested!)
cd66f45b 3634 wxYieldIfNeeded();
f89d65ea 3635#endif
3dbeaa52
VZ
3636 }
3637 else
bbe0af5b 3638 {
e4899fd7 3639 // If we got to this point, we are not dragging or moving the mouse.
8239070b
VZ
3640 // Because the code in carbon/toplevel.cpp will only set focus to the
3641 // tree if we skip for EVT_LEFT_DOWN, we MUST skip this event here for
3642 // focus to work.
e4899fd7 3643 // We skip even if we didn't hit an item because we still should
8239070b
VZ
3644 // restore focus to the tree control even if we didn't exactly hit an
3645 // item.
e4899fd7
KH
3646 if ( event.LeftDown() )
3647 {
3648 event.Skip();
3649 }
3650
3dbeaa52
VZ
3651 // here we process only the messages which happen on tree items
3652
3653 m_dragCount = 0;
3654
3655 if (item == NULL) return; /* we hit the blank area */
3656
3657 if ( event.RightDown() )
3658 {
e8cf9a5f 3659 // If the item is already selected, do not update the selection.
8239070b
VZ
3660 // Multi-selections should not be cleared if a selected item is
3661 // clicked.
e8cf9a5f
KH
3662 if (!IsSelected(item))
3663 {
3664 DoSelectItem(item, true, false);
3665 }
3666
8239070b
VZ
3667 wxTreeEvent
3668 nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, this, item);
d027179b 3669 nevent.m_pointDrag = CalcScrolledPosition(pt);
ac103441 3670 event.Skip(!GetEventHandler()->ProcessEvent(nevent));
39faaf39
KH
3671
3672 // Consistent with MSW (for now), send the ITEM_MENU *after*
3673 // the RIGHT_CLICK event. TODO: This behavior may change.
09f277d6 3674 wxTreeEvent nevent2(wxEVT_COMMAND_TREE_ITEM_MENU, this, item);
39faaf39 3675 nevent2.m_pointDrag = CalcScrolledPosition(pt);
39faaf39 3676 GetEventHandler()->ProcessEvent(nevent2);
3dbeaa52 3677 }
da87911d
VZ
3678 else if ( event.MiddleDown() )
3679 {
8239070b
VZ
3680 wxTreeEvent
3681 nevent(wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, this, item);
da87911d
VZ
3682 nevent.m_pointDrag = CalcScrolledPosition(pt);
3683 event.Skip(!GetEventHandler()->ProcessEvent(nevent));
3684 }
80d2803f 3685 else if ( event.LeftUp() )
f6bcfd97 3686 {
03966fcb
RR
3687 if (flags & wxTREE_HITTEST_ONITEMSTATEICON)
3688 {
8239070b
VZ
3689 wxTreeEvent
3690 nevent(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, this, item);
03966fcb
RR
3691 GetEventHandler()->ProcessEvent(nevent);
3692 }
3693
35cf1ec6
VZ
3694 // this facilitates multiple-item drag-and-drop
3695
902725ee 3696 if ( /* item && */ HasFlag(wxTR_MULTIPLE))
35cf1ec6
VZ
3697 {
3698 wxArrayTreeItemIds selections;
3699 size_t count = GetSelections(selections);
3700
3701 if (count > 1 &&
105fc244 3702 !event.CmdDown() &&
35cf1ec6
VZ
3703 !event.ShiftDown())
3704 {
78f12104 3705 DoSelectItem(item, true, false);
35cf1ec6
VZ
3706 }
3707 }
3708
80d2803f 3709 if ( m_lastOnSame )
f6bcfd97 3710 {
80d2803f
VZ
3711 if ( (item == m_current) &&
3712 (flags & wxTREE_HITTEST_ONITEMLABEL) &&
3713 HasFlag(wxTR_EDIT_LABELS) )
3714 {
cb59313c
VZ
3715 if ( m_renameTimer )
3716 {
3717 if ( m_renameTimer->IsRunning() )
3718 m_renameTimer->Stop();
3719 }
3720 else
3721 {
3722 m_renameTimer = new wxTreeRenameTimer( this );
3723 }
bdb310a7 3724
ca65c044 3725 m_renameTimer->Start( wxTreeRenameTimer::DELAY, true );
80d2803f 3726 }
f6bcfd97 3727
ca65c044 3728 m_lastOnSame = false;
80d2803f 3729 }
3dbeaa52 3730 }
8239070b 3731 else // !RightDown() && !MiddleDown() && !LeftUp()
3dbeaa52 3732 {
8239070b 3733 // ==> LeftDown() || LeftDClick()
f6bcfd97
BP
3734 if ( event.LeftDown() )
3735 {
3736 m_lastOnSame = item == m_current;
3737 }
3738
0326c494
VZ
3739 if ( flags & wxTREE_HITTEST_ONITEMBUTTON )
3740 {
3741 // only toggle the item for a single click, double click on
3742 // the button doesn't do anything (it toggles the item twice)
3743 if ( event.LeftDown() )
3744 {
3745 Toggle( item );
3746 }
3747
3748 // don't select the item if the button was clicked
3749 return;
3750 }
3751
3dbeaa52 3752
35cf1ec6
VZ
3753 // clear the previously selected items, if the
3754 // user clicked outside of the present selection.
3755 // otherwise, perform the deselection on mouse-up.
3756 // this allows multiple drag and drop to work.
105fc244
JS
3757 // but if Cmd is down, toggle selection of the clicked item
3758 if (!IsSelected(item) || event.CmdDown())
35cf1ec6
VZ
3759 {
3760 // how should the selection work for this event?
3761 bool is_multiple, extended_select, unselect_others;
3762 EventFlagsToSelType(GetWindowStyleFlag(),
3763 event.ShiftDown(),
105fc244 3764 event.CmdDown(),
8239070b
VZ
3765 is_multiple,
3766 extended_select,
3767 unselect_others);
35cf1ec6 3768
78f12104 3769 DoSelectItem(item, unselect_others, extended_select);
35cf1ec6
VZ
3770 }
3771
3dbeaa52 3772
618a5e38
RR
3773 // For some reason, Windows isn't recognizing a left double-click,
3774 // so we need to simulate it here. Allow 200 milliseconds for now.
3dbeaa52
VZ
3775 if ( event.LeftDClick() )
3776 {
f6bcfd97 3777 // double clicking should not start editing the item label
cb59313c
VZ
3778 if ( m_renameTimer )
3779 m_renameTimer->Stop();
3780
ca65c044 3781 m_lastOnSame = false;
f6bcfd97 3782
ebb987b7 3783 // send activate event first
8239070b
VZ
3784 wxTreeEvent
3785 nevent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, item);
d027179b 3786 nevent.m_pointDrag = CalcScrolledPosition(pt);
ebb987b7 3787 if ( !GetEventHandler()->ProcessEvent( nevent ) )
618a5e38 3788 {
ebb987b7
VZ
3789 // if the user code didn't process the activate event,
3790 // handle it ourselves by toggling the item when it is
3791 // double clicked
3792 if ( item->HasPlus() )
3793 {
3794 Toggle(item);
3795 }
618a5e38 3796 }
3dbeaa52
VZ
3797 }
3798 }
bbe0af5b 3799 }
edaa81ae 3800}
c801d85f 3801
5180055b 3802void wxGenericTreeCtrl::OnInternalIdle()
3db7be80 3803{
5180055b 3804 wxWindow::OnInternalIdle();
ca65c044 3805
3e3a7b97
JS
3806 // Check if we need to select the root item
3807 // because nothing else has been selected.
3808 // Delaying it means that we can invoke event handlers
3809 // as required, when a first item is selected.
3810 if (!HasFlag(wxTR_MULTIPLE) && !GetSelection().IsOk())
3811 {
3812 if (m_select_me)
3813 SelectItem(m_select_me);
3814 else if (GetRootItem().IsOk())
3815 SelectItem(GetRootItem());
3816 }
3817
27f8357f
VZ
3818 // after all changes have been done to the tree control,
3819 // actually redraw the tree when everything is over
3820 if (m_dirty)
3821 DoDirtyProcessing();
3db7be80
RR
3822}
3823
8239070b
VZ
3824void
3825wxGenericTreeCtrl::CalculateLevel(wxGenericTreeItem *item,
3826 wxDC &dc,
3827 int level,
3828 int &y )
c801d85f 3829{
618a5e38
RR
3830 int x = level*m_indent;
3831 if (!HasFlag(wxTR_HIDE_ROOT))
3832 {
3833 x += m_indent;
3834 }
3835 else if (level == 0)
3836 {
3837 // a hidden root is not evaluated, but its
3838 // children are always calculated
3839 goto Recurse;
3840 }
389cdc7a 3841
cac09ce8 3842 item->CalculateSize(this, dc);
d3a9f4af 3843
91b8de8d 3844 // set its position
618a5e38 3845 item->SetX( x+m_spacing );
91b8de8d 3846 item->SetY( y );
618a5e38 3847 y += GetLineHeight(item);
4c681997 3848
bbe0af5b
RR
3849 if ( !item->IsExpanded() )
3850 {
618a5e38 3851 // we don't need to calculate collapsed branches
bbe0af5b
RR
3852 return;
3853 }
389cdc7a 3854
618a5e38 3855 Recurse:
91b8de8d 3856 wxArrayGenericTreeItems& children = item->GetChildren();
b4a980f4 3857 size_t n, count = children.GetCount();
618a5e38 3858 ++level;
91b8de8d 3859 for (n = 0; n < count; ++n )
618a5e38 3860 CalculateLevel( children[n], dc, level, y ); // recurse
edaa81ae 3861}
c801d85f 3862
941830cb 3863void wxGenericTreeCtrl::CalculatePositions()
c801d85f 3864{
bbe0af5b 3865 if ( !m_anchor ) return;
29d87bba 3866
bbe0af5b
RR
3867 wxClientDC dc(this);
3868 PrepareDC( dc );
29d87bba 3869
eff869aa 3870 dc.SetFont( m_normalFont );
29d87bba 3871
bbe0af5b 3872 dc.SetPen( m_dottedPen );
91b8de8d
RR
3873 //if(GetImageList() == NULL)
3874 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 3875
8dc99046 3876 int y = 2;
f65635b5 3877 CalculateLevel( m_anchor, dc, 0, y ); // start recursion
edaa81ae 3878}
c801d85f 3879
3e6e5147
VZ
3880void wxGenericTreeCtrl::Refresh(bool eraseBackground, const wxRect *rect)
3881{
17808a75 3882 if ( !IsFrozen() )
3e6e5147
VZ
3883 wxTreeCtrlBase::Refresh(eraseBackground, rect);
3884}
3885
941830cb 3886void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
c801d85f 3887{
17808a75 3888 if (m_dirty || IsFrozen() )
3e6e5147 3889 return;
e6527f9d 3890
d027179b 3891 wxSize client = GetClientSize();
4832f7c0 3892
bbe0af5b 3893 wxRect rect;
5941c5de 3894 CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
d027179b
VS
3895 rect.width = client.x;
3896 rect.height = client.y;
f135ff73 3897
ca65c044 3898 Refresh(true, &rect);
f135ff73 3899
bbe0af5b 3900 AdjustMyScrollbars();
edaa81ae 3901}
c801d85f 3902
941830cb 3903void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
c801d85f 3904{
17808a75 3905 if (m_dirty || IsFrozen() )
3e6e5147 3906 return;
e6527f9d 3907
bbe0af5b 3908 wxRect rect;
5941c5de 3909 CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
d027179b 3910 rect.width = GetClientSize().x;
91b8de8d 3911 rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
978f38c2 3912
ca65c044 3913 Refresh(true, &rect);
edaa81ae 3914}
c801d85f 3915
b771aa29
VZ
3916void wxGenericTreeCtrl::RefreshSelected()
3917{
17808a75 3918 if (IsFrozen())
3e6e5147 3919 return;
ca65c044 3920
b771aa29
VZ
3921 // TODO: this is awfully inefficient, we should keep the list of all
3922 // selected items internally, should be much faster
3923 if ( m_anchor )
3924 RefreshSelectedUnder(m_anchor);
3925}
3926
3927void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item)
3928{
17808a75 3929 if (IsFrozen())
3e6e5147 3930 return;
ca65c044 3931
b771aa29
VZ
3932 if ( item->IsSelected() )
3933 RefreshLine(item);
3934
3935 const wxArrayGenericTreeItems& children = item->GetChildren();
3936 size_t count = children.GetCount();
3937 for ( size_t n = 0; n < count; n++ )
3938 {
3939 RefreshSelectedUnder(children[n]);
3940 }
3941}
3942
17808a75 3943void wxGenericTreeCtrl::DoThaw()
e1983ab5 3944{
ad1c5f45
VS
3945 wxTreeCtrlBase::DoThaw();
3946
17808a75
VZ
3947 if ( m_dirty )
3948 DoDirtyProcessing();
3949 else
3950 Refresh();
e1983ab5
VZ
3951}
3952
7009f411
VZ
3953// ----------------------------------------------------------------------------
3954// changing colours: we need to refresh the tree control
3955// ----------------------------------------------------------------------------
3956
3957bool wxGenericTreeCtrl::SetBackgroundColour(const wxColour& colour)
3958{
3959 if ( !wxWindow::SetBackgroundColour(colour) )
ca65c044
WS
3960 return false;
3961
7009f411
VZ
3962 Refresh();
3963
ca65c044 3964 return true;
7009f411
VZ
3965}
3966
0800a4ba 3967bool wxGenericTreeCtrl::SetForegroundColour(const wxColour& colour)
7009f411
VZ
3968{
3969 if ( !wxWindow::SetForegroundColour(colour) )
ca65c044
WS
3970 return false;
3971
7009f411
VZ
3972 Refresh();
3973
ca65c044 3974 return true;
7009f411
VZ
3975}
3976
73bb6776
JS
3977// Process the tooltip event, to speed up event processing.
3978// Doesn't actually get a tooltip.
3979void wxGenericTreeCtrl::OnGetToolTip( wxTreeEvent &event )
3980{
3981 event.Veto();
3982}
3983
35d4c967
RD
3984
3985// NOTE: If using the wxListBox visual attributes works everywhere then this can
3986// be removed, as well as the #else case below.
3987#define _USE_VISATTR 0
3988
35d4c967
RD
3989//static
3990wxVisualAttributes
3f927d50 3991#if _USE_VISATTR
35d4c967 3992wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
3f927d50
DS
3993#else
3994wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
3995#endif
35d4c967
RD
3996{
3997#if _USE_VISATTR
3998 // Use the same color scheme as wxListBox
3999 return wxListBox::GetClassDefaultAttributes(variant);
4000#else
4001 wxVisualAttributes attr;
9f2968ad 4002 attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT);
35d4c967
RD
4003 attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX);
4004 attr.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
4005 return attr;
4006#endif
4007}
4008
27f8357f
VZ
4009void wxGenericTreeCtrl::DoDirtyProcessing()
4010{
17808a75 4011 if (IsFrozen())
27f8357f
VZ
4012 return;
4013
4014 m_dirty = false;
4015
4016 CalculatePositions();
4017 Refresh();
4018 AdjustMyScrollbars();
4019}
4020
54a4121a
VZ
4021wxSize wxGenericTreeCtrl::DoGetBestSize() const
4022{
1f640c45
VZ
4023 // make sure all positions are calculated as normally this only done during
4024 // idle time but we need them for base class DoGetBestSize() to return the
4025 // correct result
4026 wxConstCast(this, wxGenericTreeCtrl)->CalculatePositions();
4027
54a4121a
VZ
4028 wxSize size = wxTreeCtrlBase::DoGetBestSize();
4029
b9643cd6
VZ
4030 // there seems to be an implicit extra border around the items, although
4031 // I'm not really sure where does it come from -- but without this, the
4032 // scrollbars appear in a tree with default/best size
54a4121a
VZ
4033 size.IncBy(4, 4);
4034
1f640c45
VZ
4035 // and the border has to be rounded up to a multiple of PIXELS_PER_UNIT or
4036 // scrollbars still appear
4037 const wxSize& borderSize = GetWindowBorderSize();
4038
4039 int dx = (size.x - borderSize.x) % PIXELS_PER_UNIT;
4040 if ( dx )
4041 size.x += PIXELS_PER_UNIT - dx;
4042 int dy = (size.y - borderSize.y) % PIXELS_PER_UNIT;
4043 if ( dy )
4044 size.y += PIXELS_PER_UNIT - dy;
4045
4046 // we need to update the cache too as the base class cached its own value
4047 CacheBestSize(size);
b9643cd6 4048
54a4121a
VZ
4049 return size;
4050}
4051
1e6feb95 4052#endif // wxUSE_TREECTRL