]> git.saurik.com Git - wxWidgets.git/blame - src/generic/treectrl.cpp
Insert row/col now uses the current position to determine where to insert.
[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 );
91fc2bdc
VS
561 m_normalBrush = new wxBrush
562 (
563 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_LISTBOX),
564 wxSOLID
565 );
f135ff73 566
00e12320
RR
567 m_imageListNormal =
568 m_imageListState = (wxImageList *) NULL;
978f38c2 569
00e12320 570 m_dragCount = 0;
9dfbf520 571
00e12320 572 m_renameTimer = new wxTreeRenameTimer( this );
f38374d0 573
00e12320
RR
574 m_normalFont = wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT );
575 m_boldFont = wxFont( m_normalFont.GetPointSize(),
eff869aa
RR
576 m_normalFont.GetFamily(),
577 m_normalFont.GetStyle(),
578 wxBOLD,
579 m_normalFont.GetUnderlined());
edaa81ae 580}
c801d85f 581
f135ff73
VZ
582bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
583 const wxPoint& pos, const wxSize& size,
978f38c2 584 long style,
674ac8b9
VZ
585 const wxValidator &validator,
586 const wxString& name )
c801d85f 587{
00e12320 588 Init();
f135ff73 589
00e12320 590 wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name );
978f38c2 591
ce4169a4 592#if wxUSE_VALIDATORS
00e12320 593 SetValidator( validator );
ce4169a4 594#endif
f135ff73 595
91fc2bdc 596 SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
00e12320
RR
597// m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86
598 m_dottedPen = wxPen( "grey", 0, 0 );
f135ff73 599
00e12320 600 return TRUE;
edaa81ae 601}
c801d85f 602
f135ff73 603wxTreeCtrl::~wxTreeCtrl()
c801d85f 604{
00e12320 605 wxDELETE( m_hilightBrush );
91fc2bdc 606 wxDELETE( m_normalBrush );
a43a4f9d 607
00e12320 608 DeleteAllItems();
9dfbf520 609
00e12320 610 delete m_renameTimer;
edaa81ae 611}
c801d85f 612
f135ff73
VZ
613// -----------------------------------------------------------------------------
614// accessors
615// -----------------------------------------------------------------------------
616
617size_t wxTreeCtrl::GetCount() const
c801d85f 618{
f2593d0d 619 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
edaa81ae 620}
c801d85f 621
f135ff73 622void wxTreeCtrl::SetIndent(unsigned int indent)
c801d85f 623{
f2593d0d
RR
624 m_indent = indent;
625 m_dirty = TRUE;
cf724bce
RR
626}
627
628void wxTreeCtrl::SetSpacing(unsigned int spacing)
629{
f2593d0d
RR
630 m_spacing = spacing;
631 m_dirty = TRUE;
f135ff73 632}
74bedbeb 633
4832f7c0
VZ
634size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
635{
f2593d0d 636 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
4832f7c0 637
f2593d0d 638 return item.m_pItem->GetChildrenCount(recursively);
4832f7c0
VZ
639}
640
f135ff73
VZ
641// -----------------------------------------------------------------------------
642// functions to work with tree items
643// -----------------------------------------------------------------------------
74bedbeb 644
f135ff73
VZ
645wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
646{
f2593d0d 647 wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") );
4832f7c0 648
f2593d0d 649 return item.m_pItem->GetText();
edaa81ae 650}
74bedbeb 651
8dc99046
VZ
652int wxTreeCtrl::GetItemImage(const wxTreeItemId& item,
653 wxTreeItemIcon which) const
74bedbeb 654{
f2593d0d 655 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
4832f7c0 656
f2593d0d 657 return item.m_pItem->GetImage(which);
edaa81ae 658}
c801d85f 659
f135ff73 660wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
c801d85f 661{
f2593d0d 662 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
4832f7c0 663
f2593d0d 664 return item.m_pItem->GetData();
edaa81ae 665}
c801d85f 666
f135ff73
VZ
667void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
668{
f2593d0d 669 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 670
f2593d0d
RR
671 wxClientDC dc(this);
672 wxGenericTreeItem *pItem = item.m_pItem;
673 pItem->SetText(text);
674 CalculateSize(pItem, dc);
675 RefreshLine(pItem);
f135ff73 676}
c801d85f 677
8dc99046
VZ
678void wxTreeCtrl::SetItemImage(const wxTreeItemId& item,
679 int image,
680 wxTreeItemIcon which)
f135ff73 681{
223d09f6 682 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 683
8dc99046
VZ
684 wxGenericTreeItem *pItem = item.m_pItem;
685 pItem->SetImage(image, which);
4832f7c0 686
8dc99046
VZ
687 wxClientDC dc(this);
688 CalculateSize(pItem, dc);
689 RefreshLine(pItem);
edaa81ae 690}
c801d85f 691
f135ff73 692void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
c801d85f 693{
f2593d0d 694 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 695
f2593d0d 696 item.m_pItem->SetData(data);
edaa81ae 697}
c801d85f 698
f135ff73 699void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
c801d85f 700{
f2593d0d 701 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4832f7c0 702
f2593d0d
RR
703 wxGenericTreeItem *pItem = item.m_pItem;
704 pItem->SetHasPlus(has);
705 RefreshLine(pItem);
edaa81ae 706}
c801d85f 707
ef44a621
VZ
708void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
709{
9ec64fa7 710 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
ef44a621 711
9ec64fa7
VZ
712 // avoid redrawing the tree if no real change
713 wxGenericTreeItem *pItem = item.m_pItem;
714 if ( pItem->IsBold() != bold )
715 {
716 pItem->SetBold(bold);
717 RefreshLine(pItem);
718 }
719}
720
721void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
722 const wxColour& col)
723{
724 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
725
726 wxGenericTreeItem *pItem = item.m_pItem;
727 pItem->Attr().SetTextColour(col);
728 RefreshLine(pItem);
729}
730
731void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
732 const wxColour& col)
733{
734 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
735
736 wxGenericTreeItem *pItem = item.m_pItem;
737 pItem->Attr().SetBackgroundColour(col);
738 RefreshLine(pItem);
739}
740
741void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
742{
743 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
744
745 wxGenericTreeItem *pItem = item.m_pItem;
746 pItem->Attr().SetFont(font);
ef44a621 747 RefreshLine(pItem);
ef44a621
VZ
748}
749
f135ff73
VZ
750// -----------------------------------------------------------------------------
751// item status inquiries
752// -----------------------------------------------------------------------------
753
df875e59 754bool wxTreeCtrl::IsVisible(const wxTreeItemId& WXUNUSED(item)) const
c801d85f 755{
f2593d0d 756 wxFAIL_MSG(wxT("not implemented"));
f135ff73 757
f2593d0d 758 return TRUE;
edaa81ae 759}
c801d85f 760
f135ff73 761bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
c801d85f 762{
f2593d0d 763 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
4832f7c0 764
f2593d0d 765 return !item.m_pItem->GetChildren().IsEmpty();
edaa81ae 766}
c801d85f 767
f135ff73 768bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
c801d85f 769{
f2593d0d 770 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
4832f7c0 771
f2593d0d 772 return item.m_pItem->IsExpanded();
f135ff73 773}
29d87bba 774
f135ff73
VZ
775bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
776{
f2593d0d 777 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
4832f7c0 778
f2593d0d 779 return item.m_pItem->IsSelected();
f135ff73 780}
29d87bba 781
ef44a621
VZ
782bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
783{
f2593d0d 784 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
ef44a621 785
f2593d0d 786 return item.m_pItem->IsBold();
ef44a621
VZ
787}
788
f135ff73
VZ
789// -----------------------------------------------------------------------------
790// navigation
791// -----------------------------------------------------------------------------
29d87bba 792
f135ff73
VZ
793wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
794{
223d09f6 795 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
389cdc7a 796
f135ff73
VZ
797 return item.m_pItem->GetParent();
798}
29d87bba 799
f135ff73
VZ
800wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
801{
223d09f6 802 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 803
f135ff73
VZ
804 cookie = 0;
805 return GetNextChild(item, cookie);
806}
29d87bba 807
f135ff73
VZ
808wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
809{
223d09f6 810 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 811
91b8de8d 812 wxArrayGenericTreeItems& children = item.m_pItem->GetChildren();
4832f7c0
VZ
813 if ( (size_t)cookie < children.Count() )
814 {
06b466c7 815 return children.Item((size_t)cookie++);
4832f7c0
VZ
816 }
817 else
818 {
819 // there are no more of them
1e6d9499 820 return wxTreeItemId();
4832f7c0 821 }
f135ff73 822}
29d87bba 823
978f38c2
VZ
824wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
825{
223d09f6 826 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
978f38c2 827
91b8de8d 828 wxArrayGenericTreeItems& children = item.m_pItem->GetChildren();
0a240683 829 return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
978f38c2
VZ
830}
831
f135ff73
VZ
832wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
833{
223d09f6 834 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f135ff73
VZ
835
836 wxGenericTreeItem *i = item.m_pItem;
837 wxGenericTreeItem *parent = i->GetParent();
838 if ( parent == NULL )
839 {
840 // root item doesn't have any siblings
1e6d9499 841 return wxTreeItemId();
edaa81ae 842 }
4832f7c0 843
91b8de8d 844 wxArrayGenericTreeItems& siblings = parent->GetChildren();
f135ff73 845 int index = siblings.Index(i);
3c67202d 846 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 847
f135ff73 848 size_t n = (size_t)(index + 1);
fdd8d7b5 849 return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
edaa81ae 850}
c801d85f 851
f135ff73 852wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
c801d85f 853{
223d09f6 854 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f135ff73
VZ
855
856 wxGenericTreeItem *i = item.m_pItem;
857 wxGenericTreeItem *parent = i->GetParent();
858 if ( parent == NULL )
c801d85f 859 {
f135ff73 860 // root item doesn't have any siblings
1e6d9499 861 return wxTreeItemId();
edaa81ae 862 }
4832f7c0 863
91b8de8d 864 wxArrayGenericTreeItems& siblings = parent->GetChildren();
f135ff73 865 int index = siblings.Index(i);
3c67202d 866 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 867
fdd8d7b5
VZ
868 return index == 0 ? wxTreeItemId()
869 : wxTreeItemId(siblings[(size_t)(index - 1)]);
f135ff73 870}
389cdc7a 871
f135ff73
VZ
872wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
873{
223d09f6 874 wxFAIL_MSG(wxT("not implemented"));
29d87bba 875
1e6d9499 876 return wxTreeItemId();
f135ff73 877}
29d87bba 878
f135ff73
VZ
879wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
880{
223d09f6 881 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 882
223d09f6 883 wxFAIL_MSG(wxT("not implemented"));
29d87bba 884
1e6d9499 885 return wxTreeItemId();
f135ff73 886}
29d87bba 887
f135ff73
VZ
888wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
889{
223d09f6 890 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
29d87bba 891
223d09f6 892 wxFAIL_MSG(wxT("not implemented"));
29d87bba 893
1e6d9499 894 return wxTreeItemId();
edaa81ae 895}
c801d85f 896
f135ff73
VZ
897// -----------------------------------------------------------------------------
898// operations
899// -----------------------------------------------------------------------------
900
901wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
902 size_t previous,
903 const wxString& text,
904 int image, int selImage,
905 wxTreeItemData *data)
c801d85f 906{
f49f2b0c
RR
907 wxGenericTreeItem *parent = parentId.m_pItem;
908 if ( !parent )
909 {
910 // should we give a warning here?
911 return AddRoot(text, image, selImage, data);
912 }
4832f7c0 913
f49f2b0c 914 wxClientDC dc(this);
f38374d0 915 wxGenericTreeItem *item =
f49f2b0c 916 new wxGenericTreeItem( parent, text, dc, image, selImage, data );
74bedbeb 917
f49f2b0c
RR
918 if ( data != NULL )
919 {
920 data->m_pItem = item;
921 }
74bedbeb 922
f49f2b0c 923 parent->Insert( item, previous );
ef44a621 924
f49f2b0c 925 m_dirty = TRUE;
389cdc7a 926
f49f2b0c 927 return item;
4c681997
RR
928}
929
f135ff73
VZ
930wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
931 int image, int selImage,
932 wxTreeItemData *data)
4c681997 933{
f49f2b0c 934 wxCHECK_MSG( !m_anchor, wxTreeItemId(), wxT("tree can have only one root") );
389cdc7a 935
f49f2b0c
RR
936 wxClientDC dc(this);
937 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
f135ff73 938 image, selImage, data);
f49f2b0c
RR
939 if ( data != NULL )
940 {
941 data->m_pItem = m_anchor;
942 }
f38374d0 943
f49f2b0c
RR
944 if (!HasFlag(wxTR_MULTIPLE))
945 {
946 m_current = m_key_current = m_anchor;
06b466c7 947 m_current->SetHilight( TRUE );
f49f2b0c 948 }
389cdc7a 949
f49f2b0c
RR
950 Refresh();
951 AdjustMyScrollbars();
a32dd690 952
f49f2b0c 953 return m_anchor;
edaa81ae 954}
c801d85f 955
f135ff73
VZ
956wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
957 const wxString& text,
958 int image, int selImage,
959 wxTreeItemData *data)
c801d85f 960{
f2593d0d 961 return DoInsertItem(parent, 0u, text, image, selImage, data);
edaa81ae 962}
c801d85f 963
f135ff73
VZ
964wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
965 const wxTreeItemId& idPrevious,
966 const wxString& text,
967 int image, int selImage,
968 wxTreeItemData *data)
c801d85f 969{
f2593d0d
RR
970 wxGenericTreeItem *parent = parentId.m_pItem;
971 if ( !parent )
972 {
973 // should we give a warning here?
974 return AddRoot(text, image, selImage, data);
975 }
c801d85f 976
f2593d0d
RR
977 int index = parent->GetChildren().Index(idPrevious.m_pItem);
978 wxASSERT_MSG( index != wxNOT_FOUND,
979 wxT("previous item in wxTreeCtrl::InsertItem() is not a sibling") );
06b466c7 980
f2593d0d
RR
981 return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
982}
983
984wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
985 size_t before,
986 const wxString& text,
987 int image, int selImage,
988 wxTreeItemData *data)
989{
990 wxGenericTreeItem *parent = parentId.m_pItem;
991 if ( !parent )
992 {
993 // should we give a warning here?
994 return AddRoot(text, image, selImage, data);
995 }
996
997 return DoInsertItem(parentId, before, text, image, selImage, data);
edaa81ae 998}
c801d85f 999
f135ff73
VZ
1000wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId,
1001 const wxString& text,
1002 int image, int selImage,
1003 wxTreeItemData *data)
74bedbeb 1004{
f2593d0d
RR
1005 wxGenericTreeItem *parent = parentId.m_pItem;
1006 if ( !parent )
1007 {
1008 // should we give a warning here?
1009 return AddRoot(text, image, selImage, data);
1010 }
f135ff73 1011
f2593d0d
RR
1012 return DoInsertItem( parent, parent->GetChildren().Count(), text,
1013 image, selImage, data);
74bedbeb
VZ
1014}
1015
a43a4f9d
VZ
1016void wxTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
1017{
f2593d0d
RR
1018 wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
1019 event.m_item = item;
1020 event.SetEventObject( this );
1021 ProcessEvent( event );
a43a4f9d
VZ
1022}
1023
372edb9d
VZ
1024void wxTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
1025{
1026 wxGenericTreeItem *item = itemId.m_pItem;
a43a4f9d 1027 item->DeleteChildren(this);
372edb9d
VZ
1028
1029 m_dirty = TRUE;
1030}
1031
f135ff73 1032void wxTreeCtrl::Delete(const wxTreeItemId& itemId)
c801d85f 1033{
97d7bfb8
RR
1034 wxGenericTreeItem *item = itemId.m_pItem;
1035 wxGenericTreeItem *parent = item->GetParent();
ff5bf259 1036
97d7bfb8
RR
1037 if ( parent )
1038 {
1039 parent->GetChildren().Remove( item ); // remove by value
1040 }
f135ff73 1041
97d7bfb8
RR
1042 item->DeleteChildren(this);
1043 SendDeleteEvent(item);
1044 delete item;
f135ff73 1045
97d7bfb8 1046 m_dirty = TRUE;
edaa81ae 1047}
c801d85f 1048
f135ff73 1049void wxTreeCtrl::DeleteAllItems()
c801d85f 1050{
97d7bfb8
RR
1051 if ( m_anchor )
1052 {
1053 m_anchor->DeleteChildren(this);
1054 delete m_anchor;
a43a4f9d 1055
97d7bfb8 1056 m_anchor = NULL;
f135ff73 1057
97d7bfb8
RR
1058 m_dirty = TRUE;
1059 }
edaa81ae
RR
1060}
1061
f135ff73 1062void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
edaa81ae 1063{
f2593d0d 1064 wxGenericTreeItem *item = itemId.m_pItem;
f135ff73 1065
f2593d0d
RR
1066 if ( !item->HasPlus() )
1067 return;
978f38c2 1068
f2593d0d
RR
1069 if ( item->IsExpanded() )
1070 return;
f135ff73 1071
f2593d0d
RR
1072 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
1073 event.m_item = item;
1074 event.SetEventObject( this );
004fd0c8 1075
d5a07b9e 1076// if ( ProcessEvent( event ) && event.m_code ) TODO: Was this a typo ?
f2593d0d
RR
1077 if ( ProcessEvent( event ) && !event.IsAllowed() )
1078 {
1079 // cancelled by program
1080 return;
1081 }
4832f7c0 1082
f2593d0d
RR
1083 item->Expand();
1084 CalculatePositions();
f135ff73 1085
f2593d0d 1086 RefreshSubtree(item);
f135ff73 1087
f2593d0d
RR
1088 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
1089 ProcessEvent( event );
edaa81ae
RR
1090}
1091
f135ff73 1092void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
edaa81ae 1093{
f2593d0d 1094 wxGenericTreeItem *item = itemId.m_pItem;
f135ff73 1095
f2593d0d
RR
1096 if ( !item->IsExpanded() )
1097 return;
f135ff73 1098
f2593d0d
RR
1099 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
1100 event.m_item = item;
1101 event.SetEventObject( this );
1102 if ( ProcessEvent( event ) && !event.IsAllowed() )
1103 {
1104 // cancelled by program
1105 return;
1106 }
4832f7c0 1107
f2593d0d 1108 item->Collapse();
f135ff73 1109
f2593d0d
RR
1110 wxArrayGenericTreeItems& children = item->GetChildren();
1111 size_t count = children.Count();
1112 for ( size_t n = 0; n < count; n++ )
1113 {
1114 Collapse(children[n]);
1115 }
f135ff73 1116
f2593d0d 1117 CalculatePositions();
f135ff73 1118
f2593d0d 1119 RefreshSubtree(item);
f135ff73 1120
f2593d0d
RR
1121 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
1122 ProcessEvent( event );
edaa81ae 1123}
c801d85f 1124
f135ff73 1125void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
c801d85f 1126{
00e12320
RR
1127 Collapse(item);
1128 DeleteChildren(item);
edaa81ae 1129}
c801d85f 1130
f135ff73 1131void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
c801d85f 1132{
00e12320 1133 wxGenericTreeItem *item = itemId.m_pItem;
389cdc7a 1134
00e12320
RR
1135 if (item->IsExpanded())
1136 Collapse(itemId);
1137 else
1138 Expand(itemId);
f135ff73 1139}
389cdc7a 1140
f135ff73
VZ
1141void wxTreeCtrl::Unselect()
1142{
00e12320
RR
1143 if (m_current)
1144 {
1145 m_current->SetHilight( FALSE );
1146 RefreshLine( m_current );
1147 }
edaa81ae 1148}
c801d85f 1149
88ac883a 1150void wxTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
389cdc7a 1151{
00e12320
RR
1152 if (item->IsSelected())
1153 {
1154 item->SetHilight(FALSE);
1155 RefreshLine(item);
1156 }
d3a9f4af 1157
00e12320 1158 if (item->HasChildren())
88ac883a 1159 {
00e12320
RR
1160 wxArrayGenericTreeItems& children = item->GetChildren();
1161 size_t count = children.Count();
1162 for ( size_t n = 0; n < count; ++n )
1163 {
1164 UnselectAllChildren(children[n]);
1165 }
88ac883a
VZ
1166 }
1167}
f135ff73 1168
88ac883a
VZ
1169void wxTreeCtrl::UnselectAll()
1170{
00e12320 1171 UnselectAllChildren(GetRootItem().m_pItem);
88ac883a
VZ
1172}
1173
1174// Recursive function !
1175// To stop we must have crt_item<last_item
91b8de8d 1176// Algorithm :
88ac883a 1177// Tag all next children, when no more children,
d3a9f4af 1178// Move to parent (not to tag)
91b8de8d 1179// Keep going... if we found last_item, we stop.
88ac883a
VZ
1180bool wxTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1181{
1182 wxGenericTreeItem *parent = crt_item->GetParent();
1183
00e12320
RR
1184 if (parent == NULL) // This is root item
1185 return TagAllChildrenUntilLast(crt_item, last_item, select);
88ac883a 1186
91b8de8d 1187 wxArrayGenericTreeItems& children = parent->GetChildren();
88ac883a
VZ
1188 int index = children.Index(crt_item);
1189 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
1190
1191 size_t count = children.Count();
1192 for (size_t n=(size_t)(index+1); n<count; ++n)
00e12320
RR
1193 {
1194 if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
1195 }
88ac883a
VZ
1196
1197 return TagNextChildren(parent, last_item, select);
1198}
1199
1200bool wxTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1201{
00e12320
RR
1202 crt_item->SetHilight(select);
1203 RefreshLine(crt_item);
d3a9f4af 1204
06b466c7 1205 if (crt_item==last_item)
00e12320 1206 return TRUE;
88ac883a 1207
00e12320 1208 if (crt_item->HasChildren())
88ac883a 1209 {
00e12320
RR
1210 wxArrayGenericTreeItems& children = crt_item->GetChildren();
1211 size_t count = children.Count();
1212 for ( size_t n = 0; n < count; ++n )
1213 {
06b466c7 1214 if (TagAllChildrenUntilLast(children[n], last_item, select))
00e12320 1215 return TRUE;
06b466c7 1216 }
88ac883a 1217 }
d3a9f4af 1218
c0de7af4 1219 return FALSE;
88ac883a
VZ
1220}
1221
1222void wxTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *item2)
1223{
f2593d0d
RR
1224 // item2 is not necessary after item1
1225 wxGenericTreeItem *first=NULL, *last=NULL;
88ac883a 1226
f2593d0d
RR
1227 // choice first' and 'last' between item1 and item2
1228 if (item1->GetY()<item2->GetY())
06b466c7 1229 {
f2593d0d
RR
1230 first=item1;
1231 last=item2;
1232 }
1233 else
1234 {
1235 first=item2;
1236 last=item1;
1237 }
88ac883a 1238
f2593d0d 1239 bool select = m_current->IsSelected();
d3a9f4af 1240
f2593d0d
RR
1241 if ( TagAllChildrenUntilLast(first,last,select) )
1242 return;
88ac883a 1243
f2593d0d 1244 TagNextChildren(first,last,select);
88ac883a
VZ
1245}
1246
d3a9f4af 1247void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId,
c193b707
VZ
1248 bool unselect_others,
1249 bool extended_select)
d3a9f4af 1250{
223d09f6 1251 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
8b04a037 1252
88ac883a 1253 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
5391f772 1254 wxGenericTreeItem *item = itemId.m_pItem;
88ac883a
VZ
1255
1256 //wxCHECK_RET( ( (!unselect_others) && is_single),
223d09f6 1257 // wxT("this is a single selection tree") );
88ac883a
VZ
1258
1259 // to keep going anyhow !!!
d3a9f4af 1260 if (is_single)
8dc99046
VZ
1261 {
1262 if (item->IsSelected())
1263 return; // nothing to do
1264 unselect_others = TRUE;
1265 extended_select = FALSE;
1266 }
1267 else if ( unselect_others && item->IsSelected() )
1268 {
1269 // selection change if there is more than one item currently selected
1270 wxArrayTreeItemIds selected_items;
1271 if ( GetSelections(selected_items) == 1 )
1272 return;
1273 }
d3a9f4af 1274
f135ff73
VZ
1275 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
1276 event.m_item = item;
1277 event.m_itemOld = m_current;
1278 event.SetEventObject( this );
91b8de8d 1279 // TODO : Here we don't send any selection mode yet !
d3a9f4af 1280
f98e2558 1281 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
f135ff73
VZ
1282 return;
1283
88ac883a
VZ
1284 // ctrl press
1285 if (unselect_others)
389cdc7a 1286 {
88ac883a 1287 if (is_single) Unselect(); // to speed up thing
c193b707 1288 else UnselectAll();
edaa81ae 1289 }
f135ff73 1290
88ac883a 1291 // shift press
d3a9f4af 1292 if (extended_select)
88ac883a 1293 {
91b8de8d 1294 if (m_current == NULL) m_current=m_key_current=GetRootItem().m_pItem;
88ac883a
VZ
1295 // don't change the mark (m_current)
1296 SelectItemRange(m_current, item);
1297 }
1298 else
1299 {
c0de7af4 1300 bool select=TRUE; // the default
88ac883a 1301
c193b707
VZ
1302 // Check if we need to toggle hilight (ctrl mode)
1303 if (!unselect_others)
8dc99046 1304 select=!item->IsSelected();
88ac883a 1305
91b8de8d 1306 m_current = m_key_current = item;
c193b707
VZ
1307 m_current->SetHilight(select);
1308 RefreshLine( m_current );
88ac883a 1309 }
389cdc7a 1310
f135ff73 1311 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
6daa0637 1312 GetEventHandler()->ProcessEvent( event );
389cdc7a
VZ
1313}
1314
9dfbf520
VZ
1315void wxTreeCtrl::FillArray(wxGenericTreeItem *item,
1316 wxArrayTreeItemIds &array) const
c801d85f 1317{
8dc99046 1318 if ( item->IsSelected() )
9dfbf520 1319 array.Add(wxTreeItemId(item));
91b8de8d 1320
9dfbf520 1321 if ( item->HasChildren() )
91b8de8d 1322 {
9dfbf520
VZ
1323 wxArrayGenericTreeItems& children = item->GetChildren();
1324 size_t count = children.GetCount();
1325 for ( size_t n = 0; n < count; ++n )
1326 FillArray(children[n],array);
91b8de8d
RR
1327 }
1328}
1329
1330size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
1331{
1332 array.Empty();
1333 FillArray(GetRootItem().m_pItem, array);
1334
1335 return array.Count();
1336}
1337
1338void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
d3a9f4af 1339{
91b8de8d
RR
1340 if (!item.IsOk()) return;
1341
0659e7ee 1342 wxGenericTreeItem *gitem = item.m_pItem;
ef44a621 1343
f65635b5
VZ
1344 // first expand all parent branches
1345 wxGenericTreeItem *parent = gitem->GetParent();
5391f772 1346 while ( parent )
f65635b5 1347 {
8dc99046 1348 Expand(parent);
f65635b5
VZ
1349 parent = parent->GetParent();
1350 }
1351
5391f772 1352 //if (parent) CalculatePositions();
91b8de8d
RR
1353
1354 ScrollTo(item);
1355}
1356
1357void wxTreeCtrl::ScrollTo(const wxTreeItemId &item)
1358{
1359 if (!item.IsOk()) return;
1360
dc6c62a9
RR
1361 // We have to call this here because the label in
1362 // question might just have been added and no screen
1363 // update taken place.
1364 if (m_dirty) wxYield();
1365
91b8de8d
RR
1366 wxGenericTreeItem *gitem = item.m_pItem;
1367
f65635b5 1368 // now scroll to the item
0659e7ee 1369 int item_y = gitem->GetY();
8dc99046 1370
0659e7ee
RR
1371 int start_x = 0;
1372 int start_y = 0;
1373 ViewStart( &start_x, &start_y );
91b8de8d 1374 start_y *= PIXELS_PER_UNIT;
978f38c2 1375
a93109d5
RR
1376 int client_h = 0;
1377 int client_w = 0;
1378 GetClientSize( &client_w, &client_h );
ef44a621 1379
0659e7ee
RR
1380 if (item_y < start_y+3)
1381 {
91b8de8d 1382 // going down
0659e7ee
RR
1383 int x = 0;
1384 int y = 0;
91b8de8d
RR
1385 m_anchor->GetSize( x, y, this );
1386 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
c7a9fa36 1387 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
0659e7ee 1388 int x_pos = GetScrollPos( wxHORIZONTAL );
c193b707 1389 // Item should appear at top
91b8de8d 1390 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, item_y/PIXELS_PER_UNIT );
0659e7ee 1391 }
91b8de8d 1392 else if (item_y+GetLineHeight(gitem) > start_y+client_h)
0659e7ee 1393 {
c7a9fa36
RR
1394 // going up
1395 int x = 0;
1396 int y = 0;
1397 m_anchor->GetSize( x, y, this );
1398 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1399 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1400 item_y += PIXELS_PER_UNIT+2;
1401 int x_pos = GetScrollPos( wxHORIZONTAL );
c193b707 1402 // Item should appear at bottom
c7a9fa36 1403 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 1404 }
edaa81ae 1405}
c801d85f 1406
e1ee62bd
VZ
1407// FIXME: tree sorting functions are not reentrant and not MT-safe!
1408static wxTreeCtrl *s_treeBeingSorted = NULL;
0659e7ee 1409
004fd0c8 1410static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
e1ee62bd 1411 wxGenericTreeItem **item2)
edaa81ae 1412{
223d09f6 1413 wxCHECK_MSG( s_treeBeingSorted, 0, wxT("bug in wxTreeCtrl::SortChildren()") );
e1ee62bd
VZ
1414
1415 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
0659e7ee
RR
1416}
1417
e1ee62bd
VZ
1418int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
1419 const wxTreeItemId& item2)
0659e7ee 1420{
87138c52 1421 return wxStrcmp(GetItemText(item1), GetItemText(item2));
e1ee62bd
VZ
1422}
1423
1424void wxTreeCtrl::SortChildren(const wxTreeItemId& itemId)
1425{
223d09f6 1426 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
e1ee62bd
VZ
1427
1428 wxGenericTreeItem *item = itemId.m_pItem;
978f38c2 1429
e1ee62bd 1430 wxCHECK_RET( !s_treeBeingSorted,
223d09f6 1431 wxT("wxTreeCtrl::SortChildren is not reentrant") );
e1ee62bd 1432
91b8de8d 1433 wxArrayGenericTreeItems& children = item->GetChildren();
e1ee62bd
VZ
1434 if ( children.Count() > 1 )
1435 {
1436 s_treeBeingSorted = this;
1437 children.Sort(tree_ctrl_compare_func);
1438 s_treeBeingSorted = NULL;
978f38c2 1439
e1ee62bd
VZ
1440 m_dirty = TRUE;
1441 }
1442 //else: don't make the tree dirty as nothing changed
edaa81ae
RR
1443}
1444
f135ff73 1445wxImageList *wxTreeCtrl::GetImageList() const
edaa81ae 1446{
f135ff73 1447 return m_imageListNormal;
edaa81ae
RR
1448}
1449
f135ff73 1450wxImageList *wxTreeCtrl::GetStateImageList() const
c801d85f 1451{
f135ff73 1452 return m_imageListState;
edaa81ae 1453}
c801d85f 1454
f135ff73 1455void wxTreeCtrl::SetImageList(wxImageList *imageList)
e2414cbe 1456{
f2593d0d 1457 m_imageListNormal = imageList;
91b8de8d 1458
f2593d0d
RR
1459 // Calculate a m_lineHeight value from the image sizes.
1460 // May be toggle off. Then wxTreeCtrl will spread when
1461 // necessary (which might look ugly).
91b8de8d 1462#if 1
f2593d0d
RR
1463 wxClientDC dc(this);
1464 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1465 int width = 0, height = 0,
1466 n = m_imageListNormal->GetImageCount();
06b466c7 1467
f2593d0d
RR
1468 for (int i = 0; i < n ; i++)
1469 {
1470 m_imageListNormal->GetSize(i, width, height);
1471 if (height > m_lineHeight) m_lineHeight = height;
1472 }
91b8de8d 1473
06b466c7 1474 if (m_lineHeight < 40)
f2593d0d 1475 m_lineHeight += 2; // at least 2 pixels
06b466c7 1476 else
f2593d0d 1477 m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
91b8de8d 1478#endif
edaa81ae 1479}
e2414cbe 1480
f135ff73 1481void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
e2414cbe 1482{
f135ff73 1483 m_imageListState = imageList;
edaa81ae 1484}
e2414cbe 1485
f135ff73
VZ
1486// -----------------------------------------------------------------------------
1487// helpers
1488// -----------------------------------------------------------------------------
0659e7ee 1489
74bedbeb 1490void wxTreeCtrl::AdjustMyScrollbars()
c801d85f 1491{
0659e7ee
RR
1492 if (m_anchor)
1493 {
1494 int x = 0;
1495 int y = 0;
91b8de8d 1496 m_anchor->GetSize( x, y, this );
c193b707 1497 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
c7a9fa36 1498 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
0659e7ee
RR
1499 int x_pos = GetScrollPos( wxHORIZONTAL );
1500 int y_pos = GetScrollPos( wxVERTICAL );
91b8de8d 1501 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, y_pos );
0659e7ee
RR
1502 }
1503 else
1504 {
1505 SetScrollbars( 0, 0, 0, 0 );
1506 }
edaa81ae 1507}
c801d85f 1508
91b8de8d
RR
1509int wxTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
1510{
dc6c62a9
RR
1511 if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
1512 return item->GetHeight();
1513 else
1514 return m_lineHeight;
91b8de8d
RR
1515}
1516
ef44a621
VZ
1517void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1518{
9ec64fa7
VZ
1519 wxTreeItemAttr *attr = item->GetAttributes();
1520 if ( attr && attr->HasFont() )
1521 dc.SetFont(attr->GetFont());
1522 else if (item->IsBold())
eff869aa 1523 dc.SetFont(m_boldFont);
ef44a621 1524
bbe0af5b
RR
1525 long text_w = 0;
1526 long text_h = 0;
1527 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
ef44a621 1528
bbe0af5b
RR
1529 int image_h = 0;
1530 int image_w = 0;
8dc99046
VZ
1531 int image = item->GetCurrentImage();
1532 if ( image != NO_IMAGE )
bbe0af5b 1533 {
8dc99046 1534 m_imageListNormal->GetSize( image, image_w, image_h );
bbe0af5b
RR
1535 image_w += 4;
1536 }
ef44a621 1537
91b8de8d
RR
1538 int total_h = GetLineHeight(item);
1539
afa6a1a1
VZ
1540 wxColour colBg;
1541 if ( attr && attr->HasBackgroundColour() )
1542 colBg = attr->GetBackgroundColour();
1543 else
1544 colBg = m_backgroundColour;
1545 dc.SetBrush(wxBrush(colBg, wxSOLID));
1546
91b8de8d 1547 dc.DrawRectangle( item->GetX()-2, item->GetY(), item->GetWidth()+2, total_h );
ef44a621 1548
8dc99046 1549 if ( image != NO_IMAGE )
bbe0af5b 1550 {
d30b4d20 1551 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h );
8dc99046 1552 m_imageListNormal->Draw( image, dc,
49cd56ef 1553 item->GetX(),
d701d432 1554 item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0),
bbe0af5b
RR
1555 wxIMAGELIST_DRAW_TRANSPARENT );
1556 dc.DestroyClippingRegion();
1557 }
ef44a621 1558
afa6a1a1 1559 dc.SetBackgroundMode(wxTRANSPARENT);
d30b4d20 1560 dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY()
49cd56ef 1561 + ((total_h > text_h) ? (total_h - text_h)/2 : 0));
ef44a621 1562
eff869aa
RR
1563 // restore normal font
1564 dc.SetFont( m_normalFont );
ef44a621
VZ
1565}
1566
91b8de8d 1567// Now y stands for the top of the item, whereas it used to stand for middle !
16c1f7f3 1568void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
c801d85f 1569{
bbe0af5b 1570 int horizX = level*m_indent;
389cdc7a 1571
cf724bce 1572 item->SetX( horizX+m_indent+m_spacing );
91b8de8d 1573 item->SetY( y );
4c681997 1574
bbe0af5b 1575 int oldY = y;
91b8de8d
RR
1576 y+=GetLineHeight(item)/2;
1577
1578 item->SetCross( horizX+m_indent, y );
389cdc7a 1579
bbe0af5b 1580 int exposed_x = dc.LogicalToDeviceX( 0 );
5391f772 1581 int exposed_y = dc.LogicalToDeviceY( item->GetY() );
4832f7c0 1582
5391f772 1583 if (IsExposed( exposed_x, exposed_y, 10000, GetLineHeight(item) )) // 10000 = very much
bbe0af5b
RR
1584 {
1585 int startX = horizX;
cf724bce 1586 int endX = horizX + (m_indent-5);
29d87bba 1587
cf724bce 1588// if (!item->HasChildren()) endX += (m_indent+5);
bbe0af5b 1589 if (!item->HasChildren()) endX += 20;
4832f7c0 1590
bbe0af5b 1591 dc.DrawLine( startX, y, endX, y );
29d87bba 1592
bbe0af5b
RR
1593 if (item->HasPlus())
1594 {
cf724bce 1595 dc.DrawLine( horizX+(m_indent+5), y, horizX+(m_indent+15), y );
bbe0af5b
RR
1596 dc.SetPen( *wxGREY_PEN );
1597 dc.SetBrush( *wxWHITE_BRUSH );
cf724bce 1598 dc.DrawRectangle( horizX+(m_indent-5), y-4, 11, 9 );
9dfbf520 1599
bbe0af5b 1600 dc.SetPen( *wxBLACK_PEN );
cf724bce 1601 dc.DrawLine( horizX+(m_indent-2), y, horizX+(m_indent+3), y );
bbe0af5b 1602 if (!item->IsExpanded())
cf724bce 1603 dc.DrawLine( horizX+m_indent, y-2, horizX+m_indent, y+3 );
9dfbf520 1604
112c5086 1605 dc.SetPen( m_dottedPen );
bbe0af5b 1606 }
c801d85f 1607
9ec64fa7
VZ
1608 wxPen *pen = wxTRANSPARENT_PEN;
1609 wxBrush *brush; // FIXME is this really needed?
1610 wxColour colText;
a367b9b3 1611
9ec64fa7
VZ
1612 if ( item->IsSelected() )
1613 {
1614 colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
4832f7c0 1615
9ec64fa7 1616 brush = m_hilightBrush;
4832f7c0 1617
9ec64fa7
VZ
1618 if ( m_hasFocus )
1619 pen = wxBLACK_PEN;
f135ff73 1620
bbe0af5b
RR
1621 }
1622 else
1623 {
9ec64fa7
VZ
1624 wxTreeItemAttr *attr = item->GetAttributes();
1625 if ( attr && attr->HasTextColour() )
1626 colText = attr->GetTextColour();
1627 else
1628 colText = *wxBLACK;
4832f7c0 1629
91fc2bdc 1630 brush = m_normalBrush;
bbe0af5b 1631 }
9ec64fa7
VZ
1632
1633 // prepare to draw
1634 dc.SetTextForeground(colText);
1635 dc.SetPen(*pen);
1636 dc.SetBrush(*brush);
1637
1638 // draw
1639 PaintItem(item, dc);
1640
1641 // restore DC objects
1642 dc.SetBrush( *wxWHITE_BRUSH );
1643 dc.SetPen( m_dottedPen );
1644 dc.SetTextForeground( *wxBLACK );
f135ff73 1645 }
d3a9f4af 1646
91b8de8d 1647 y = oldY+GetLineHeight(item);
e2414cbe 1648
bbe0af5b
RR
1649 if (item->IsExpanded())
1650 {
91b8de8d 1651 oldY+=GetLineHeight(item)/2;
9ec64fa7 1652 int semiOldY=0;
389cdc7a 1653
91b8de8d
RR
1654 wxArrayGenericTreeItems& children = item->GetChildren();
1655 size_t n, count = children.Count();
1656 for ( n = 0; n < count; ++n )
c193b707
VZ
1657 {
1658 semiOldY=y;
1659 PaintLevel( children[n], dc, level+1, y );
1660 }
389cdc7a 1661
f65635b5
VZ
1662 // it may happen that the item is expanded but has no items (when you
1663 // delete all its children for example) - don't draw the vertical line
1664 // in this case
1665 if (count > 0)
9ec64fa7 1666 {
c193b707 1667 semiOldY+=GetLineHeight(children[--n])/2;
cf724bce 1668 dc.DrawLine( horizX+m_indent, oldY+5, horizX+m_indent, semiOldY );
9ec64fa7 1669 }
bbe0af5b 1670 }
4c681997 1671}
c801d85f 1672
91b8de8d
RR
1673void wxTreeCtrl::DrawBorder(wxTreeItemId &item)
1674{
1675 if (!item) return;
1676
1677 wxGenericTreeItem *i=item.m_pItem;
1678
1044a386 1679 wxClientDC dc(this);
91b8de8d
RR
1680 PrepareDC( dc );
1681 dc.SetLogicalFunction(wxINVERT);
1682
1683 int w,h,x;
1684 ViewStart(&x,&h); // we only need x
1685 GetClientSize(&w,&h); // we only need w
1686
1687 h=GetLineHeight(i)+1;
1688 // 2 white column at border
1689 dc.DrawRectangle( PIXELS_PER_UNIT*x+2, i->GetY()-1, w-6, h);
1690}
1691
1692void wxTreeCtrl::DrawLine(wxTreeItemId &item, bool below)
1693{
1694 if (!item) return;
1695
1696 wxGenericTreeItem *i=item.m_pItem;
1697
1044a386 1698 wxClientDC dc(this);
91b8de8d
RR
1699 PrepareDC( dc );
1700 dc.SetLogicalFunction(wxINVERT);
d3a9f4af 1701
91b8de8d
RR
1702 int w,h,y;
1703 GetSize(&w,&h);
1704
1705 if (below) y=i->GetY()+GetLineHeight(i)-1;
1706 else y=i->GetY();
1707
1708 dc.DrawLine( 0, y, w, y);
1709}
1710
f135ff73
VZ
1711// -----------------------------------------------------------------------------
1712// wxWindows callbacks
1713// -----------------------------------------------------------------------------
1714
3db7be80 1715void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
c801d85f 1716{
91b8de8d 1717 if ( !m_anchor)
0659e7ee 1718 return;
c801d85f 1719
0659e7ee
RR
1720 wxPaintDC dc(this);
1721 PrepareDC( dc );
29d87bba 1722
eff869aa 1723 dc.SetFont( m_normalFont );
0659e7ee 1724 dc.SetPen( m_dottedPen );
f38374d0 1725
eff869aa 1726 // this is now done dynamically
91b8de8d
RR
1727 //if(GetImageList() == NULL)
1728 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 1729
91b8de8d 1730 int y = 2;
0659e7ee 1731 PaintLevel( m_anchor, dc, 0, y );
edaa81ae 1732}
c801d85f 1733
3db7be80 1734void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
c801d85f 1735{
0659e7ee 1736 m_hasFocus = TRUE;
978f38c2 1737
bbe0af5b 1738 if (m_current) RefreshLine( m_current );
edaa81ae 1739}
c801d85f 1740
3db7be80 1741void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
c801d85f 1742{
0659e7ee 1743 m_hasFocus = FALSE;
978f38c2 1744
bbe0af5b 1745 if (m_current) RefreshLine( m_current );
edaa81ae 1746}
c801d85f
KB
1747
1748void wxTreeCtrl::OnChar( wxKeyEvent &event )
1749{
978f38c2 1750 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
06b466c7 1751 te.m_code = (int)event.KeyCode();
978f38c2
VZ
1752 te.SetEventObject( this );
1753 GetEventHandler()->ProcessEvent( te );
435fe83e 1754
91b8de8d 1755 if ( (m_current == 0) || (m_key_current == 0) )
978f38c2
VZ
1756 {
1757 event.Skip();
1758 return;
1759 }
ef44a621 1760
06b466c7
VZ
1761 // how should the selection work for this event?
1762 bool is_multiple, extended_select, unselect_others;
1763 EventFlagsToSelType(GetWindowStyleFlag(),
1764 event.ShiftDown(),
1765 event.ControlDown(),
1766 &is_multiple, &extended_select, &unselect_others);
88ac883a 1767
978f38c2
VZ
1768 switch (event.KeyCode())
1769 {
1770 case '+':
1771 case WXK_ADD:
1772 if (m_current->HasPlus() && !IsExpanded(m_current))
1773 {
1774 Expand(m_current);
1775 }
1776 break;
ef44a621 1777
978f38c2
VZ
1778 case '-':
1779 case WXK_SUBTRACT:
1780 if (IsExpanded(m_current))
1781 {
1782 Collapse(m_current);
1783 }
1784 break;
ef44a621 1785
978f38c2
VZ
1786 case '*':
1787 case WXK_MULTIPLY:
1788 Toggle(m_current);
1789 break;
ef44a621 1790
978f38c2
VZ
1791 case ' ':
1792 case WXK_RETURN:
1793 {
1794 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1795 event.m_item = m_current;
1796 event.m_code = 0;
1797 event.SetEventObject( this );
1798 GetEventHandler()->ProcessEvent( event );
1799 }
1800 break;
ef44a621 1801
978f38c2
VZ
1802 // up goes to the previous sibling or to the last of its children if
1803 // it's expanded
1804 case WXK_UP:
1805 {
91b8de8d 1806 wxTreeItemId prev = GetPrevSibling( m_key_current );
978f38c2
VZ
1807 if (!prev)
1808 {
91b8de8d 1809 prev = GetParent( m_key_current );
c193b707
VZ
1810 if (prev)
1811 {
90e58684 1812 long cockie = 0;
91b8de8d 1813 wxTreeItemId current = m_key_current;
90e58684
RR
1814 if (current == GetFirstChild( prev, cockie ))
1815 {
1816 // otherwise we return to where we came from
88ac883a 1817 SelectItem( prev, unselect_others, extended_select );
c193b707
VZ
1818 m_key_current=prev.m_pItem;
1819 EnsureVisible( prev );
90e58684 1820 break;
c193b707 1821 }
978f38c2
VZ
1822 }
1823 }
1824 if (prev)
1825 {
69a282d4 1826 while ( IsExpanded(prev) && HasChildren(prev) )
978f38c2 1827 {
69a282d4
VZ
1828 wxTreeItemId child = GetLastChild(prev);
1829 if ( child )
1830 {
1831 prev = child;
1832 }
978f38c2 1833 }
69a282d4 1834
88ac883a 1835 SelectItem( prev, unselect_others, extended_select );
c193b707 1836 m_key_current=prev.m_pItem;
978f38c2
VZ
1837 EnsureVisible( prev );
1838 }
1839 }
1840 break;
ef44a621 1841
978f38c2
VZ
1842 // left arrow goes to the parent
1843 case WXK_LEFT:
1844 {
1845 wxTreeItemId prev = GetParent( m_current );
1846 if (prev)
1847 {
1848 EnsureVisible( prev );
88ac883a 1849 SelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
1850 }
1851 }
1852 break;
ef44a621 1853
978f38c2
VZ
1854 case WXK_RIGHT:
1855 // this works the same as the down arrow except that we also expand the
1856 // item if it wasn't expanded yet
1857 Expand(m_current);
1858 // fall through
1859
1860 case WXK_DOWN:
ef44a621 1861 {
91b8de8d 1862 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
978f38c2
VZ
1863 {
1864 long cookie = 0;
91b8de8d 1865 wxTreeItemId child = GetFirstChild( m_key_current, cookie );
88ac883a 1866 SelectItem( child, unselect_others, extended_select );
c193b707 1867 m_key_current=child.m_pItem;
978f38c2
VZ
1868 EnsureVisible( child );
1869 }
1870 else
1871 {
91b8de8d 1872 wxTreeItemId next = GetNextSibling( m_key_current );
b62c3631
RR
1873// if (next == 0)
1874 if (!next)
978f38c2 1875 {
91b8de8d 1876 wxTreeItemId current = m_key_current;
978f38c2
VZ
1877 while (current && !next)
1878 {
1879 current = GetParent( current );
1880 if (current) next = GetNextSibling( current );
1881 }
1882 }
b62c3631
RR
1883// if (next != 0)
1884 if (next)
978f38c2 1885 {
88ac883a 1886 SelectItem( next, unselect_others, extended_select );
c193b707 1887 m_key_current=next.m_pItem;
978f38c2
VZ
1888 EnsureVisible( next );
1889 }
1890 }
ef44a621 1891 }
978f38c2 1892 break;
ef44a621 1893
978f38c2
VZ
1894 // <End> selects the last visible tree item
1895 case WXK_END:
1896 {
1897 wxTreeItemId last = GetRootItem();
1898
1899 while ( last.IsOk() && IsExpanded(last) )
1900 {
1901 wxTreeItemId lastChild = GetLastChild(last);
1902
1903 // it may happen if the item was expanded but then all of
1904 // its children have been deleted - so IsExpanded() returned
1905 // TRUE, but GetLastChild() returned invalid item
1906 if ( !lastChild )
1907 break;
1908
1909 last = lastChild;
1910 }
1911
1912 if ( last.IsOk() )
1913 {
1914 EnsureVisible( last );
88ac883a 1915 SelectItem( last, unselect_others, extended_select );
978f38c2
VZ
1916 }
1917 }
1918 break;
1919
1920 // <Home> selects the root item
1921 case WXK_HOME:
1922 {
1923 wxTreeItemId prev = GetRootItem();
1924 if (prev)
1925 {
1926 EnsureVisible( prev );
88ac883a 1927 SelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
1928 }
1929 }
1930 break;
1931
1932 default:
1933 event.Skip();
1934 }
edaa81ae 1935}
c801d85f 1936
91b8de8d 1937wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& flags)
4f22cf8d 1938{
dc6c62a9
RR
1939 // We have to call this here because the label in
1940 // question might just have been added and no screen
1941 // update taken place.
1942 if (m_dirty) wxYield();
1943
67a7abf7
VZ
1944 wxClientDC dc(this);
1945 PrepareDC(dc);
06b466c7
VZ
1946 wxCoord x = dc.DeviceToLogicalX( point.x );
1947 wxCoord y = dc.DeviceToLogicalY( point.y );
91b8de8d
RR
1948 int w, h;
1949 GetSize(&w, &h);
1950
1951 flags=0;
1952 if (point.x<0) flags|=wxTREE_HITTEST_TOLEFT;
1953 if (point.x>w) flags|=wxTREE_HITTEST_TORIGHT;
1954 if (point.y<0) flags|=wxTREE_HITTEST_ABOVE;
1955 if (point.y>h) flags|=wxTREE_HITTEST_BELOW;
d3a9f4af 1956
91b8de8d 1957 return m_anchor->HitTest( wxPoint(x, y), this, flags);
4f22cf8d
RR
1958}
1959
e179bd65
RR
1960/* **** */
1961
1962void wxTreeCtrl::Edit( const wxTreeItemId& item )
1963{
1964 if (!item.IsOk()) return;
1965
1966 m_currentEdit = item.m_pItem;
9dfbf520 1967
e179bd65
RR
1968 wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() );
1969 te.m_item = m_currentEdit;
1970 te.SetEventObject( this );
1971 GetEventHandler()->ProcessEvent( te );
1972
1973 if (!te.IsAllowed()) return;
8dc99046 1974
dc6c62a9
RR
1975 // We have to call this here because the label in
1976 // question might just have been added and no screen
1977 // update taken place.
1978 if (m_dirty) wxYield();
9dfbf520 1979
e179bd65
RR
1980 wxString s = m_currentEdit->GetText();
1981 int x = m_currentEdit->GetX();
1982 int y = m_currentEdit->GetY();
1983 int w = m_currentEdit->GetWidth();
1984 int h = m_currentEdit->GetHeight();
9dfbf520 1985
5f1ea0ee
RR
1986 int image_h = 0;
1987 int image_w = 0;
8dc99046
VZ
1988
1989 int image = m_currentEdit->GetCurrentImage();
1990 if ( image != NO_IMAGE )
5f1ea0ee 1991 {
8dc99046 1992 m_imageListNormal->GetSize( image, image_w, image_h );
5f1ea0ee
RR
1993 image_w += 4;
1994 }
1995 x += image_w;
1996 w -= image_w + 4; // I don't know why +4 is needed
e179bd65
RR
1997
1998 wxClientDC dc(this);
1999 PrepareDC( dc );
2000 x = dc.LogicalToDeviceX( x );
2001 y = dc.LogicalToDeviceY( y );
2002
2003 wxTreeTextCtrl *text = new wxTreeTextCtrl(
2004 this, -1, &m_renameAccept, &m_renameRes, this, s, wxPoint(x-4,y-4), wxSize(w+11,h+8) );
2005 text->SetFocus();
2006}
2007
2008void wxTreeCtrl::OnRenameTimer()
2009{
2010 Edit( m_current );
2011}
2012
2013void wxTreeCtrl::OnRenameAccept()
2014{
2015 wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
2016 le.m_item = m_currentEdit;
2017 le.SetEventObject( this );
2018 le.m_label = m_renameRes;
2019 GetEventHandler()->ProcessEvent( le );
9dfbf520 2020
e179bd65 2021 if (!le.IsAllowed()) return;
9dfbf520 2022
5f1ea0ee 2023 SetItemText( m_currentEdit, m_renameRes );
e179bd65 2024}
9dfbf520 2025
3db7be80 2026void wxTreeCtrl::OnMouse( wxMouseEvent &event )
c801d85f 2027{
fb882e1c 2028 if ( !(event.LeftUp() || event.RightDown() || event.LeftDClick() || event.Dragging()) ) return;
29d87bba 2029
bbe0af5b 2030 if ( !m_anchor ) return;
978f38c2 2031
bbe0af5b
RR
2032 wxClientDC dc(this);
2033 PrepareDC(dc);
06b466c7
VZ
2034 wxCoord x = dc.DeviceToLogicalX( event.GetX() );
2035 wxCoord y = dc.DeviceToLogicalY( event.GetY() );
29d87bba 2036
91b8de8d
RR
2037 int flags=0;
2038 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), this, flags);
2039 bool onButton = flags & wxTREE_HITTEST_ONITEMBUTTON;
978f38c2 2040
bbe0af5b
RR
2041 if (event.Dragging())
2042 {
fd9811b1 2043 if (m_dragCount == 0)
8dc99046
VZ
2044 m_dragStart = wxPoint(x,y);
2045
fd9811b1 2046 m_dragCount++;
8dc99046
VZ
2047
2048 if (m_dragCount != 3) return;
2049
2050 int command = wxEVT_COMMAND_TREE_BEGIN_DRAG;
2051 if (event.RightIsDown()) command = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
2052
fd9811b1
RR
2053 wxTreeEvent nevent( command, GetId() );
2054 nevent.m_item = m_current;
2055 nevent.SetEventObject(this);
2056 GetEventHandler()->ProcessEvent(nevent);
8dc99046 2057 return;
bbe0af5b 2058 }
fd9811b1
RR
2059 else
2060 {
2061 m_dragCount = 0;
2062 }
2063
2064 if (item == NULL) return; /* we hit the blank area */
978f38c2 2065
004fd0c8 2066 if (event.RightDown()) {
06b466c7
VZ
2067 wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,GetId());
2068 nevent.m_item=item;
2069 nevent.m_code=0;
2070 nevent.SetEventObject(this);
2071 GetEventHandler()->ProcessEvent(nevent);
2072 return;
fb882e1c
UC
2073 }
2074
9dfbf520
VZ
2075 if (event.LeftUp() && (item == m_current) &&
2076 (flags & wxTREE_HITTEST_ONITEMLABEL) &&
2077 HasFlag(wxTR_EDIT_LABELS) )
e179bd65
RR
2078 {
2079 m_renameTimer->Start( 100, TRUE );
2080 return;
2081 }
9dfbf520 2082
06b466c7
VZ
2083 // how should the selection work for this event?
2084 bool is_multiple, extended_select, unselect_others;
2085 EventFlagsToSelType(GetWindowStyleFlag(),
2086 event.ShiftDown(),
2087 event.ControlDown(),
2088 &is_multiple, &extended_select, &unselect_others);
88ac883a 2089
91b8de8d
RR
2090 if (onButton)
2091 {
2092 Toggle( item );
c193b707
VZ
2093 if (is_multiple)
2094 return;
91b8de8d
RR
2095 }
2096
88ac883a 2097 SelectItem(item, unselect_others, extended_select);
29d87bba 2098
bbe0af5b
RR
2099 if (event.LeftDClick())
2100 {
2101 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
2102 event.m_item = item;
2103 event.m_code = 0;
2104 event.SetEventObject( this );
2105 GetEventHandler()->ProcessEvent( event );
2106 }
edaa81ae 2107}
c801d85f 2108
3db7be80
RR
2109void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
2110{
bbe0af5b
RR
2111 /* after all changes have been done to the tree control,
2112 * we actually redraw the tree when everything is over */
ef44a621 2113
f65635b5
VZ
2114 if (!m_dirty)
2115 return;
ef44a621 2116
bbe0af5b 2117 m_dirty = FALSE;
3db7be80 2118
bbe0af5b 2119 CalculatePositions();
91b8de8d 2120 Refresh();
bbe0af5b 2121 AdjustMyScrollbars();
3db7be80
RR
2122}
2123
91b8de8d 2124void wxTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc )
d3a9f4af 2125{
91b8de8d
RR
2126 long text_w = 0;
2127 long text_h = 0;
9dfbf520 2128
a62867a5 2129 if (item->IsBold())
eff869aa 2130 dc.SetFont(m_boldFont);
9dfbf520 2131
91b8de8d 2132 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
a62867a5 2133 text_h+=2;
91b8de8d 2134
eff869aa
RR
2135 // restore normal font
2136 dc.SetFont( m_normalFont );
9dfbf520 2137
91b8de8d
RR
2138 int image_h = 0;
2139 int image_w = 0;
8dc99046
VZ
2140 int image = item->GetCurrentImage();
2141 if ( image != NO_IMAGE )
91b8de8d 2142 {
8dc99046 2143 m_imageListNormal->GetSize( image, image_w, image_h );
91b8de8d
RR
2144 image_w += 4;
2145 }
2146
2147 int total_h = (image_h > text_h) ? image_h : text_h;
2148
06b466c7 2149 if (total_h < 40)
f2593d0d 2150 total_h += 2; // at least 2 pixels
06b466c7 2151 else
f2593d0d 2152 total_h += total_h/10; // otherwise 10% extra spacing
91b8de8d
RR
2153
2154 item->SetHeight(total_h);
2155 if (total_h>m_lineHeight) m_lineHeight=total_h;
bbe0af5b 2156
91b8de8d
RR
2157 item->SetWidth(image_w+text_w+2);
2158}
2159
2160// -----------------------------------------------------------------------------
2161// for developper : y is now the top of the level
2162// not the middle of it !
bbe0af5b 2163void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
c801d85f 2164{
bbe0af5b 2165 int horizX = level*m_indent;
389cdc7a 2166
91b8de8d 2167 CalculateSize( item, dc );
d3a9f4af 2168
91b8de8d 2169 // set its position
cf724bce 2170 item->SetX( horizX+m_indent+m_spacing );
91b8de8d
RR
2171 item->SetY( y );
2172 y+=GetLineHeight(item);
4c681997 2173
bbe0af5b
RR
2174 if ( !item->IsExpanded() )
2175 {
f65635b5 2176 // we dont need to calculate collapsed branches
bbe0af5b
RR
2177 return;
2178 }
389cdc7a 2179
91b8de8d
RR
2180 wxArrayGenericTreeItems& children = item->GetChildren();
2181 size_t n, count = children.Count();
2182 for (n = 0; n < count; ++n )
f2593d0d 2183 CalculateLevel( children[n], dc, level+1, y ); // recurse
edaa81ae 2184}
c801d85f 2185
74bedbeb 2186void wxTreeCtrl::CalculatePositions()
c801d85f 2187{
bbe0af5b 2188 if ( !m_anchor ) return;
29d87bba 2189
bbe0af5b
RR
2190 wxClientDC dc(this);
2191 PrepareDC( dc );
29d87bba 2192
eff869aa 2193 dc.SetFont( m_normalFont );
29d87bba 2194
bbe0af5b 2195 dc.SetPen( m_dottedPen );
91b8de8d
RR
2196 //if(GetImageList() == NULL)
2197 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 2198
8dc99046 2199 int y = 2;
f65635b5 2200 CalculateLevel( m_anchor, dc, 0, y ); // start recursion
edaa81ae 2201}
c801d85f 2202
f135ff73 2203void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
c801d85f 2204{
e6527f9d
RR
2205 if (m_dirty) return;
2206
bbe0af5b
RR
2207 wxClientDC dc(this);
2208 PrepareDC(dc);
4832f7c0 2209
bbe0af5b
RR
2210 int cw = 0;
2211 int ch = 0;
2212 GetClientSize( &cw, &ch );
4832f7c0 2213
bbe0af5b
RR
2214 wxRect rect;
2215 rect.x = dc.LogicalToDeviceX( 0 );
2216 rect.width = cw;
2217 rect.y = dc.LogicalToDeviceY( item->GetY() );
2218 rect.height = ch;
f135ff73 2219
bbe0af5b 2220 Refresh( TRUE, &rect );
f135ff73 2221
bbe0af5b 2222 AdjustMyScrollbars();
edaa81ae 2223}
c801d85f
KB
2224
2225void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
2226{
e6527f9d
RR
2227 if (m_dirty) return;
2228
bbe0af5b
RR
2229 wxClientDC dc(this);
2230 PrepareDC( dc );
2231
5391f772
SB
2232 int cw = 0;
2233 int ch = 0;
2234 GetClientSize( &cw, &ch );
2235
bbe0af5b 2236 wxRect rect;
5391f772
SB
2237 rect.x = dc.LogicalToDeviceX( 0 );
2238 rect.y = dc.LogicalToDeviceY( item->GetY() );
2239 rect.width = cw;
91b8de8d 2240 rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
978f38c2 2241
bbe0af5b 2242 Refresh( TRUE, &rect );
edaa81ae 2243}
c801d85f 2244