]> git.saurik.com Git - wxWidgets.git/blame - src/generic/treectrl.cpp
Hashtable now supports negative keys
[wxWidgets.git] / src / generic / treectrl.cpp
CommitLineData
c801d85f
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: treectrl.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__
f135ff73 21 #pragma implementation "treectrl.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__
28#pragma hdrstop
29#endif
30
f98e2558 31#include "wx/treectrl.h"
f60d0f94 32#include "wx/generic/imaglist.h"
c801d85f 33#include "wx/settings.h"
389cdc7a 34#include "wx/log.h"
f135ff73
VZ
35#include "wx/intl.h"
36#include "wx/dynarray.h"
91b8de8d 37#include "wx/arrimpl.cpp"
f135ff73 38#include "wx/dcclient.h"
0659e7ee 39#include "wx/msgdlg.h"
c801d85f 40
f135ff73
VZ
41// -----------------------------------------------------------------------------
42// array types
43// -----------------------------------------------------------------------------
c801d85f 44
1e6d9499
JS
45class WXDLLEXPORT wxGenericTreeItem;
46
91b8de8d
RR
47WX_DEFINE_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
48WX_DEFINE_OBJARRAY(wxArrayTreeItemIds);
c801d85f 49
8dc99046
VZ
50// ----------------------------------------------------------------------------
51// constants
52// ----------------------------------------------------------------------------
53
54static const int NO_IMAGE = -1;
55
f135ff73
VZ
56// -----------------------------------------------------------------------------
57// private classes
58// -----------------------------------------------------------------------------
59
60// a tree item
61class WXDLLEXPORT wxGenericTreeItem
c801d85f 62{
f135ff73 63public:
9ec64fa7
VZ
64 // ctors & dtor
65 wxGenericTreeItem() { m_data = NULL; }
66 wxGenericTreeItem( wxGenericTreeItem *parent,
67 const wxString& text,
68 wxDC& dc,
69 int image, int selImage,
70 wxTreeItemData *data );
f135ff73 71
9ec64fa7 72 ~wxGenericTreeItem();
f135ff73 73
9ec64fa7
VZ
74 // trivial accessors
75 wxArrayGenericTreeItems& GetChildren() { return m_children; }
f135ff73 76
9ec64fa7
VZ
77 const wxString& GetText() const { return m_text; }
78 int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const
8dc99046 79 { return m_images[which]; }
9ec64fa7 80 wxTreeItemData *GetData() const { return m_data; }
f135ff73 81
9ec64fa7
VZ
82 // returns the current image for the item (depending on its
83 // selected/expanded/whatever state)
84 int GetCurrentImage() const;
8dc99046 85
9ec64fa7
VZ
86 void SetText( const wxString &text );
87 void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; }
88 void SetData(wxTreeItemData *data) { m_data = data; }
f135ff73 89
9ec64fa7 90 void SetHasPlus(bool has = TRUE) { m_hasPlus = has; }
f135ff73 91
9ec64fa7 92 void SetBold(bool bold) { m_isBold = bold; }
ef44a621 93
9ec64fa7
VZ
94 int GetX() const { return m_x; }
95 int GetY() const { return m_y; }
f135ff73 96
9ec64fa7
VZ
97 void SetX(int x) { m_x = x; }
98 void SetY(int y) { m_y = y; }
f135ff73 99
9ec64fa7
VZ
100 int GetHeight() const { return m_height; }
101 int GetWidth() const { return m_width; }
91b8de8d 102
9ec64fa7
VZ
103 void SetHeight(int h) { m_height = h; }
104 void SetWidth(int w) { m_width = w; }
91b8de8d
RR
105
106
9ec64fa7 107 wxGenericTreeItem *GetParent() const { return m_parent; }
c801d85f 108
9ec64fa7
VZ
109 // operations
110 // deletes all children notifying the treectrl about it if !NULL
111 // pointer given
112 void DeleteChildren(wxTreeCtrl *tree = NULL);
113 // FIXME don't know what is it for
114 void Reset();
f135ff73 115
9ec64fa7
VZ
116 // get count of all children (and grand children if 'recursively')
117 size_t GetChildrenCount(bool recursively = TRUE) const;
f135ff73 118
9ec64fa7 119 void Insert(wxGenericTreeItem *child, size_t index)
f135ff73
VZ
120 { m_children.Insert(child, index); }
121
9ec64fa7
VZ
122 void SetCross( int x, int y );
123 void GetSize( int &x, int &y, const wxTreeCtrl* );
f135ff73 124
9ec64fa7
VZ
125 // return the item at given position (or NULL if no item), onButton is
126 // TRUE if the point belongs to the item's button, otherwise it lies
127 // on the button's label
128 wxGenericTreeItem *HitTest( const wxPoint& point, const wxTreeCtrl *, int &flags);
f135ff73 129
9ec64fa7
VZ
130 void Expand() { m_isCollapsed = FALSE; }
131 void Collapse() { m_isCollapsed = TRUE; }
f135ff73 132
9ec64fa7 133 void SetHilight( bool set = TRUE ) { m_hasHilight = set; }
f135ff73 134
9ec64fa7
VZ
135 // status inquiries
136 bool HasChildren() const { return !m_children.IsEmpty(); }
137 bool IsSelected() const { return m_hasHilight; }
138 bool IsExpanded() const { return !m_isCollapsed; }
139 bool HasPlus() const { return m_hasPlus || HasChildren(); }
140 bool IsBold() const { return m_isBold; }
141
142 // attributes
143 // get them - may be NULL
144 wxTreeItemAttr *GetAttributes() const { return m_attr; }
145 // get them ensuring that the pointer is not NULL
146 wxTreeItemAttr& Attr()
147 {
148 if ( !m_attr )
149 m_attr = new wxTreeItemAttr;
150
151 return *m_attr;
152 }
f135ff73
VZ
153
154private:
9ec64fa7
VZ
155 wxString m_text;
156
157 // tree ctrl images for the normal, selected, expanded and
158 // expanded+selected states
159 int m_images[wxTreeItemIcon_Max];
160
161 wxTreeItemData *m_data;
162
163 // use bitfields to save size
164 int m_isCollapsed :1;
165 int m_hasHilight :1; // same as focused
166 int m_hasPlus :1; // used for item which doesn't have
167 // children but has a [+] button
168 int m_isBold :1; // render the label in bold font
169
6f2a55e3
VZ
170 wxCoord m_x, m_y;
171 wxCoord m_height, m_width;
9ec64fa7
VZ
172 int m_xCross, m_yCross;
173 int m_level;
174
175 wxArrayGenericTreeItems m_children;
176 wxGenericTreeItem *m_parent;
177
178 wxTreeItemAttr *m_attr;
f135ff73
VZ
179};
180
181// =============================================================================
182// implementation
183// =============================================================================
184
06b466c7
VZ
185// ----------------------------------------------------------------------------
186// private functions
187// ----------------------------------------------------------------------------
188
189// translate the key or mouse event flags to the type of selection we're
190// dealing with
191static void EventFlagsToSelType(long style,
192 bool shiftDown,
193 bool ctrlDown,
194 bool *is_multiple,
195 bool *extended_select,
196 bool *unselect_others)
197{
198 *is_multiple = (style & wxTR_MULTIPLE) != 0;
199 *extended_select = shiftDown && is_multiple;
200 *unselect_others = !(extended_select || (ctrlDown && is_multiple));
201}
e179bd65
RR
202
203// -----------------------------------------------------------------------------
204// wxTreeRenameTimer (internal)
205// -----------------------------------------------------------------------------
206
207wxTreeRenameTimer::wxTreeRenameTimer( wxTreeCtrl *owner )
208{
209 m_owner = owner;
210}
211
212void wxTreeRenameTimer::Notify()
213{
214 m_owner->OnRenameTimer();
215}
216
217//-----------------------------------------------------------------------------
218// wxTreeTextCtrl (internal)
219//-----------------------------------------------------------------------------
220
221IMPLEMENT_DYNAMIC_CLASS(wxTreeTextCtrl,wxTextCtrl);
222
223BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl)
224 EVT_CHAR (wxTreeTextCtrl::OnChar)
225 EVT_KILL_FOCUS (wxTreeTextCtrl::OnKillFocus)
226END_EVENT_TABLE()
227
674ac8b9
VZ
228wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent,
229 const wxWindowID id,
230 bool *accept,
231 wxString *res,
232 wxTreeCtrl *owner,
233 const wxString &value,
234 const wxPoint &pos,
235 const wxSize &size,
236 int style,
237 const wxValidator& validator,
238 const wxString &name )
239 : wxTextCtrl( parent, id, value, pos, size, style, validator, name )
e179bd65
RR
240{
241 m_res = res;
242 m_accept = accept;
243 m_owner = owner;
5f1ea0ee
RR
244 (*m_accept) = FALSE;
245 (*m_res) = "";
246 m_startValue = value;
e179bd65
RR
247}
248
249void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
250{
251 if (event.m_keyCode == WXK_RETURN)
252 {
253 (*m_accept) = TRUE;
254 (*m_res) = GetValue();
bce1406b
RR
255
256 if (!wxPendingDelete.Member(this))
257 wxPendingDelete.Append(this);
258
259 if ((*m_accept) && ((*m_res) != m_startValue))
260 m_owner->OnRenameAccept();
261
e179bd65
RR
262 return;
263 }
264 if (event.m_keyCode == WXK_ESCAPE)
265 {
266 (*m_accept) = FALSE;
267 (*m_res) = "";
bce1406b
RR
268
269 if (!wxPendingDelete.Member(this))
270 wxPendingDelete.Append(this);
271
e179bd65
RR
272 return;
273 }
274 event.Skip();
275}
276
277void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
278{
bce1406b
RR
279 if (!wxPendingDelete.Member(this))
280 wxPendingDelete.Append(this);
9dfbf520 281
5f1ea0ee
RR
282 if ((*m_accept) && ((*m_res) != m_startValue))
283 m_owner->OnRenameAccept();
e179bd65
RR
284}
285
91b8de8d 286#define PIXELS_PER_UNIT 10
f135ff73 287// -----------------------------------------------------------------------------
c801d85f 288// wxTreeEvent
f135ff73 289// -----------------------------------------------------------------------------
c801d85f 290
e179bd65 291IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent)
9dfbf520 292
f135ff73 293wxTreeEvent::wxTreeEvent( wxEventType commandType, int id )
92976ab6 294 : wxNotifyEvent( commandType, id )
c801d85f 295{
00e12320
RR
296 m_code = 0;
297 m_itemOld = (wxGenericTreeItem *)NULL;
edaa81ae 298}
c801d85f 299
f135ff73 300// -----------------------------------------------------------------------------
c801d85f 301// wxGenericTreeItem
f135ff73 302// -----------------------------------------------------------------------------
c801d85f 303
f135ff73
VZ
304wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent,
305 const wxString& text,
406005d2 306 wxDC& WXUNUSED(dc),
f135ff73
VZ
307 int image, int selImage,
308 wxTreeItemData *data)
309 : m_text(text)
c801d85f 310{
00e12320
RR
311 m_images[wxTreeItemIcon_Normal] = image;
312 m_images[wxTreeItemIcon_Selected] = selImage;
313 m_images[wxTreeItemIcon_Expanded] = NO_IMAGE;
314 m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE;
8dc99046 315
00e12320
RR
316 m_data = data;
317 m_x = m_y = 0;
318 m_xCross = m_yCross = 0;
f135ff73 319
00e12320 320 m_level = 0;
f135ff73 321
00e12320
RR
322 m_isCollapsed = TRUE;
323 m_hasHilight = FALSE;
324 m_hasPlus = FALSE;
325 m_isBold = FALSE;
c801d85f 326
00e12320 327 m_parent = parent;
f135ff73 328
00e12320 329 m_attr = (wxTreeItemAttr *)NULL;
06b466c7 330
00e12320
RR
331 // We don't know the height here yet.
332 m_width = 0;
333 m_height = 0;
edaa81ae 334}
c801d85f 335
f135ff73 336wxGenericTreeItem::~wxGenericTreeItem()
c801d85f 337{
00e12320 338 delete m_data;
4832f7c0 339
00e12320 340 delete m_attr;
9ec64fa7 341
00e12320
RR
342 wxASSERT_MSG( m_children.IsEmpty(),
343 wxT("please call DeleteChildren() before deleting the item") );
372edb9d
VZ
344}
345
a43a4f9d 346void wxGenericTreeItem::DeleteChildren(wxTreeCtrl *tree)
372edb9d 347{
00e12320
RR
348 size_t count = m_children.Count();
349 for ( size_t n = 0; n < count; n++ )
a43a4f9d 350 {
00e12320
RR
351 wxGenericTreeItem *child = m_children[n];
352 if (tree)
353 tree->SendDeleteEvent(child);
a43a4f9d 354
00e12320
RR
355 child->DeleteChildren(tree);
356 delete child;
357 }
372edb9d 358
00e12320 359 m_children.Empty();
edaa81ae 360}
c801d85f 361
91b8de8d 362void wxGenericTreeItem::SetText( const wxString &text )
c801d85f 363{
00e12320 364 m_text = text;
edaa81ae 365}
c801d85f 366
74bedbeb 367void wxGenericTreeItem::Reset()
c801d85f 368{
00e12320
RR
369 m_text.Empty();
370 for ( int i = 0; i < wxTreeItemIcon_Max; i++ )
371 {
372 m_images[i] = NO_IMAGE;
373 }
8dc99046 374
00e12320
RR
375 m_data = NULL;
376 m_x = m_y =
377 m_height = m_width = 0;
378 m_xCross =
379 m_yCross = 0;
74bedbeb 380
00e12320 381 m_level = 0;
c801d85f 382
00e12320
RR
383 DeleteChildren();
384 m_isCollapsed = TRUE;
c801d85f 385
00e12320 386 m_parent = (wxGenericTreeItem *)NULL;
edaa81ae 387}
c801d85f 388
4832f7c0 389size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const
c801d85f 390{
00e12320
RR
391 size_t count = m_children.Count();
392 if ( !recursively )
393 return count;
4832f7c0 394
00e12320 395 size_t total = count;
f2593d0d 396 for (size_t n = 0; n < count; ++n)
00e12320
RR
397 {
398 total += m_children[n]->GetChildrenCount();
399 }
c801d85f 400
00e12320 401 return total;
edaa81ae 402}
c801d85f
KB
403
404void wxGenericTreeItem::SetCross( int x, int y )
405{
00e12320
RR
406 m_xCross = x;
407 m_yCross = y;
edaa81ae 408}
c801d85f 409
91b8de8d 410void wxGenericTreeItem::GetSize( int &x, int &y, const wxTreeCtrl *theTree )
c801d85f 411{
00e12320
RR
412 int bottomY=m_y+theTree->GetLineHeight(this);
413 if ( y < bottomY ) y = bottomY;
414 int width = m_x + m_width;
415 if ( x < width ) x = width;
f135ff73 416
00e12320 417 if (IsExpanded())
4832f7c0 418 {
00e12320
RR
419 size_t count = m_children.Count();
420 for ( size_t n = 0; n < count; ++n )
421 {
422 m_children[n]->GetSize( x, y, theTree );
423 }
df875e59 424 }
edaa81ae 425}
c801d85f 426
f135ff73 427wxGenericTreeItem *wxGenericTreeItem::HitTest( const wxPoint& point,
c193b707
VZ
428 const wxTreeCtrl *theTree,
429 int &flags)
c801d85f 430{
f2593d0d 431 if ((point.y > m_y) && (point.y < m_y + theTree->GetLineHeight(this)))
c801d85f 432 {
f2593d0d
RR
433 if (point.y<m_y+theTree->GetLineHeight(this)/2)
434 flags |= wxTREE_HITTEST_ONITEMUPPERPART;
435 else
436 flags |= wxTREE_HITTEST_ONITEMLOWERPART;
978f38c2 437
f2593d0d
RR
438 // 5 is the size of the plus sign
439 if ((point.x > m_xCross-5) && (point.x < m_xCross+5) &&
440 (point.y > m_yCross-5) && (point.y < m_yCross+5) &&
441 (IsExpanded() || HasPlus()))
442 {
443 flags|=wxTREE_HITTEST_ONITEMBUTTON;
444 return this;
445 }
d3a9f4af 446
f2593d0d
RR
447 if ((point.x >= m_x) && (point.x <= m_x+m_width))
448 {
449 int image_w = -1;
450 int image_h;
0ae7f2a2 451
f2593d0d
RR
452 // assuming every image (normal and selected ) has the same size !
453 if ( (GetImage() != NO_IMAGE) && theTree->m_imageListNormal )
454 theTree->m_imageListNormal->GetSize(GetImage(), image_w, image_h);
91b8de8d 455
f2593d0d
RR
456 if ((image_w != -1) && (point.x <= m_x + image_w + 1))
457 flags |= wxTREE_HITTEST_ONITEMICON;
458 else
459 flags |= wxTREE_HITTEST_ONITEMLABEL;
91b8de8d 460
f2593d0d
RR
461 return this;
462 }
91b8de8d 463
f2593d0d
RR
464 if (point.x < m_x)
465 flags |= wxTREE_HITTEST_ONITEMINDENT;
466 if (point.x > m_x+m_width)
467 flags |= wxTREE_HITTEST_ONITEMRIGHT;
468
469 return this;
470 }
471 else
c801d85f 472 {
f2593d0d
RR
473 if (!m_isCollapsed)
474 {
475 size_t count = m_children.Count();
476 for ( size_t n = 0; n < count; n++ )
477 {
478 wxGenericTreeItem *res = m_children[n]->HitTest( point, theTree, flags );
479 if ( res != NULL )
480 return res;
481 }
482 }
edaa81ae 483 }
f135ff73 484
f2593d0d 485 flags|=wxTREE_HITTEST_NOWHERE;
06b466c7 486
f2593d0d 487 return (wxGenericTreeItem*) NULL;
edaa81ae 488}
c801d85f 489
8dc99046
VZ
490int wxGenericTreeItem::GetCurrentImage() const
491{
492 int image = NO_IMAGE;
493 if ( IsExpanded() )
494 {
495 if ( IsSelected() )
496 {
497 image = GetImage(wxTreeItemIcon_SelectedExpanded);
498 }
499
500 if ( image == NO_IMAGE )
501 {
502 // we usually fall back to the normal item, but try just the
503 // expanded one (and not selected) first in this case
504 image = GetImage(wxTreeItemIcon_Expanded);
505 }
506 }
507 else // not expanded
508 {
509 if ( IsSelected() )
510 image = GetImage(wxTreeItemIcon_Selected);
511 }
512
513 // may be it doesn't have the specific image we want, try the default one
514 // instead
515 if ( image == NO_IMAGE )
516 {
517 image = GetImage();
518 }
519
520 return image;
521}
522
f135ff73
VZ
523// -----------------------------------------------------------------------------
524// wxTreeCtrl implementation
525// -----------------------------------------------------------------------------
526
527IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxScrolledWindow)
528
529BEGIN_EVENT_TABLE(wxTreeCtrl,wxScrolledWindow)
00e12320
RR
530 EVT_PAINT (wxTreeCtrl::OnPaint)
531 EVT_MOUSE_EVENTS (wxTreeCtrl::OnMouse)
532 EVT_CHAR (wxTreeCtrl::OnChar)
533 EVT_SET_FOCUS (wxTreeCtrl::OnSetFocus)
534 EVT_KILL_FOCUS (wxTreeCtrl::OnKillFocus)
535 EVT_IDLE (wxTreeCtrl::OnIdle)
f135ff73
VZ
536END_EVENT_TABLE()
537
538// -----------------------------------------------------------------------------
539// construction/destruction
540// -----------------------------------------------------------------------------
91b8de8d 541
f135ff73 542void wxTreeCtrl::Init()
c801d85f 543{
00e12320
RR
544 m_current =
545 m_key_current =
546 m_anchor = (wxGenericTreeItem *) NULL;
547 m_hasFocus = FALSE;
548 m_dirty = FALSE;
f135ff73 549
00e12320
RR
550 m_xScroll = 0;
551 m_yScroll = 0;
552 m_lineHeight = 10;
553 m_indent = 15;
554 m_spacing = 18;
f135ff73 555
00e12320 556 m_hilightBrush = new wxBrush
f135ff73
VZ
557 (
558 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT),
559 wxSOLID
560 );
561
00e12320
RR
562 m_imageListNormal =
563 m_imageListState = (wxImageList *) NULL;
978f38c2 564
00e12320 565 m_dragCount = 0;
9dfbf520 566
00e12320 567 m_renameTimer = new wxTreeRenameTimer( this );
f38374d0 568
00e12320
RR
569 m_normalFont = wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT );
570 m_boldFont = wxFont( m_normalFont.GetPointSize(),
eff869aa
RR
571 m_normalFont.GetFamily(),
572 m_normalFont.GetStyle(),
573 wxBOLD,
574 m_normalFont.GetUnderlined());
edaa81ae 575}
c801d85f 576
f135ff73
VZ
577bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
578 const wxPoint& pos, const wxSize& size,
978f38c2 579 long style,
674ac8b9
VZ
580 const wxValidator &validator,
581 const wxString& name )
c801d85f 582{
00e12320 583 Init();
f135ff73 584
00e12320 585 wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name );
978f38c2 586
ce4169a4 587#if wxUSE_VALIDATORS
00e12320 588 SetValidator( validator );
ce4169a4 589#endif
f135ff73 590
91fc2bdc 591 SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
00e12320
RR
592// m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86
593 m_dottedPen = wxPen( "grey", 0, 0 );
f135ff73 594
00e12320 595 return TRUE;
edaa81ae 596}
c801d85f 597
f135ff73 598wxTreeCtrl::~wxTreeCtrl()
c801d85f 599{
00e12320 600 wxDELETE( m_hilightBrush );
a43a4f9d 601
00e12320 602 DeleteAllItems();
9dfbf520 603
00e12320 604 delete m_renameTimer;
edaa81ae 605}
c801d85f 606
f135ff73
VZ
607// -----------------------------------------------------------------------------
608// accessors
609// -----------------------------------------------------------------------------
610
611size_t wxTreeCtrl::GetCount() const
c801d85f 612{
f2593d0d 613 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
edaa81ae 614}
c801d85f 615
f135ff73 616void wxTreeCtrl::SetIndent(unsigned int indent)
c801d85f 617{
f2593d0d
RR
618 m_indent = indent;
619 m_dirty = TRUE;
cf724bce
RR
620}
621
622void wxTreeCtrl::SetSpacing(unsigned int spacing)
623{
f2593d0d
RR
624 m_spacing = spacing;
625 m_dirty = TRUE;
f135ff73 626}
74bedbeb 627
4832f7c0
VZ
628size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
629{
f2593d0d 630 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
4832f7c0 631
f2593d0d 632 return item.m_pItem->GetChildrenCount(recursively);
4832f7c0
VZ
633}
634
f135ff73
VZ
635// -----------------------------------------------------------------------------
636// functions to work with tree items
637// -----------------------------------------------------------------------------
74bedbeb 638
f135ff73
VZ
639wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
640{
f2593d0d 641 wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") );
4832f7c0 642
f2593d0d 643 return item.m_pItem->GetText();
edaa81ae 644}
74bedbeb 645
8dc99046
VZ
646int wxTreeCtrl::GetItemImage(const wxTreeItemId& item,
647 wxTreeItemIcon which) const
74bedbeb 648{
f2593d0d 649 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
4832f7c0 650
f2593d0d 651 return item.m_pItem->GetImage(which);
edaa81ae 652}
c801d85f 653
f135ff73 654wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
c801d85f 655{
f2593d0d 656 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
4832f7c0 657
f2593d0d 658 return item.m_pItem->GetData();
edaa81ae 659}
c801d85f 660
f135ff73
VZ
661void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
662{
f2593d0d 663 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 664
f2593d0d
RR
665 wxClientDC dc(this);
666 wxGenericTreeItem *pItem = item.m_pItem;
667 pItem->SetText(text);
668 CalculateSize(pItem, dc);
669 RefreshLine(pItem);
f135ff73 670}
c801d85f 671
8dc99046
VZ
672void wxTreeCtrl::SetItemImage(const wxTreeItemId& item,
673 int image,
674 wxTreeItemIcon which)
f135ff73 675{
223d09f6 676 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 677
8dc99046
VZ
678 wxGenericTreeItem *pItem = item.m_pItem;
679 pItem->SetImage(image, which);
4832f7c0 680
8dc99046
VZ
681 wxClientDC dc(this);
682 CalculateSize(pItem, dc);
683 RefreshLine(pItem);
edaa81ae 684}
c801d85f 685
f135ff73 686void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
c801d85f 687{
f2593d0d 688 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 689
f2593d0d 690 item.m_pItem->SetData(data);
edaa81ae 691}
c801d85f 692
f135ff73 693void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
c801d85f 694{
f2593d0d 695 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 696
f2593d0d
RR
697 wxGenericTreeItem *pItem = item.m_pItem;
698 pItem->SetHasPlus(has);
699 RefreshLine(pItem);
edaa81ae 700}
c801d85f 701
ef44a621
VZ
702void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
703{
9ec64fa7 704 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
ef44a621 705
9ec64fa7
VZ
706 // avoid redrawing the tree if no real change
707 wxGenericTreeItem *pItem = item.m_pItem;
708 if ( pItem->IsBold() != bold )
709 {
710 pItem->SetBold(bold);
711 RefreshLine(pItem);
712 }
713}
714
715void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
716 const wxColour& col)
717{
718 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
719
720 wxGenericTreeItem *pItem = item.m_pItem;
721 pItem->Attr().SetTextColour(col);
722 RefreshLine(pItem);
723}
724
725void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
726 const wxColour& col)
727{
728 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
729
730 wxGenericTreeItem *pItem = item.m_pItem;
731 pItem->Attr().SetBackgroundColour(col);
732 RefreshLine(pItem);
733}
734
735void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
736{
737 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
738
739 wxGenericTreeItem *pItem = item.m_pItem;
740 pItem->Attr().SetFont(font);
ef44a621 741 RefreshLine(pItem);
ef44a621
VZ
742}
743
f135ff73
VZ
744// -----------------------------------------------------------------------------
745// item status inquiries
746// -----------------------------------------------------------------------------
747
df875e59 748bool wxTreeCtrl::IsVisible(const wxTreeItemId& WXUNUSED(item)) const
c801d85f 749{
f2593d0d 750 wxFAIL_MSG(wxT("not implemented"));
f135ff73 751
f2593d0d 752 return TRUE;
edaa81ae 753}
c801d85f 754
f135ff73 755bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
c801d85f 756{
f2593d0d 757 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
4832f7c0 758
f2593d0d 759 return !item.m_pItem->GetChildren().IsEmpty();
edaa81ae 760}
c801d85f 761
f135ff73 762bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
c801d85f 763{
f2593d0d 764 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
4832f7c0 765
f2593d0d 766 return item.m_pItem->IsExpanded();
f135ff73 767}
29d87bba 768
f135ff73
VZ
769bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
770{
f2593d0d 771 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
4832f7c0 772
f2593d0d 773 return item.m_pItem->IsSelected();
f135ff73 774}
29d87bba 775
ef44a621
VZ
776bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
777{
f2593d0d 778 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
ef44a621 779
f2593d0d 780 return item.m_pItem->IsBold();
ef44a621
VZ
781}
782
f135ff73
VZ
783// -----------------------------------------------------------------------------
784// navigation
785// -----------------------------------------------------------------------------
29d87bba 786
f135ff73
VZ
787wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
788{
223d09f6 789 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
389cdc7a 790
f135ff73
VZ
791 return item.m_pItem->GetParent();
792}
29d87bba 793
f135ff73
VZ
794wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
795{
223d09f6 796 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 797
f135ff73
VZ
798 cookie = 0;
799 return GetNextChild(item, cookie);
800}
29d87bba 801
f135ff73
VZ
802wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
803{
223d09f6 804 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 805
91b8de8d 806 wxArrayGenericTreeItems& children = item.m_pItem->GetChildren();
4832f7c0
VZ
807 if ( (size_t)cookie < children.Count() )
808 {
06b466c7 809 return children.Item((size_t)cookie++);
4832f7c0
VZ
810 }
811 else
812 {
813 // there are no more of them
1e6d9499 814 return wxTreeItemId();
4832f7c0 815 }
f135ff73 816}
29d87bba 817
978f38c2
VZ
818wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
819{
223d09f6 820 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
978f38c2 821
91b8de8d 822 wxArrayGenericTreeItems& children = item.m_pItem->GetChildren();
0a240683 823 return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
978f38c2
VZ
824}
825
f135ff73
VZ
826wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
827{
223d09f6 828 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f135ff73
VZ
829
830 wxGenericTreeItem *i = item.m_pItem;
831 wxGenericTreeItem *parent = i->GetParent();
832 if ( parent == NULL )
833 {
834 // root item doesn't have any siblings
1e6d9499 835 return wxTreeItemId();
edaa81ae 836 }
4832f7c0 837
91b8de8d 838 wxArrayGenericTreeItems& siblings = parent->GetChildren();
f135ff73 839 int index = siblings.Index(i);
3c67202d 840 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 841
f135ff73 842 size_t n = (size_t)(index + 1);
fdd8d7b5 843 return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
edaa81ae 844}
c801d85f 845
f135ff73 846wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
c801d85f 847{
223d09f6 848 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f135ff73
VZ
849
850 wxGenericTreeItem *i = item.m_pItem;
851 wxGenericTreeItem *parent = i->GetParent();
852 if ( parent == NULL )
c801d85f 853 {
f135ff73 854 // root item doesn't have any siblings
1e6d9499 855 return wxTreeItemId();
edaa81ae 856 }
4832f7c0 857
91b8de8d 858 wxArrayGenericTreeItems& siblings = parent->GetChildren();
f135ff73 859 int index = siblings.Index(i);
3c67202d 860 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 861
fdd8d7b5
VZ
862 return index == 0 ? wxTreeItemId()
863 : wxTreeItemId(siblings[(size_t)(index - 1)]);
f135ff73 864}
389cdc7a 865
f135ff73
VZ
866wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
867{
223d09f6 868 wxFAIL_MSG(wxT("not implemented"));
29d87bba 869
1e6d9499 870 return wxTreeItemId();
f135ff73 871}
29d87bba 872
f135ff73
VZ
873wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
874{
223d09f6 875 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 876
223d09f6 877 wxFAIL_MSG(wxT("not implemented"));
29d87bba 878
1e6d9499 879 return wxTreeItemId();
f135ff73 880}
29d87bba 881
f135ff73
VZ
882wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
883{
223d09f6 884 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 885
223d09f6 886 wxFAIL_MSG(wxT("not implemented"));
29d87bba 887
1e6d9499 888 return wxTreeItemId();
edaa81ae 889}
c801d85f 890
f135ff73
VZ
891// -----------------------------------------------------------------------------
892// operations
893// -----------------------------------------------------------------------------
894
895wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
896 size_t previous,
897 const wxString& text,
898 int image, int selImage,
899 wxTreeItemData *data)
c801d85f 900{
f49f2b0c
RR
901 wxGenericTreeItem *parent = parentId.m_pItem;
902 if ( !parent )
903 {
904 // should we give a warning here?
905 return AddRoot(text, image, selImage, data);
906 }
4832f7c0 907
f49f2b0c 908 wxClientDC dc(this);
f38374d0 909 wxGenericTreeItem *item =
f49f2b0c 910 new wxGenericTreeItem( parent, text, dc, image, selImage, data );
74bedbeb 911
f49f2b0c
RR
912 if ( data != NULL )
913 {
914 data->m_pItem = item;
915 }
74bedbeb 916
f49f2b0c 917 parent->Insert( item, previous );
ef44a621 918
f49f2b0c 919 m_dirty = TRUE;
389cdc7a 920
f49f2b0c 921 return item;
4c681997
RR
922}
923
f135ff73
VZ
924wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
925 int image, int selImage,
926 wxTreeItemData *data)
4c681997 927{
f49f2b0c 928 wxCHECK_MSG( !m_anchor, wxTreeItemId(), wxT("tree can have only one root") );
389cdc7a 929
f49f2b0c
RR
930 wxClientDC dc(this);
931 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
f135ff73 932 image, selImage, data);
f49f2b0c
RR
933 if ( data != NULL )
934 {
935 data->m_pItem = m_anchor;
936 }
f38374d0 937
f49f2b0c
RR
938 if (!HasFlag(wxTR_MULTIPLE))
939 {
940 m_current = m_key_current = m_anchor;
06b466c7 941 m_current->SetHilight( TRUE );
f49f2b0c 942 }
389cdc7a 943
f49f2b0c
RR
944 Refresh();
945 AdjustMyScrollbars();
a32dd690 946
f49f2b0c 947 return m_anchor;
edaa81ae 948}
c801d85f 949
f135ff73
VZ
950wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
951 const wxString& text,
952 int image, int selImage,
953 wxTreeItemData *data)
c801d85f 954{
f2593d0d 955 return DoInsertItem(parent, 0u, text, image, selImage, data);
edaa81ae 956}
c801d85f 957
f135ff73
VZ
958wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
959 const wxTreeItemId& idPrevious,
960 const wxString& text,
961 int image, int selImage,
962 wxTreeItemData *data)
c801d85f 963{
f2593d0d
RR
964 wxGenericTreeItem *parent = parentId.m_pItem;
965 if ( !parent )
966 {
967 // should we give a warning here?
968 return AddRoot(text, image, selImage, data);
969 }
c801d85f 970
f2593d0d
RR
971 int index = parent->GetChildren().Index(idPrevious.m_pItem);
972 wxASSERT_MSG( index != wxNOT_FOUND,
973 wxT("previous item in wxTreeCtrl::InsertItem() is not a sibling") );
06b466c7 974
f2593d0d
RR
975 return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
976}
977
978wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
979 size_t before,
980 const wxString& text,
981 int image, int selImage,
982 wxTreeItemData *data)
983{
984 wxGenericTreeItem *parent = parentId.m_pItem;
985 if ( !parent )
986 {
987 // should we give a warning here?
988 return AddRoot(text, image, selImage, data);
989 }
990
991 return DoInsertItem(parentId, before, text, image, selImage, data);
edaa81ae 992}
c801d85f 993
f135ff73
VZ
994wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId,
995 const wxString& text,
996 int image, int selImage,
997 wxTreeItemData *data)
74bedbeb 998{
f2593d0d
RR
999 wxGenericTreeItem *parent = parentId.m_pItem;
1000 if ( !parent )
1001 {
1002 // should we give a warning here?
1003 return AddRoot(text, image, selImage, data);
1004 }
f135ff73 1005
f2593d0d
RR
1006 return DoInsertItem( parent, parent->GetChildren().Count(), text,
1007 image, selImage, data);
74bedbeb
VZ
1008}
1009
a43a4f9d
VZ
1010void wxTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
1011{
f2593d0d
RR
1012 wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
1013 event.m_item = item;
1014 event.SetEventObject( this );
1015 ProcessEvent( event );
a43a4f9d
VZ
1016}
1017
372edb9d
VZ
1018void wxTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
1019{
1020 wxGenericTreeItem *item = itemId.m_pItem;
a43a4f9d 1021 item->DeleteChildren(this);
372edb9d
VZ
1022
1023 m_dirty = TRUE;
1024}
1025
f135ff73 1026void wxTreeCtrl::Delete(const wxTreeItemId& itemId)
c801d85f 1027{
97d7bfb8
RR
1028 wxGenericTreeItem *item = itemId.m_pItem;
1029 wxGenericTreeItem *parent = item->GetParent();
ff5bf259 1030
97d7bfb8
RR
1031 if ( parent )
1032 {
1033 parent->GetChildren().Remove( item ); // remove by value
1034 }
f135ff73 1035
97d7bfb8
RR
1036 item->DeleteChildren(this);
1037 SendDeleteEvent(item);
1038 delete item;
f135ff73 1039
97d7bfb8 1040 m_dirty = TRUE;
edaa81ae 1041}
c801d85f 1042
f135ff73 1043void wxTreeCtrl::DeleteAllItems()
c801d85f 1044{
97d7bfb8
RR
1045 if ( m_anchor )
1046 {
1047 m_anchor->DeleteChildren(this);
1048 delete m_anchor;
a43a4f9d 1049
97d7bfb8 1050 m_anchor = NULL;
f135ff73 1051
97d7bfb8
RR
1052 m_dirty = TRUE;
1053 }
edaa81ae
RR
1054}
1055
f135ff73 1056void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
edaa81ae 1057{
f2593d0d 1058 wxGenericTreeItem *item = itemId.m_pItem;
f135ff73 1059
f2593d0d
RR
1060 if ( !item->HasPlus() )
1061 return;
978f38c2 1062
f2593d0d
RR
1063 if ( item->IsExpanded() )
1064 return;
f135ff73 1065
f2593d0d
RR
1066 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
1067 event.m_item = item;
1068 event.SetEventObject( this );
004fd0c8 1069
d5a07b9e 1070// if ( ProcessEvent( event ) && event.m_code ) TODO: Was this a typo ?
f2593d0d
RR
1071 if ( ProcessEvent( event ) && !event.IsAllowed() )
1072 {
1073 // cancelled by program
1074 return;
1075 }
4832f7c0 1076
f2593d0d
RR
1077 item->Expand();
1078 CalculatePositions();
f135ff73 1079
f2593d0d 1080 RefreshSubtree(item);
f135ff73 1081
f2593d0d
RR
1082 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
1083 ProcessEvent( event );
edaa81ae
RR
1084}
1085
f135ff73 1086void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
edaa81ae 1087{
f2593d0d 1088 wxGenericTreeItem *item = itemId.m_pItem;
f135ff73 1089
f2593d0d
RR
1090 if ( !item->IsExpanded() )
1091 return;
f135ff73 1092
f2593d0d
RR
1093 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
1094 event.m_item = item;
1095 event.SetEventObject( this );
1096 if ( ProcessEvent( event ) && !event.IsAllowed() )
1097 {
1098 // cancelled by program
1099 return;
1100 }
4832f7c0 1101
f2593d0d 1102 item->Collapse();
f135ff73 1103
f2593d0d
RR
1104 wxArrayGenericTreeItems& children = item->GetChildren();
1105 size_t count = children.Count();
1106 for ( size_t n = 0; n < count; n++ )
1107 {
1108 Collapse(children[n]);
1109 }
f135ff73 1110
f2593d0d 1111 CalculatePositions();
f135ff73 1112
f2593d0d 1113 RefreshSubtree(item);
f135ff73 1114
f2593d0d
RR
1115 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
1116 ProcessEvent( event );
edaa81ae 1117}
c801d85f 1118
f135ff73 1119void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
c801d85f 1120{
00e12320
RR
1121 Collapse(item);
1122 DeleteChildren(item);
edaa81ae 1123}
c801d85f 1124
f135ff73 1125void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
c801d85f 1126{
00e12320 1127 wxGenericTreeItem *item = itemId.m_pItem;
389cdc7a 1128
00e12320
RR
1129 if (item->IsExpanded())
1130 Collapse(itemId);
1131 else
1132 Expand(itemId);
f135ff73 1133}
389cdc7a 1134
f135ff73
VZ
1135void wxTreeCtrl::Unselect()
1136{
00e12320
RR
1137 if (m_current)
1138 {
1139 m_current->SetHilight( FALSE );
1140 RefreshLine( m_current );
1141 }
edaa81ae 1142}
c801d85f 1143
88ac883a 1144void wxTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
389cdc7a 1145{
00e12320
RR
1146 if (item->IsSelected())
1147 {
1148 item->SetHilight(FALSE);
1149 RefreshLine(item);
1150 }
d3a9f4af 1151
00e12320 1152 if (item->HasChildren())
88ac883a 1153 {
00e12320
RR
1154 wxArrayGenericTreeItems& children = item->GetChildren();
1155 size_t count = children.Count();
1156 for ( size_t n = 0; n < count; ++n )
1157 {
1158 UnselectAllChildren(children[n]);
1159 }
88ac883a
VZ
1160 }
1161}
f135ff73 1162
88ac883a
VZ
1163void wxTreeCtrl::UnselectAll()
1164{
00e12320 1165 UnselectAllChildren(GetRootItem().m_pItem);
88ac883a
VZ
1166}
1167
1168// Recursive function !
1169// To stop we must have crt_item<last_item
91b8de8d 1170// Algorithm :
88ac883a 1171// Tag all next children, when no more children,
d3a9f4af 1172// Move to parent (not to tag)
91b8de8d 1173// Keep going... if we found last_item, we stop.
88ac883a
VZ
1174bool wxTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1175{
1176 wxGenericTreeItem *parent = crt_item->GetParent();
1177
00e12320
RR
1178 if (parent == NULL) // This is root item
1179 return TagAllChildrenUntilLast(crt_item, last_item, select);
88ac883a 1180
91b8de8d 1181 wxArrayGenericTreeItems& children = parent->GetChildren();
88ac883a
VZ
1182 int index = children.Index(crt_item);
1183 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
1184
1185 size_t count = children.Count();
1186 for (size_t n=(size_t)(index+1); n<count; ++n)
00e12320
RR
1187 {
1188 if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
1189 }
88ac883a
VZ
1190
1191 return TagNextChildren(parent, last_item, select);
1192}
1193
1194bool wxTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1195{
00e12320
RR
1196 crt_item->SetHilight(select);
1197 RefreshLine(crt_item);
d3a9f4af 1198
06b466c7 1199 if (crt_item==last_item)
00e12320 1200 return TRUE;
88ac883a 1201
00e12320 1202 if (crt_item->HasChildren())
88ac883a 1203 {
00e12320
RR
1204 wxArrayGenericTreeItems& children = crt_item->GetChildren();
1205 size_t count = children.Count();
1206 for ( size_t n = 0; n < count; ++n )
1207 {
06b466c7 1208 if (TagAllChildrenUntilLast(children[n], last_item, select))
00e12320 1209 return TRUE;
06b466c7 1210 }
88ac883a 1211 }
d3a9f4af 1212
c0de7af4 1213 return FALSE;
88ac883a
VZ
1214}
1215
1216void wxTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *item2)
1217{
f2593d0d
RR
1218 // item2 is not necessary after item1
1219 wxGenericTreeItem *first=NULL, *last=NULL;
88ac883a 1220
f2593d0d
RR
1221 // choice first' and 'last' between item1 and item2
1222 if (item1->GetY()<item2->GetY())
06b466c7 1223 {
f2593d0d
RR
1224 first=item1;
1225 last=item2;
1226 }
1227 else
1228 {
1229 first=item2;
1230 last=item1;
1231 }
88ac883a 1232
f2593d0d 1233 bool select = m_current->IsSelected();
d3a9f4af 1234
f2593d0d
RR
1235 if ( TagAllChildrenUntilLast(first,last,select) )
1236 return;
88ac883a 1237
f2593d0d 1238 TagNextChildren(first,last,select);
88ac883a
VZ
1239}
1240
d3a9f4af 1241void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId,
c193b707
VZ
1242 bool unselect_others,
1243 bool extended_select)
d3a9f4af 1244{
223d09f6 1245 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
8b04a037 1246
88ac883a 1247 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
5391f772 1248 wxGenericTreeItem *item = itemId.m_pItem;
88ac883a
VZ
1249
1250 //wxCHECK_RET( ( (!unselect_others) && is_single),
223d09f6 1251 // wxT("this is a single selection tree") );
88ac883a
VZ
1252
1253 // to keep going anyhow !!!
d3a9f4af 1254 if (is_single)
8dc99046
VZ
1255 {
1256 if (item->IsSelected())
1257 return; // nothing to do
1258 unselect_others = TRUE;
1259 extended_select = FALSE;
1260 }
1261 else if ( unselect_others && item->IsSelected() )
1262 {
1263 // selection change if there is more than one item currently selected
1264 wxArrayTreeItemIds selected_items;
1265 if ( GetSelections(selected_items) == 1 )
1266 return;
1267 }
d3a9f4af 1268
f135ff73
VZ
1269 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
1270 event.m_item = item;
1271 event.m_itemOld = m_current;
1272 event.SetEventObject( this );
91b8de8d 1273 // TODO : Here we don't send any selection mode yet !
d3a9f4af 1274
f98e2558 1275 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
f135ff73
VZ
1276 return;
1277
88ac883a
VZ
1278 // ctrl press
1279 if (unselect_others)
389cdc7a 1280 {
88ac883a 1281 if (is_single) Unselect(); // to speed up thing
c193b707 1282 else UnselectAll();
edaa81ae 1283 }
f135ff73 1284
88ac883a 1285 // shift press
d3a9f4af 1286 if (extended_select)
88ac883a 1287 {
91b8de8d 1288 if (m_current == NULL) m_current=m_key_current=GetRootItem().m_pItem;
88ac883a
VZ
1289 // don't change the mark (m_current)
1290 SelectItemRange(m_current, item);
1291 }
1292 else
1293 {
c0de7af4 1294 bool select=TRUE; // the default
88ac883a 1295
c193b707
VZ
1296 // Check if we need to toggle hilight (ctrl mode)
1297 if (!unselect_others)
8dc99046 1298 select=!item->IsSelected();
88ac883a 1299
91b8de8d 1300 m_current = m_key_current = item;
c193b707
VZ
1301 m_current->SetHilight(select);
1302 RefreshLine( m_current );
88ac883a 1303 }
389cdc7a 1304
f135ff73 1305 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
6daa0637 1306 GetEventHandler()->ProcessEvent( event );
389cdc7a
VZ
1307}
1308
9dfbf520
VZ
1309void wxTreeCtrl::FillArray(wxGenericTreeItem *item,
1310 wxArrayTreeItemIds &array) const
c801d85f 1311{
8dc99046 1312 if ( item->IsSelected() )
9dfbf520 1313 array.Add(wxTreeItemId(item));
91b8de8d 1314
9dfbf520 1315 if ( item->HasChildren() )
91b8de8d 1316 {
9dfbf520
VZ
1317 wxArrayGenericTreeItems& children = item->GetChildren();
1318 size_t count = children.GetCount();
1319 for ( size_t n = 0; n < count; ++n )
1320 FillArray(children[n],array);
91b8de8d
RR
1321 }
1322}
1323
1324size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
1325{
1326 array.Empty();
1327 FillArray(GetRootItem().m_pItem, array);
1328
1329 return array.Count();
1330}
1331
1332void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
d3a9f4af 1333{
91b8de8d
RR
1334 if (!item.IsOk()) return;
1335
0659e7ee 1336 wxGenericTreeItem *gitem = item.m_pItem;
ef44a621 1337
f65635b5
VZ
1338 // first expand all parent branches
1339 wxGenericTreeItem *parent = gitem->GetParent();
5391f772 1340 while ( parent )
f65635b5 1341 {
8dc99046 1342 Expand(parent);
f65635b5
VZ
1343 parent = parent->GetParent();
1344 }
1345
5391f772 1346 //if (parent) CalculatePositions();
91b8de8d
RR
1347
1348 ScrollTo(item);
1349}
1350
1351void wxTreeCtrl::ScrollTo(const wxTreeItemId &item)
1352{
1353 if (!item.IsOk()) return;
1354
dc6c62a9
RR
1355 // We have to call this here because the label in
1356 // question might just have been added and no screen
1357 // update taken place.
1358 if (m_dirty) wxYield();
1359
91b8de8d
RR
1360 wxGenericTreeItem *gitem = item.m_pItem;
1361
f65635b5 1362 // now scroll to the item
0659e7ee 1363 int item_y = gitem->GetY();
8dc99046 1364
0659e7ee
RR
1365 int start_x = 0;
1366 int start_y = 0;
1367 ViewStart( &start_x, &start_y );
91b8de8d 1368 start_y *= PIXELS_PER_UNIT;
978f38c2 1369
a93109d5
RR
1370 int client_h = 0;
1371 int client_w = 0;
1372 GetClientSize( &client_w, &client_h );
ef44a621 1373
0659e7ee
RR
1374 if (item_y < start_y+3)
1375 {
91b8de8d 1376 // going down
0659e7ee
RR
1377 int x = 0;
1378 int y = 0;
91b8de8d
RR
1379 m_anchor->GetSize( x, y, this );
1380 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
c7a9fa36 1381 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
0659e7ee 1382 int x_pos = GetScrollPos( wxHORIZONTAL );
c193b707 1383 // Item should appear at top
91b8de8d 1384 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, item_y/PIXELS_PER_UNIT );
0659e7ee 1385 }
91b8de8d 1386 else if (item_y+GetLineHeight(gitem) > start_y+client_h)
0659e7ee 1387 {
c7a9fa36
RR
1388 // going up
1389 int x = 0;
1390 int y = 0;
1391 m_anchor->GetSize( x, y, this );
1392 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1393 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1394 item_y += PIXELS_PER_UNIT+2;
1395 int x_pos = GetScrollPos( wxHORIZONTAL );
c193b707 1396 // Item should appear at bottom
c7a9fa36 1397 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 1398 }
edaa81ae 1399}
c801d85f 1400
e1ee62bd
VZ
1401// FIXME: tree sorting functions are not reentrant and not MT-safe!
1402static wxTreeCtrl *s_treeBeingSorted = NULL;
0659e7ee 1403
004fd0c8 1404static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
e1ee62bd 1405 wxGenericTreeItem **item2)
edaa81ae 1406{
223d09f6 1407 wxCHECK_MSG( s_treeBeingSorted, 0, wxT("bug in wxTreeCtrl::SortChildren()") );
e1ee62bd
VZ
1408
1409 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
0659e7ee
RR
1410}
1411
e1ee62bd
VZ
1412int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
1413 const wxTreeItemId& item2)
0659e7ee 1414{
87138c52 1415 return wxStrcmp(GetItemText(item1), GetItemText(item2));
e1ee62bd
VZ
1416}
1417
1418void wxTreeCtrl::SortChildren(const wxTreeItemId& itemId)
1419{
223d09f6 1420 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
e1ee62bd
VZ
1421
1422 wxGenericTreeItem *item = itemId.m_pItem;
978f38c2 1423
e1ee62bd 1424 wxCHECK_RET( !s_treeBeingSorted,
223d09f6 1425 wxT("wxTreeCtrl::SortChildren is not reentrant") );
e1ee62bd 1426
91b8de8d 1427 wxArrayGenericTreeItems& children = item->GetChildren();
e1ee62bd
VZ
1428 if ( children.Count() > 1 )
1429 {
1430 s_treeBeingSorted = this;
1431 children.Sort(tree_ctrl_compare_func);
1432 s_treeBeingSorted = NULL;
978f38c2 1433
e1ee62bd
VZ
1434 m_dirty = TRUE;
1435 }
1436 //else: don't make the tree dirty as nothing changed
edaa81ae
RR
1437}
1438
f135ff73 1439wxImageList *wxTreeCtrl::GetImageList() const
edaa81ae 1440{
f135ff73 1441 return m_imageListNormal;
edaa81ae
RR
1442}
1443
f135ff73 1444wxImageList *wxTreeCtrl::GetStateImageList() const
c801d85f 1445{
f135ff73 1446 return m_imageListState;
edaa81ae 1447}
c801d85f 1448
f135ff73 1449void wxTreeCtrl::SetImageList(wxImageList *imageList)
e2414cbe 1450{
f2593d0d 1451 m_imageListNormal = imageList;
91b8de8d 1452
f2593d0d
RR
1453 // Calculate a m_lineHeight value from the image sizes.
1454 // May be toggle off. Then wxTreeCtrl will spread when
1455 // necessary (which might look ugly).
91b8de8d 1456#if 1
f2593d0d
RR
1457 wxClientDC dc(this);
1458 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1459 int width = 0, height = 0,
1460 n = m_imageListNormal->GetImageCount();
06b466c7 1461
f2593d0d
RR
1462 for (int i = 0; i < n ; i++)
1463 {
1464 m_imageListNormal->GetSize(i, width, height);
1465 if (height > m_lineHeight) m_lineHeight = height;
1466 }
91b8de8d 1467
06b466c7 1468 if (m_lineHeight < 40)
f2593d0d 1469 m_lineHeight += 2; // at least 2 pixels
06b466c7 1470 else
f2593d0d 1471 m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
91b8de8d 1472#endif
edaa81ae 1473}
e2414cbe 1474
f135ff73 1475void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
e2414cbe 1476{
f135ff73 1477 m_imageListState = imageList;
edaa81ae 1478}
e2414cbe 1479
f135ff73
VZ
1480// -----------------------------------------------------------------------------
1481// helpers
1482// -----------------------------------------------------------------------------
0659e7ee 1483
74bedbeb 1484void wxTreeCtrl::AdjustMyScrollbars()
c801d85f 1485{
0659e7ee
RR
1486 if (m_anchor)
1487 {
1488 int x = 0;
1489 int y = 0;
91b8de8d 1490 m_anchor->GetSize( x, y, this );
c193b707 1491 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
c7a9fa36 1492 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
0659e7ee
RR
1493 int x_pos = GetScrollPos( wxHORIZONTAL );
1494 int y_pos = GetScrollPos( wxVERTICAL );
91b8de8d 1495 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, y_pos );
0659e7ee
RR
1496 }
1497 else
1498 {
1499 SetScrollbars( 0, 0, 0, 0 );
1500 }
edaa81ae 1501}
c801d85f 1502
91b8de8d
RR
1503int wxTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
1504{
dc6c62a9
RR
1505 if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
1506 return item->GetHeight();
1507 else
1508 return m_lineHeight;
91b8de8d
RR
1509}
1510
ef44a621
VZ
1511void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1512{
9ec64fa7
VZ
1513 wxTreeItemAttr *attr = item->GetAttributes();
1514 if ( attr && attr->HasFont() )
1515 dc.SetFont(attr->GetFont());
1516 else if (item->IsBold())
eff869aa 1517 dc.SetFont(m_boldFont);
ef44a621 1518
bbe0af5b
RR
1519 long text_w = 0;
1520 long text_h = 0;
1521 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
ef44a621 1522
bbe0af5b
RR
1523 int image_h = 0;
1524 int image_w = 0;
8dc99046
VZ
1525 int image = item->GetCurrentImage();
1526 if ( image != NO_IMAGE )
bbe0af5b 1527 {
8dc99046 1528 m_imageListNormal->GetSize( image, image_w, image_h );
bbe0af5b
RR
1529 image_w += 4;
1530 }
ef44a621 1531
91b8de8d
RR
1532 int total_h = GetLineHeight(item);
1533
c0fba4d1
VS
1534 if (item->IsSelected())
1535 dc.SetBrush(*m_hilightBrush);
afa6a1a1 1536 else
c0fba4d1
VS
1537 {
1538 wxColour colBg;
1539 if ( attr && attr->HasBackgroundColour() )
1540 colBg = attr->GetBackgroundColour();
1541 else
1542 colBg = m_backgroundColour;
1543 dc.SetBrush(wxBrush(colBg, wxSOLID));
1544 }
afa6a1a1 1545
91b8de8d 1546 dc.DrawRectangle( item->GetX()-2, item->GetY(), item->GetWidth()+2, total_h );
ef44a621 1547
8dc99046 1548 if ( image != NO_IMAGE )
bbe0af5b 1549 {
d30b4d20 1550 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h );
8dc99046 1551 m_imageListNormal->Draw( image, dc,
49cd56ef 1552 item->GetX(),
d701d432 1553 item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0),
bbe0af5b
RR
1554 wxIMAGELIST_DRAW_TRANSPARENT );
1555 dc.DestroyClippingRegion();
1556 }
ef44a621 1557
afa6a1a1 1558 dc.SetBackgroundMode(wxTRANSPARENT);
d30b4d20 1559 dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY()
49cd56ef 1560 + ((total_h > text_h) ? (total_h - text_h)/2 : 0));
ef44a621 1561
eff869aa
RR
1562 // restore normal font
1563 dc.SetFont( m_normalFont );
ef44a621
VZ
1564}
1565
91b8de8d 1566// Now y stands for the top of the item, whereas it used to stand for middle !
16c1f7f3 1567void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
c801d85f 1568{
bbe0af5b 1569 int horizX = level*m_indent;
389cdc7a 1570
cf724bce 1571 item->SetX( horizX+m_indent+m_spacing );
91b8de8d 1572 item->SetY( y );
4c681997 1573
bbe0af5b 1574 int oldY = y;
91b8de8d
RR
1575 y+=GetLineHeight(item)/2;
1576
1577 item->SetCross( horizX+m_indent, y );
389cdc7a 1578
bbe0af5b 1579 int exposed_x = dc.LogicalToDeviceX( 0 );
5391f772 1580 int exposed_y = dc.LogicalToDeviceY( item->GetY() );
4832f7c0 1581
5391f772 1582 if (IsExposed( exposed_x, exposed_y, 10000, GetLineHeight(item) )) // 10000 = very much
bbe0af5b
RR
1583 {
1584 int startX = horizX;
cf724bce 1585 int endX = horizX + (m_indent-5);
29d87bba 1586
cf724bce 1587// if (!item->HasChildren()) endX += (m_indent+5);
bbe0af5b 1588 if (!item->HasChildren()) endX += 20;
4832f7c0 1589
bbe0af5b 1590 dc.DrawLine( startX, y, endX, y );
29d87bba 1591
bbe0af5b
RR
1592 if (item->HasPlus())
1593 {
cf724bce 1594 dc.DrawLine( horizX+(m_indent+5), y, horizX+(m_indent+15), y );
bbe0af5b
RR
1595 dc.SetPen( *wxGREY_PEN );
1596 dc.SetBrush( *wxWHITE_BRUSH );
cf724bce 1597 dc.DrawRectangle( horizX+(m_indent-5), y-4, 11, 9 );
9dfbf520 1598
bbe0af5b 1599 dc.SetPen( *wxBLACK_PEN );
cf724bce 1600 dc.DrawLine( horizX+(m_indent-2), y, horizX+(m_indent+3), y );
bbe0af5b 1601 if (!item->IsExpanded())
cf724bce 1602 dc.DrawLine( horizX+m_indent, y-2, horizX+m_indent, y+3 );
9dfbf520 1603
112c5086 1604 dc.SetPen( m_dottedPen );
bbe0af5b 1605 }
c801d85f 1606
9ec64fa7 1607 wxPen *pen = wxTRANSPARENT_PEN;
9ec64fa7 1608 wxColour colText;
a367b9b3 1609
9ec64fa7
VZ
1610 if ( item->IsSelected() )
1611 {
1612 colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
4832f7c0 1613
9ec64fa7
VZ
1614 if ( m_hasFocus )
1615 pen = wxBLACK_PEN;
f135ff73 1616
bbe0af5b
RR
1617 }
1618 else
1619 {
9ec64fa7
VZ
1620 wxTreeItemAttr *attr = item->GetAttributes();
1621 if ( attr && attr->HasTextColour() )
1622 colText = attr->GetTextColour();
1623 else
1624 colText = *wxBLACK;
bbe0af5b 1625 }
9ec64fa7
VZ
1626
1627 // prepare to draw
1628 dc.SetTextForeground(colText);
1629 dc.SetPen(*pen);
9ec64fa7
VZ
1630
1631 // draw
1632 PaintItem(item, dc);
1633
1634 // restore DC objects
1635 dc.SetBrush( *wxWHITE_BRUSH );
1636 dc.SetPen( m_dottedPen );
1637 dc.SetTextForeground( *wxBLACK );
f135ff73 1638 }
d3a9f4af 1639
91b8de8d 1640 y = oldY+GetLineHeight(item);
e2414cbe 1641
bbe0af5b
RR
1642 if (item->IsExpanded())
1643 {
91b8de8d 1644 oldY+=GetLineHeight(item)/2;
9ec64fa7 1645 int semiOldY=0;
389cdc7a 1646
91b8de8d
RR
1647 wxArrayGenericTreeItems& children = item->GetChildren();
1648 size_t n, count = children.Count();
1649 for ( n = 0; n < count; ++n )
c193b707
VZ
1650 {
1651 semiOldY=y;
1652 PaintLevel( children[n], dc, level+1, y );
1653 }
389cdc7a 1654
f65635b5
VZ
1655 // it may happen that the item is expanded but has no items (when you
1656 // delete all its children for example) - don't draw the vertical line
1657 // in this case
1658 if (count > 0)
9ec64fa7 1659 {
c193b707 1660 semiOldY+=GetLineHeight(children[--n])/2;
cf724bce 1661 dc.DrawLine( horizX+m_indent, oldY+5, horizX+m_indent, semiOldY );
9ec64fa7 1662 }
bbe0af5b 1663 }
4c681997 1664}
c801d85f 1665
91b8de8d
RR
1666void wxTreeCtrl::DrawBorder(wxTreeItemId &item)
1667{
1668 if (!item) return;
1669
1670 wxGenericTreeItem *i=item.m_pItem;
1671
1044a386 1672 wxClientDC dc(this);
91b8de8d
RR
1673 PrepareDC( dc );
1674 dc.SetLogicalFunction(wxINVERT);
1675
1676 int w,h,x;
1677 ViewStart(&x,&h); // we only need x
1678 GetClientSize(&w,&h); // we only need w
1679
1680 h=GetLineHeight(i)+1;
1681 // 2 white column at border
1682 dc.DrawRectangle( PIXELS_PER_UNIT*x+2, i->GetY()-1, w-6, h);
1683}
1684
1685void wxTreeCtrl::DrawLine(wxTreeItemId &item, bool below)
1686{
1687 if (!item) return;
1688
1689 wxGenericTreeItem *i=item.m_pItem;
1690
1044a386 1691 wxClientDC dc(this);
91b8de8d
RR
1692 PrepareDC( dc );
1693 dc.SetLogicalFunction(wxINVERT);
d3a9f4af 1694
91b8de8d
RR
1695 int w,h,y;
1696 GetSize(&w,&h);
1697
1698 if (below) y=i->GetY()+GetLineHeight(i)-1;
1699 else y=i->GetY();
1700
1701 dc.DrawLine( 0, y, w, y);
1702}
1703
f135ff73
VZ
1704// -----------------------------------------------------------------------------
1705// wxWindows callbacks
1706// -----------------------------------------------------------------------------
1707
3db7be80 1708void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
c801d85f 1709{
91b8de8d 1710 if ( !m_anchor)
0659e7ee 1711 return;
c801d85f 1712
0659e7ee
RR
1713 wxPaintDC dc(this);
1714 PrepareDC( dc );
29d87bba 1715
eff869aa 1716 dc.SetFont( m_normalFont );
0659e7ee 1717 dc.SetPen( m_dottedPen );
f38374d0 1718
eff869aa 1719 // this is now done dynamically
91b8de8d
RR
1720 //if(GetImageList() == NULL)
1721 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 1722
91b8de8d 1723 int y = 2;
0659e7ee 1724 PaintLevel( m_anchor, dc, 0, y );
edaa81ae 1725}
c801d85f 1726
3db7be80 1727void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
c801d85f 1728{
0659e7ee 1729 m_hasFocus = TRUE;
978f38c2 1730
bbe0af5b 1731 if (m_current) RefreshLine( m_current );
edaa81ae 1732}
c801d85f 1733
3db7be80 1734void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
c801d85f 1735{
0659e7ee 1736 m_hasFocus = FALSE;
978f38c2 1737
bbe0af5b 1738 if (m_current) RefreshLine( m_current );
edaa81ae 1739}
c801d85f
KB
1740
1741void wxTreeCtrl::OnChar( wxKeyEvent &event )
1742{
978f38c2 1743 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
06b466c7 1744 te.m_code = (int)event.KeyCode();
978f38c2
VZ
1745 te.SetEventObject( this );
1746 GetEventHandler()->ProcessEvent( te );
435fe83e 1747
91b8de8d 1748 if ( (m_current == 0) || (m_key_current == 0) )
978f38c2
VZ
1749 {
1750 event.Skip();
1751 return;
1752 }
ef44a621 1753
06b466c7
VZ
1754 // how should the selection work for this event?
1755 bool is_multiple, extended_select, unselect_others;
1756 EventFlagsToSelType(GetWindowStyleFlag(),
1757 event.ShiftDown(),
1758 event.ControlDown(),
1759 &is_multiple, &extended_select, &unselect_others);
88ac883a 1760
978f38c2
VZ
1761 switch (event.KeyCode())
1762 {
1763 case '+':
1764 case WXK_ADD:
1765 if (m_current->HasPlus() && !IsExpanded(m_current))
1766 {
1767 Expand(m_current);
1768 }
1769 break;
ef44a621 1770
978f38c2
VZ
1771 case '-':
1772 case WXK_SUBTRACT:
1773 if (IsExpanded(m_current))
1774 {
1775 Collapse(m_current);
1776 }
1777 break;
ef44a621 1778
978f38c2
VZ
1779 case '*':
1780 case WXK_MULTIPLY:
1781 Toggle(m_current);
1782 break;
ef44a621 1783
978f38c2
VZ
1784 case ' ':
1785 case WXK_RETURN:
1786 {
1787 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1788 event.m_item = m_current;
1789 event.m_code = 0;
1790 event.SetEventObject( this );
1791 GetEventHandler()->ProcessEvent( event );
1792 }
1793 break;
ef44a621 1794
978f38c2
VZ
1795 // up goes to the previous sibling or to the last of its children if
1796 // it's expanded
1797 case WXK_UP:
1798 {
91b8de8d 1799 wxTreeItemId prev = GetPrevSibling( m_key_current );
978f38c2
VZ
1800 if (!prev)
1801 {
91b8de8d 1802 prev = GetParent( m_key_current );
c193b707
VZ
1803 if (prev)
1804 {
90e58684 1805 long cockie = 0;
91b8de8d 1806 wxTreeItemId current = m_key_current;
90e58684
RR
1807 if (current == GetFirstChild( prev, cockie ))
1808 {
1809 // otherwise we return to where we came from
88ac883a 1810 SelectItem( prev, unselect_others, extended_select );
c193b707
VZ
1811 m_key_current=prev.m_pItem;
1812 EnsureVisible( prev );
90e58684 1813 break;
c193b707 1814 }
978f38c2
VZ
1815 }
1816 }
1817 if (prev)
1818 {
69a282d4 1819 while ( IsExpanded(prev) && HasChildren(prev) )
978f38c2 1820 {
69a282d4
VZ
1821 wxTreeItemId child = GetLastChild(prev);
1822 if ( child )
1823 {
1824 prev = child;
1825 }
978f38c2 1826 }
69a282d4 1827
88ac883a 1828 SelectItem( prev, unselect_others, extended_select );
c193b707 1829 m_key_current=prev.m_pItem;
978f38c2
VZ
1830 EnsureVisible( prev );
1831 }
1832 }
1833 break;
ef44a621 1834
978f38c2
VZ
1835 // left arrow goes to the parent
1836 case WXK_LEFT:
1837 {
1838 wxTreeItemId prev = GetParent( m_current );
1839 if (prev)
1840 {
1841 EnsureVisible( prev );
88ac883a 1842 SelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
1843 }
1844 }
1845 break;
ef44a621 1846
978f38c2
VZ
1847 case WXK_RIGHT:
1848 // this works the same as the down arrow except that we also expand the
1849 // item if it wasn't expanded yet
1850 Expand(m_current);
1851 // fall through
1852
1853 case WXK_DOWN:
ef44a621 1854 {
91b8de8d 1855 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
978f38c2
VZ
1856 {
1857 long cookie = 0;
91b8de8d 1858 wxTreeItemId child = GetFirstChild( m_key_current, cookie );
88ac883a 1859 SelectItem( child, unselect_others, extended_select );
c193b707 1860 m_key_current=child.m_pItem;
978f38c2
VZ
1861 EnsureVisible( child );
1862 }
1863 else
1864 {
91b8de8d 1865 wxTreeItemId next = GetNextSibling( m_key_current );
b62c3631
RR
1866// if (next == 0)
1867 if (!next)
978f38c2 1868 {
91b8de8d 1869 wxTreeItemId current = m_key_current;
978f38c2
VZ
1870 while (current && !next)
1871 {
1872 current = GetParent( current );
1873 if (current) next = GetNextSibling( current );
1874 }
1875 }
b62c3631
RR
1876// if (next != 0)
1877 if (next)
978f38c2 1878 {
88ac883a 1879 SelectItem( next, unselect_others, extended_select );
c193b707 1880 m_key_current=next.m_pItem;
978f38c2
VZ
1881 EnsureVisible( next );
1882 }
1883 }
ef44a621 1884 }
978f38c2 1885 break;
ef44a621 1886
978f38c2
VZ
1887 // <End> selects the last visible tree item
1888 case WXK_END:
1889 {
1890 wxTreeItemId last = GetRootItem();
1891
1892 while ( last.IsOk() && IsExpanded(last) )
1893 {
1894 wxTreeItemId lastChild = GetLastChild(last);
1895
1896 // it may happen if the item was expanded but then all of
1897 // its children have been deleted - so IsExpanded() returned
1898 // TRUE, but GetLastChild() returned invalid item
1899 if ( !lastChild )
1900 break;
1901
1902 last = lastChild;
1903 }
1904
1905 if ( last.IsOk() )
1906 {
1907 EnsureVisible( last );
88ac883a 1908 SelectItem( last, unselect_others, extended_select );
978f38c2
VZ
1909 }
1910 }
1911 break;
1912
1913 // <Home> selects the root item
1914 case WXK_HOME:
1915 {
1916 wxTreeItemId prev = GetRootItem();
1917 if (prev)
1918 {
1919 EnsureVisible( prev );
88ac883a 1920 SelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
1921 }
1922 }
1923 break;
1924
1925 default:
1926 event.Skip();
1927 }
edaa81ae 1928}
c801d85f 1929
91b8de8d 1930wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& flags)
4f22cf8d 1931{
dc6c62a9
RR
1932 // We have to call this here because the label in
1933 // question might just have been added and no screen
1934 // update taken place.
1935 if (m_dirty) wxYield();
1936
67a7abf7
VZ
1937 wxClientDC dc(this);
1938 PrepareDC(dc);
06b466c7
VZ
1939 wxCoord x = dc.DeviceToLogicalX( point.x );
1940 wxCoord y = dc.DeviceToLogicalY( point.y );
91b8de8d
RR
1941 int w, h;
1942 GetSize(&w, &h);
1943
1944 flags=0;
1945 if (point.x<0) flags|=wxTREE_HITTEST_TOLEFT;
1946 if (point.x>w) flags|=wxTREE_HITTEST_TORIGHT;
1947 if (point.y<0) flags|=wxTREE_HITTEST_ABOVE;
1948 if (point.y>h) flags|=wxTREE_HITTEST_BELOW;
d3a9f4af 1949
91b8de8d 1950 return m_anchor->HitTest( wxPoint(x, y), this, flags);
4f22cf8d
RR
1951}
1952
e179bd65
RR
1953/* **** */
1954
1955void wxTreeCtrl::Edit( const wxTreeItemId& item )
1956{
1957 if (!item.IsOk()) return;
1958
1959 m_currentEdit = item.m_pItem;
9dfbf520 1960
e179bd65
RR
1961 wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() );
1962 te.m_item = m_currentEdit;
1963 te.SetEventObject( this );
1964 GetEventHandler()->ProcessEvent( te );
1965
1966 if (!te.IsAllowed()) return;
8dc99046 1967
dc6c62a9
RR
1968 // We have to call this here because the label in
1969 // question might just have been added and no screen
1970 // update taken place.
1971 if (m_dirty) wxYield();
9dfbf520 1972
e179bd65
RR
1973 wxString s = m_currentEdit->GetText();
1974 int x = m_currentEdit->GetX();
1975 int y = m_currentEdit->GetY();
1976 int w = m_currentEdit->GetWidth();
1977 int h = m_currentEdit->GetHeight();
9dfbf520 1978
5f1ea0ee
RR
1979 int image_h = 0;
1980 int image_w = 0;
8dc99046
VZ
1981
1982 int image = m_currentEdit->GetCurrentImage();
1983 if ( image != NO_IMAGE )
5f1ea0ee 1984 {
8dc99046 1985 m_imageListNormal->GetSize( image, image_w, image_h );
5f1ea0ee
RR
1986 image_w += 4;
1987 }
1988 x += image_w;
1989 w -= image_w + 4; // I don't know why +4 is needed
e179bd65
RR
1990
1991 wxClientDC dc(this);
1992 PrepareDC( dc );
1993 x = dc.LogicalToDeviceX( x );
1994 y = dc.LogicalToDeviceY( y );
1995
1996 wxTreeTextCtrl *text = new wxTreeTextCtrl(
1997 this, -1, &m_renameAccept, &m_renameRes, this, s, wxPoint(x-4,y-4), wxSize(w+11,h+8) );
1998 text->SetFocus();
1999}
2000
2001void wxTreeCtrl::OnRenameTimer()
2002{
2003 Edit( m_current );
2004}
2005
2006void wxTreeCtrl::OnRenameAccept()
2007{
2008 wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
2009 le.m_item = m_currentEdit;
2010 le.SetEventObject( this );
2011 le.m_label = m_renameRes;
2012 GetEventHandler()->ProcessEvent( le );
9dfbf520 2013
e179bd65 2014 if (!le.IsAllowed()) return;
9dfbf520 2015
5f1ea0ee 2016 SetItemText( m_currentEdit, m_renameRes );
e179bd65 2017}
9dfbf520 2018
3db7be80 2019void wxTreeCtrl::OnMouse( wxMouseEvent &event )
c801d85f 2020{
fb882e1c 2021 if ( !(event.LeftUp() || event.RightDown() || event.LeftDClick() || event.Dragging()) ) return;
29d87bba 2022
bbe0af5b 2023 if ( !m_anchor ) return;
978f38c2 2024
bbe0af5b
RR
2025 wxClientDC dc(this);
2026 PrepareDC(dc);
06b466c7
VZ
2027 wxCoord x = dc.DeviceToLogicalX( event.GetX() );
2028 wxCoord y = dc.DeviceToLogicalY( event.GetY() );
29d87bba 2029
91b8de8d
RR
2030 int flags=0;
2031 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), this, flags);
2032 bool onButton = flags & wxTREE_HITTEST_ONITEMBUTTON;
978f38c2 2033
bbe0af5b
RR
2034 if (event.Dragging())
2035 {
fd9811b1 2036 if (m_dragCount == 0)
8dc99046
VZ
2037 m_dragStart = wxPoint(x,y);
2038
fd9811b1 2039 m_dragCount++;
8dc99046
VZ
2040
2041 if (m_dragCount != 3) return;
2042
2043 int command = wxEVT_COMMAND_TREE_BEGIN_DRAG;
2044 if (event.RightIsDown()) command = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
2045
fd9811b1
RR
2046 wxTreeEvent nevent( command, GetId() );
2047 nevent.m_item = m_current;
2048 nevent.SetEventObject(this);
2049 GetEventHandler()->ProcessEvent(nevent);
8dc99046 2050 return;
bbe0af5b 2051 }
fd9811b1
RR
2052 else
2053 {
2054 m_dragCount = 0;
2055 }
2056
2057 if (item == NULL) return; /* we hit the blank area */
978f38c2 2058
004fd0c8 2059 if (event.RightDown()) {
06b466c7
VZ
2060 wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,GetId());
2061 nevent.m_item=item;
2062 nevent.m_code=0;
2063 nevent.SetEventObject(this);
2064 GetEventHandler()->ProcessEvent(nevent);
2065 return;
fb882e1c
UC
2066 }
2067
9dfbf520
VZ
2068 if (event.LeftUp() && (item == m_current) &&
2069 (flags & wxTREE_HITTEST_ONITEMLABEL) &&
2070 HasFlag(wxTR_EDIT_LABELS) )
e179bd65
RR
2071 {
2072 m_renameTimer->Start( 100, TRUE );
2073 return;
2074 }
9dfbf520 2075
06b466c7
VZ
2076 // how should the selection work for this event?
2077 bool is_multiple, extended_select, unselect_others;
2078 EventFlagsToSelType(GetWindowStyleFlag(),
2079 event.ShiftDown(),
2080 event.ControlDown(),
2081 &is_multiple, &extended_select, &unselect_others);
88ac883a 2082
91b8de8d
RR
2083 if (onButton)
2084 {
2085 Toggle( item );
c193b707
VZ
2086 if (is_multiple)
2087 return;
91b8de8d
RR
2088 }
2089
88ac883a 2090 SelectItem(item, unselect_others, extended_select);
29d87bba 2091
bbe0af5b
RR
2092 if (event.LeftDClick())
2093 {
2094 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
2095 event.m_item = item;
2096 event.m_code = 0;
2097 event.SetEventObject( this );
2098 GetEventHandler()->ProcessEvent( event );
2099 }
edaa81ae 2100}
c801d85f 2101
3db7be80
RR
2102void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
2103{
bbe0af5b
RR
2104 /* after all changes have been done to the tree control,
2105 * we actually redraw the tree when everything is over */
ef44a621 2106
f65635b5
VZ
2107 if (!m_dirty)
2108 return;
ef44a621 2109
bbe0af5b 2110 m_dirty = FALSE;
3db7be80 2111
bbe0af5b 2112 CalculatePositions();
91b8de8d 2113 Refresh();
bbe0af5b 2114 AdjustMyScrollbars();
3db7be80
RR
2115}
2116
91b8de8d 2117void wxTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc )
d3a9f4af 2118{
91b8de8d
RR
2119 long text_w = 0;
2120 long text_h = 0;
9dfbf520 2121
a62867a5 2122 if (item->IsBold())
eff869aa 2123 dc.SetFont(m_boldFont);
9dfbf520 2124
91b8de8d 2125 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
a62867a5 2126 text_h+=2;
91b8de8d 2127
eff869aa
RR
2128 // restore normal font
2129 dc.SetFont( m_normalFont );
9dfbf520 2130
91b8de8d
RR
2131 int image_h = 0;
2132 int image_w = 0;
8dc99046
VZ
2133 int image = item->GetCurrentImage();
2134 if ( image != NO_IMAGE )
91b8de8d 2135 {
8dc99046 2136 m_imageListNormal->GetSize( image, image_w, image_h );
91b8de8d
RR
2137 image_w += 4;
2138 }
2139
2140 int total_h = (image_h > text_h) ? image_h : text_h;
2141
06b466c7 2142 if (total_h < 40)
f2593d0d 2143 total_h += 2; // at least 2 pixels
06b466c7 2144 else
f2593d0d 2145 total_h += total_h/10; // otherwise 10% extra spacing
91b8de8d
RR
2146
2147 item->SetHeight(total_h);
2148 if (total_h>m_lineHeight) m_lineHeight=total_h;
bbe0af5b 2149
91b8de8d
RR
2150 item->SetWidth(image_w+text_w+2);
2151}
2152
2153// -----------------------------------------------------------------------------
2154// for developper : y is now the top of the level
2155// not the middle of it !
bbe0af5b 2156void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
c801d85f 2157{
bbe0af5b 2158 int horizX = level*m_indent;
389cdc7a 2159
91b8de8d 2160 CalculateSize( item, dc );
d3a9f4af 2161
91b8de8d 2162 // set its position
cf724bce 2163 item->SetX( horizX+m_indent+m_spacing );
91b8de8d
RR
2164 item->SetY( y );
2165 y+=GetLineHeight(item);
4c681997 2166
bbe0af5b
RR
2167 if ( !item->IsExpanded() )
2168 {
f65635b5 2169 // we dont need to calculate collapsed branches
bbe0af5b
RR
2170 return;
2171 }
389cdc7a 2172
91b8de8d
RR
2173 wxArrayGenericTreeItems& children = item->GetChildren();
2174 size_t n, count = children.Count();
2175 for (n = 0; n < count; ++n )
f2593d0d 2176 CalculateLevel( children[n], dc, level+1, y ); // recurse
edaa81ae 2177}
c801d85f 2178
74bedbeb 2179void wxTreeCtrl::CalculatePositions()
c801d85f 2180{
bbe0af5b 2181 if ( !m_anchor ) return;
29d87bba 2182
bbe0af5b
RR
2183 wxClientDC dc(this);
2184 PrepareDC( dc );
29d87bba 2185
eff869aa 2186 dc.SetFont( m_normalFont );
29d87bba 2187
bbe0af5b 2188 dc.SetPen( m_dottedPen );
91b8de8d
RR
2189 //if(GetImageList() == NULL)
2190 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 2191
8dc99046 2192 int y = 2;
f65635b5 2193 CalculateLevel( m_anchor, dc, 0, y ); // start recursion
edaa81ae 2194}
c801d85f 2195
f135ff73 2196void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
c801d85f 2197{
e6527f9d
RR
2198 if (m_dirty) return;
2199
bbe0af5b
RR
2200 wxClientDC dc(this);
2201 PrepareDC(dc);
4832f7c0 2202
bbe0af5b
RR
2203 int cw = 0;
2204 int ch = 0;
2205 GetClientSize( &cw, &ch );
4832f7c0 2206
bbe0af5b
RR
2207 wxRect rect;
2208 rect.x = dc.LogicalToDeviceX( 0 );
2209 rect.width = cw;
2210 rect.y = dc.LogicalToDeviceY( item->GetY() );
2211 rect.height = ch;
f135ff73 2212
bbe0af5b 2213 Refresh( TRUE, &rect );
f135ff73 2214
bbe0af5b 2215 AdjustMyScrollbars();
edaa81ae 2216}
c801d85f
KB
2217
2218void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
2219{
e6527f9d
RR
2220 if (m_dirty) return;
2221
bbe0af5b
RR
2222 wxClientDC dc(this);
2223 PrepareDC( dc );
2224
5391f772
SB
2225 int cw = 0;
2226 int ch = 0;
2227 GetClientSize( &cw, &ch );
2228
bbe0af5b 2229 wxRect rect;
5391f772
SB
2230 rect.x = dc.LogicalToDeviceX( 0 );
2231 rect.y = dc.LogicalToDeviceY( item->GetY() );
2232 rect.width = cw;
91b8de8d 2233 rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
978f38c2 2234
bbe0af5b 2235 Refresh( TRUE, &rect );
edaa81ae 2236}
c801d85f 2237