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