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