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