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