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