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