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