]> git.saurik.com Git - wxWidgets.git/blame - src/generic/treectrl.cpp
Corrected byte swapping macros.
[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
88ac883a 86 void SetHeight(int h) { m_text_height = h; }
f135ff73
VZ
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;
88ac883a 142 long m_text_height, m_text_width;
f135ff73
VZ
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 191
88ac883a 192 dc.GetTextExtent( m_text, &m_text_width, &m_text_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 224
88ac883a 225 dc.GetTextExtent( m_text, &m_text_width, &m_text_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 =
88ac883a 235 m_text_height = m_text_width = 0;
f135ff73 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;
88ac883a 271 int width = m_x + m_text_width;
c801d85f 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{
88ac883a 287 if ((point.y > m_y) && (point.y < m_y + m_text_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 ); */
88ac883a 301 int w = m_text_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;
cf724bce 356 m_spacing = 18;
f135ff73
VZ
357
358 m_hilightBrush = new wxBrush
359 (
360 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT),
361 wxSOLID
362 );
363
364 m_imageListNormal =
365 m_imageListState = (wxImageList *) NULL;
978f38c2 366
bbe0af5b 367 m_dragCount = 0;
edaa81ae 368}
c801d85f 369
f135ff73
VZ
370bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
371 const wxPoint& pos, const wxSize& size,
978f38c2
VZ
372 long style,
373 const wxValidator &validator,
374 const wxString& name )
c801d85f 375{
f135ff73
VZ
376 Init();
377
a367b9b3 378 wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name );
978f38c2 379
4f22cf8d 380 SetValidator( validator );
f135ff73
VZ
381
382 SetBackgroundColour( *wxWHITE );
383 m_dottedPen = wxPen( *wxBLACK, 0, 0 );
384
385 return TRUE;
edaa81ae 386}
c801d85f 387
f135ff73 388wxTreeCtrl::~wxTreeCtrl()
c801d85f 389{
f135ff73 390 wxDELETE( m_hilightBrush );
a43a4f9d
VZ
391
392 DeleteAllItems();
edaa81ae 393}
c801d85f 394
f135ff73
VZ
395// -----------------------------------------------------------------------------
396// accessors
397// -----------------------------------------------------------------------------
398
399size_t wxTreeCtrl::GetCount() const
c801d85f 400{
4832f7c0 401 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
edaa81ae 402}
c801d85f 403
f135ff73 404void wxTreeCtrl::SetIndent(unsigned int indent)
c801d85f 405{
f135ff73 406 m_indent = indent;
cf724bce
RR
407 m_dirty = TRUE;
408 Refresh();
409}
410
411void wxTreeCtrl::SetSpacing(unsigned int spacing)
412{
413 m_spacing = spacing;
414 m_dirty = TRUE;
f135ff73
VZ
415 Refresh();
416}
74bedbeb 417
4832f7c0
VZ
418size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
419{
87138c52 420 wxCHECK_MSG( item.IsOk(), 0u, _T("invalid tree item") );
4832f7c0
VZ
421
422 return item.m_pItem->GetChildrenCount(recursively);
423}
424
f135ff73
VZ
425// -----------------------------------------------------------------------------
426// functions to work with tree items
427// -----------------------------------------------------------------------------
74bedbeb 428
f135ff73
VZ
429wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
430{
87138c52 431 wxCHECK_MSG( item.IsOk(), _T(""), _T("invalid tree item") );
4832f7c0 432
f135ff73 433 return item.m_pItem->GetText();
edaa81ae 434}
74bedbeb 435
f135ff73 436int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const
74bedbeb 437{
87138c52 438 wxCHECK_MSG( item.IsOk(), -1, _T("invalid tree item") );
4832f7c0 439
f135ff73 440 return item.m_pItem->GetImage();
edaa81ae 441}
c801d85f 442
f135ff73 443int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
c801d85f 444{
87138c52 445 wxCHECK_MSG( item.IsOk(), -1, _T("invalid tree item") );
4832f7c0 446
f135ff73 447 return item.m_pItem->GetSelectedImage();
edaa81ae 448}
c801d85f 449
f135ff73 450wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
c801d85f 451{
87138c52 452 wxCHECK_MSG( item.IsOk(), NULL, _T("invalid tree item") );
4832f7c0 453
f135ff73 454 return item.m_pItem->GetData();
edaa81ae 455}
c801d85f 456
f135ff73
VZ
457void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
458{
87138c52 459 wxCHECK_RET( item.IsOk(), _T("invalid tree item") );
4832f7c0 460
f135ff73 461 wxClientDC dc(this);
f992adf9
VZ
462 wxGenericTreeItem *pItem = item.m_pItem;
463 pItem->SetText(text, dc);
464 RefreshLine(pItem);
f135ff73 465}
c801d85f 466
f135ff73
VZ
467void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image)
468{
87138c52 469 wxCHECK_RET( item.IsOk(), _T("invalid tree item") );
4832f7c0 470
f992adf9
VZ
471 wxGenericTreeItem *pItem = item.m_pItem;
472 pItem->SetImage(image);
473 RefreshLine(pItem);
f135ff73 474}
c801d85f 475
f135ff73 476void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
c801d85f 477{
87138c52 478 wxCHECK_RET( item.IsOk(), _T("invalid tree item") );
4832f7c0 479
f992adf9
VZ
480 wxGenericTreeItem *pItem = item.m_pItem;
481 pItem->SetSelectedImage(image);
482 RefreshLine(pItem);
edaa81ae 483}
c801d85f 484
f135ff73 485void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
c801d85f 486{
87138c52 487 wxCHECK_RET( item.IsOk(), _T("invalid tree item") );
4832f7c0 488
de646ed1 489 item.m_pItem->SetData(data);
edaa81ae 490}
c801d85f 491
f135ff73 492void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
c801d85f 493{
87138c52 494 wxCHECK_RET( item.IsOk(), _T("invalid tree item") );
4832f7c0 495
f992adf9
VZ
496 wxGenericTreeItem *pItem = item.m_pItem;
497 pItem->SetHasPlus(has);
498 RefreshLine(pItem);
edaa81ae 499}
c801d85f 500
ef44a621
VZ
501void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
502{
87138c52 503 wxCHECK_RET( item.IsOk(), _T("invalid tree item") );
ef44a621
VZ
504
505 // avoid redrawing the tree if no real change
506 wxGenericTreeItem *pItem = item.m_pItem;
507 if ( pItem->IsBold() != bold )
508 {
509 pItem->SetBold(bold);
510 RefreshLine(pItem);
511 }
512}
513
f135ff73
VZ
514// -----------------------------------------------------------------------------
515// item status inquiries
516// -----------------------------------------------------------------------------
517
df875e59 518bool wxTreeCtrl::IsVisible(const wxTreeItemId& WXUNUSED(item)) const
c801d85f 519{
87138c52 520 wxFAIL_MSG(_T("not implemented"));
f135ff73 521
c801d85f 522 return TRUE;
edaa81ae 523}
c801d85f 524
f135ff73 525bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
c801d85f 526{
87138c52 527 wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid tree item") );
4832f7c0 528
f135ff73 529 return !item.m_pItem->GetChildren().IsEmpty();
edaa81ae 530}
c801d85f 531
f135ff73 532bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
c801d85f 533{
87138c52 534 wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid tree item") );
4832f7c0 535
f135ff73
VZ
536 return item.m_pItem->IsExpanded();
537}
29d87bba 538
f135ff73
VZ
539bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
540{
87138c52 541 wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid tree item") );
4832f7c0 542
f135ff73
VZ
543 return item.m_pItem->HasHilight();
544}
29d87bba 545
ef44a621
VZ
546bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
547{
87138c52 548 wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid tree item") );
ef44a621
VZ
549
550 return item.m_pItem->IsBold();
551}
552
f135ff73
VZ
553// -----------------------------------------------------------------------------
554// navigation
555// -----------------------------------------------------------------------------
29d87bba 556
f135ff73
VZ
557wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
558{
87138c52 559 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), _T("invalid tree item") );
389cdc7a 560
f135ff73
VZ
561 return item.m_pItem->GetParent();
562}
29d87bba 563
f135ff73
VZ
564wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
565{
87138c52 566 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), _T("invalid tree item") );
29d87bba 567
f135ff73
VZ
568 cookie = 0;
569 return GetNextChild(item, cookie);
570}
29d87bba 571
f135ff73
VZ
572wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
573{
87138c52 574 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), _T("invalid tree item") );
29d87bba 575
4832f7c0
VZ
576 wxArrayTreeItems& children = item.m_pItem->GetChildren();
577 if ( (size_t)cookie < children.Count() )
578 {
978f38c2 579 return children.Item(cookie++);
4832f7c0
VZ
580 }
581 else
582 {
583 // there are no more of them
1e6d9499 584 return wxTreeItemId();
4832f7c0 585 }
f135ff73 586}
29d87bba 587
978f38c2
VZ
588wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
589{
87138c52 590 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), _T("invalid tree item") );
978f38c2
VZ
591
592 wxArrayTreeItems& children = item.m_pItem->GetChildren();
0a240683 593 return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
978f38c2
VZ
594}
595
f135ff73
VZ
596wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
597{
87138c52 598 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), _T("invalid tree item") );
f135ff73
VZ
599
600 wxGenericTreeItem *i = item.m_pItem;
601 wxGenericTreeItem *parent = i->GetParent();
602 if ( parent == NULL )
603 {
604 // root item doesn't have any siblings
1e6d9499 605 return wxTreeItemId();
edaa81ae 606 }
4832f7c0 607
f135ff73
VZ
608 wxArrayTreeItems& siblings = parent->GetChildren();
609 int index = siblings.Index(i);
3c67202d 610 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 611
f135ff73 612 size_t n = (size_t)(index + 1);
fdd8d7b5 613 return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
edaa81ae 614}
c801d85f 615
f135ff73 616wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
c801d85f 617{
87138c52 618 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), _T("invalid tree item") );
f135ff73
VZ
619
620 wxGenericTreeItem *i = item.m_pItem;
621 wxGenericTreeItem *parent = i->GetParent();
622 if ( parent == NULL )
c801d85f 623 {
f135ff73 624 // root item doesn't have any siblings
1e6d9499 625 return wxTreeItemId();
edaa81ae 626 }
4832f7c0 627
f135ff73
VZ
628 wxArrayTreeItems& siblings = parent->GetChildren();
629 int index = siblings.Index(i);
3c67202d 630 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
29d87bba 631
fdd8d7b5
VZ
632 return index == 0 ? wxTreeItemId()
633 : wxTreeItemId(siblings[(size_t)(index - 1)]);
f135ff73 634}
389cdc7a 635
f135ff73
VZ
636wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
637{
87138c52 638 wxFAIL_MSG(_T("not implemented"));
29d87bba 639
1e6d9499 640 return wxTreeItemId();
f135ff73 641}
29d87bba 642
f135ff73
VZ
643wxTreeItemId wxTreeCtrl::GetNextVisible(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();
f135ff73 650}
29d87bba 651
f135ff73
VZ
652wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
653{
87138c52 654 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), _T("invalid tree item") );
29d87bba 655
87138c52 656 wxFAIL_MSG(_T("not implemented"));
29d87bba 657
1e6d9499 658 return wxTreeItemId();
edaa81ae 659}
c801d85f 660
f135ff73
VZ
661// -----------------------------------------------------------------------------
662// operations
663// -----------------------------------------------------------------------------
664
665wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
666 size_t previous,
667 const wxString& text,
668 int image, int selImage,
669 wxTreeItemData *data)
c801d85f 670{
f135ff73
VZ
671 wxGenericTreeItem *parent = parentId.m_pItem;
672 if ( !parent )
673 {
674 // should we give a warning here?
675 return AddRoot(text, image, selImage, data);
676 }
4832f7c0 677
f135ff73
VZ
678 wxClientDC dc(this);
679 wxGenericTreeItem *item = new wxGenericTreeItem(parent,
680 text, dc,
681 image, selImage,
682 data);
74bedbeb 683
f135ff73 684 if ( data != NULL )
c801d85f 685 {
f135ff73
VZ
686 data->m_pItem = item;
687 }
74bedbeb 688
f135ff73 689 parent->Insert( item, previous );
ef44a621 690
3db7be80 691 m_dirty = TRUE;
389cdc7a 692
f135ff73 693 return item;
4c681997
RR
694}
695
f135ff73
VZ
696wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
697 int image, int selImage,
698 wxTreeItemData *data)
4c681997 699{
87138c52 700 wxCHECK_MSG( !m_anchor, wxTreeItemId(), _T("tree can have only one root") );
389cdc7a 701
f135ff73
VZ
702 wxClientDC dc(this);
703 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
704 image, selImage, data);
705 if ( data != NULL )
706 {
707 data->m_pItem = m_anchor;
708 }
389cdc7a 709
f135ff73 710 AdjustMyScrollbars();
a32dd690 711 Refresh();
a32dd690 712
f135ff73 713 return m_anchor;
edaa81ae 714}
c801d85f 715
f135ff73
VZ
716wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
717 const wxString& text,
718 int image, int selImage,
719 wxTreeItemData *data)
c801d85f 720{
f135ff73 721 return DoInsertItem(parent, 0u, text, image, selImage, data);
edaa81ae 722}
c801d85f 723
f135ff73
VZ
724wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
725 const wxTreeItemId& idPrevious,
726 const wxString& text,
727 int image, int selImage,
728 wxTreeItemData *data)
c801d85f 729{
f135ff73
VZ
730 wxGenericTreeItem *parent = parentId.m_pItem;
731 if ( !parent )
732 {
733 // should we give a warning here?
734 return AddRoot(text, image, selImage, data);
735 }
c801d85f 736
f135ff73 737 int index = parent->GetChildren().Index(idPrevious.m_pItem);
3c67202d 738 wxASSERT_MSG( index != wxNOT_FOUND,
87138c52 739 _T("previous item in wxTreeCtrl::InsertItem() is not a sibling") );
f135ff73 740 return DoInsertItem(parentId, (size_t)index, text, image, selImage, data);
edaa81ae 741}
c801d85f 742
f135ff73
VZ
743wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId,
744 const wxString& text,
745 int image, int selImage,
746 wxTreeItemData *data)
74bedbeb 747{
f135ff73
VZ
748 wxGenericTreeItem *parent = parentId.m_pItem;
749 if ( !parent )
750 {
751 // should we give a warning here?
752 return AddRoot(text, image, selImage, data);
753 }
754
755 return DoInsertItem(parent, parent->GetChildren().Count(), text,
756 image, selImage, data);
74bedbeb
VZ
757}
758
a43a4f9d
VZ
759void wxTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
760{
761 wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
762 event.m_item = item;
763 event.SetEventObject( this );
764 ProcessEvent( event );
765}
766
372edb9d
VZ
767void wxTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
768{
769 wxGenericTreeItem *item = itemId.m_pItem;
a43a4f9d 770 item->DeleteChildren(this);
372edb9d
VZ
771
772 m_dirty = TRUE;
773}
774
f135ff73 775void wxTreeCtrl::Delete(const wxTreeItemId& itemId)
c801d85f 776{
f135ff73 777 wxGenericTreeItem *item = itemId.m_pItem;
ff5bf259
VZ
778 wxGenericTreeItem *parent = item->GetParent();
779
780 if ( parent )
781 {
782 parent->GetChildren().Remove(item);
783 }
f135ff73 784
a43a4f9d
VZ
785 item->DeleteChildren(this);
786 SendDeleteEvent(item);
f135ff73
VZ
787 delete item;
788
4bb19cfb 789 m_dirty = TRUE;
edaa81ae 790}
c801d85f 791
f135ff73 792void wxTreeCtrl::DeleteAllItems()
c801d85f 793{
f135ff73
VZ
794 if ( m_anchor )
795 {
a43a4f9d 796 m_anchor->DeleteChildren(this);
f135ff73 797 delete m_anchor;
a43a4f9d 798
f135ff73
VZ
799 m_anchor = NULL;
800
4bb19cfb 801 m_dirty = TRUE;
f135ff73 802 }
edaa81ae
RR
803}
804
f135ff73 805void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
edaa81ae 806{
f135ff73
VZ
807 wxGenericTreeItem *item = itemId.m_pItem;
808
978f38c2 809 if ( !item->HasPlus() )
4bb19cfb 810 return;
978f38c2 811
f135ff73
VZ
812 if ( item->IsExpanded() )
813 return;
814
815 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
816 event.m_item = item;
817 event.SetEventObject( this );
818 if ( ProcessEvent( event ) && event.m_code )
819 {
820 // cancelled by program
821 return;
822 }
4832f7c0 823
f135ff73 824 item->Expand();
28ab302b 825 CalculatePositions();
f135ff73
VZ
826
827 RefreshSubtree(item);
828
829 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
830 ProcessEvent( event );
edaa81ae
RR
831}
832
f135ff73 833void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
edaa81ae 834{
f135ff73
VZ
835 wxGenericTreeItem *item = itemId.m_pItem;
836
837 if ( !item->IsExpanded() )
838 return;
839
840 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
841 event.m_item = item;
842 event.SetEventObject( this );
843 if ( ProcessEvent( event ) && event.m_code )
edaa81ae 844 {
f135ff73
VZ
845 // cancelled by program
846 return;
847 }
4832f7c0 848
f135ff73
VZ
849 item->Collapse();
850
851 wxArrayTreeItems& children = item->GetChildren();
852 size_t count = children.Count();
853 for ( size_t n = 0; n < count; n++ )
854 {
855 Collapse(children[n]);
edaa81ae 856 }
f135ff73
VZ
857
858 CalculatePositions();
859
860 RefreshSubtree(item);
861
862 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
863 ProcessEvent( event );
edaa81ae 864}
c801d85f 865
f135ff73 866void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
c801d85f 867{
f135ff73 868 Collapse(item);
372edb9d 869 DeleteChildren(item);
edaa81ae 870}
c801d85f 871
f135ff73 872void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
c801d85f 873{
f135ff73 874 wxGenericTreeItem *item = itemId.m_pItem;
389cdc7a 875
f135ff73
VZ
876 if ( item->IsExpanded() )
877 Collapse(itemId);
878 else
879 Expand(itemId);
880}
389cdc7a 881
f135ff73
VZ
882void wxTreeCtrl::Unselect()
883{
884 if ( m_current )
885 {
886 m_current->SetHilight( FALSE );
887 RefreshLine( m_current );
888 }
edaa81ae 889}
c801d85f 890
88ac883a 891void wxTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
389cdc7a 892{
88ac883a
VZ
893 item->SetHilight(FALSE);
894 RefreshLine(item);
895
896 if (item->HasChildren())
897 {
898 wxArrayTreeItems& children = item->GetChildren();
899 size_t count = children.Count();
900 for ( size_t n = 0; n < count; ++n )
901 UnselectAllChildren(children[n]);
902 }
903}
f135ff73 904
88ac883a
VZ
905void wxTreeCtrl::UnselectAll()
906{
907 UnselectAllChildren(GetRootItem().m_pItem);
908}
909
910// Recursive function !
911// To stop we must have crt_item<last_item
912// Algorithm =
913// Tag all next children, when no more children,
914// Move to parent (not to tag)
915// Keep going, if we found last_item, we stop
916bool wxTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
917{
918 wxGenericTreeItem *parent = crt_item->GetParent();
919
920 if ( parent == NULL ) // This is root item
921 return TagAllChildrenUntilLast(crt_item, last_item, select);
922
923 wxArrayTreeItems& children = parent->GetChildren();
924 int index = children.Index(crt_item);
925 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
926
927 size_t count = children.Count();
928 for (size_t n=(size_t)(index+1); n<count; ++n)
25889d3c 929 if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
88ac883a
VZ
930
931 return TagNextChildren(parent, last_item, select);
932}
933
934bool wxTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
935{
936 crt_item->SetHilight(select);
937 RefreshLine(crt_item);
938
25889d3c 939 if (crt_item==last_item) return TRUE;
88ac883a
VZ
940
941 if (crt_item->HasChildren())
942 {
943 wxArrayTreeItems& children = crt_item->GetChildren();
944 size_t count = children.Count();
945 for ( size_t n = 0; n < count; ++n )
25889d3c 946 if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
88ac883a
VZ
947 }
948
25889d3c 949 return FALSE;
88ac883a
VZ
950}
951
952void wxTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *item2)
953{
954 // item2 is not necessary after item1
955 wxGenericTreeItem *first=NULL, *last=NULL;
956
957 // choice first' and 'last' between item1 and item2
958 if (item1->GetY()<item2->GetY())
959 {
960 first=item1;
961 last=item2;
962 }
963 else
964 {
965 first=item2;
966 last=item1;
967 }
968
969 bool select=m_current->HasHilight();
970
971 if (TagAllChildrenUntilLast(first,last,select)) return;
972
910f1f8c 973/*
88ac883a 974 cout << first->GetText() << " " << last->GetText() << " " << (int) select << endl;
910f1f8c 975*/
88ac883a
VZ
976 TagNextChildren(first,last,select);
977}
978
979void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId,
980 bool unselect_others,
981 bool extended_select)
982{
983 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
984
985 //wxCHECK_RET( ( (!unselect_others) && is_single),
986 // _T("this is a single selection tree") );
987
988 // to keep going anyhow !!!
989 if (is_single)
990 {
25889d3c
JS
991 unselect_others=TRUE;
992 extended_select=FALSE;
88ac883a
VZ
993 }
994
995 wxGenericTreeItem *item = itemId.m_pItem;
996
f135ff73
VZ
997 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
998 event.m_item = item;
999 event.m_itemOld = m_current;
1000 event.SetEventObject( this );
88ac883a
VZ
1001 // Here we don't send any selection mode yet ! TO SEE
1002
910f1f8c 1003/*
88ac883a
VZ
1004 if (m_current)
1005 cout << m_current->GetText() << " " << (int)m_current->HasHilight() << endl;
910f1f8c 1006*/
88ac883a 1007
6daa0637 1008 if ( GetEventHandler()->ProcessEvent( event ) && event.WasVetoed() )
f135ff73
VZ
1009 return;
1010
910f1f8c 1011/*
88ac883a 1012 if (m_current)
910f1f8c
RR
1013 cout << m_current->GetText() << " " << (int)m_current->HasHilight() << endl;
1014*/
88ac883a
VZ
1015
1016 // ctrl press
1017 if (unselect_others)
389cdc7a 1018 {
88ac883a
VZ
1019 if (is_single) Unselect(); // to speed up thing
1020 else UnselectAll();
edaa81ae 1021 }
f135ff73 1022
88ac883a
VZ
1023 // shift press
1024 if (extended_select)
1025 {
1026 if (m_current == NULL) m_current=GetRootItem().m_pItem;
1027 // don't change the mark (m_current)
1028 SelectItemRange(m_current, item);
1029 }
1030 else
1031 {
25889d3c 1032 bool select=TRUE; // the default
88ac883a
VZ
1033
1034 // Check if we need to toggle hilight (ctrl mode)
1035 if (!unselect_others)
1036 select=!item->HasHilight();
1037
1038 m_current = item;
1039 m_current->SetHilight(select);
1040 RefreshLine( m_current );
1041 }
389cdc7a 1042
f135ff73 1043 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
6daa0637 1044 GetEventHandler()->ProcessEvent( event );
389cdc7a
VZ
1045}
1046
6daa0637 1047void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
c801d85f 1048{
0659e7ee 1049 wxGenericTreeItem *gitem = item.m_pItem;
ef44a621 1050
f65635b5
VZ
1051 // first expand all parent branches
1052 wxGenericTreeItem *parent = gitem->GetParent();
1053 while ( parent && !parent->IsExpanded() )
1054 {
1055 Expand(parent);
1056
1057 parent = parent->GetParent();
1058 }
1059
1060 // now scroll to the item
0659e7ee 1061 int item_y = gitem->GetY();
ef44a621 1062
0659e7ee
RR
1063 int start_x = 0;
1064 int start_y = 0;
1065 ViewStart( &start_x, &start_y );
1066 start_y *= 10;
978f38c2 1067
a93109d5
RR
1068 int client_h = 0;
1069 int client_w = 0;
1070 GetClientSize( &client_w, &client_h );
ef44a621 1071
0659e7ee
RR
1072 if (item_y < start_y+3)
1073 {
1074 int x = 0;
1075 int y = 0;
1076 m_anchor->GetSize( x, y );
1077 y += 2*m_lineHeight;
1078 int x_pos = GetScrollPos( wxHORIZONTAL );
f65635b5 1079 SetScrollbars( 10, 10, x/10, y/10, x_pos, (item_y-client_h/2)/10 );
0659e7ee 1080 }
88ac883a 1081 else if (item_y > start_y+client_h-16)
0659e7ee
RR
1082 {
1083 int x = 0;
1084 int y = 0;
1085 m_anchor->GetSize( x, y );
1086 y += 2*m_lineHeight;
1087 int x_pos = GetScrollPos( wxHORIZONTAL );
a93109d5 1088 SetScrollbars( 10, 10, x/10, y/10, x_pos, (item_y-client_h/2)/10 );
0659e7ee 1089 }
edaa81ae 1090}
c801d85f 1091
df875e59 1092void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
c801d85f 1093{
87138c52 1094 wxFAIL_MSG(_T("not implemented"));
edaa81ae 1095}
c801d85f 1096
df875e59
RR
1097wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
1098 wxClassInfo* WXUNUSED(textCtrlClass) )
c801d85f 1099{
87138c52 1100 wxFAIL_MSG(_T("not implemented"));
c801d85f 1101
bbe0af5b 1102 return (wxTextCtrl*)NULL;
edaa81ae 1103}
c801d85f 1104
f135ff73 1105wxTextCtrl *wxTreeCtrl::GetEditControl() const
c801d85f 1106{
87138c52 1107 wxFAIL_MSG(_T("not implemented"));
c801d85f 1108
0659e7ee 1109 return (wxTextCtrl*)NULL;
edaa81ae 1110}
c801d85f 1111
df875e59 1112void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
74bedbeb 1113{
87138c52 1114 wxFAIL_MSG(_T("not implemented"));
74bedbeb
VZ
1115}
1116
e1ee62bd
VZ
1117// FIXME: tree sorting functions are not reentrant and not MT-safe!
1118static wxTreeCtrl *s_treeBeingSorted = NULL;
0659e7ee 1119
e1ee62bd
VZ
1120static int tree_ctrl_compare_func(wxGenericTreeItem **item1,
1121 wxGenericTreeItem **item2)
edaa81ae 1122{
87138c52 1123 wxCHECK_MSG( s_treeBeingSorted, 0, _T("bug in wxTreeCtrl::SortChildren()") );
e1ee62bd
VZ
1124
1125 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
0659e7ee
RR
1126}
1127
e1ee62bd
VZ
1128int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
1129 const wxTreeItemId& item2)
0659e7ee 1130{
87138c52 1131 return wxStrcmp(GetItemText(item1), GetItemText(item2));
e1ee62bd
VZ
1132}
1133
1134void wxTreeCtrl::SortChildren(const wxTreeItemId& itemId)
1135{
87138c52 1136 wxCHECK_RET( itemId.IsOk(), _T("invalid tree item") );
e1ee62bd
VZ
1137
1138 wxGenericTreeItem *item = itemId.m_pItem;
978f38c2 1139
e1ee62bd 1140 wxCHECK_RET( !s_treeBeingSorted,
87138c52 1141 _T("wxTreeCtrl::SortChildren is not reentrant") );
e1ee62bd
VZ
1142
1143 wxArrayTreeItems& children = item->GetChildren();
1144 if ( children.Count() > 1 )
1145 {
1146 s_treeBeingSorted = this;
1147 children.Sort(tree_ctrl_compare_func);
1148 s_treeBeingSorted = NULL;
978f38c2 1149
e1ee62bd
VZ
1150 m_dirty = TRUE;
1151 }
1152 //else: don't make the tree dirty as nothing changed
edaa81ae
RR
1153}
1154
f135ff73 1155wxImageList *wxTreeCtrl::GetImageList() const
edaa81ae 1156{
f135ff73 1157 return m_imageListNormal;
edaa81ae
RR
1158}
1159
f135ff73 1160wxImageList *wxTreeCtrl::GetStateImageList() const
c801d85f 1161{
f135ff73 1162 return m_imageListState;
edaa81ae 1163}
c801d85f 1164
f135ff73 1165void wxTreeCtrl::SetImageList(wxImageList *imageList)
e2414cbe 1166{
d30b4d20
KB
1167 m_imageListNormal = imageList;
1168 // calculate a m_lineHeight value from the image sizes
1169 wxPaintDC dc(this);
1170 PrepareDC( dc );
1171 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1172 int
1173 width = 0,
1174 height = 0,
1175 n = m_imageListNormal->GetImageCount();
1176 for(int i = 0; i < n ; i++)
1177 {
1178 m_imageListNormal->GetSize(i, width, height);
f0594f42 1179 height += height/5; //20% extra spacing
d30b4d20
KB
1180 if(height > m_lineHeight) m_lineHeight = height;
1181 }
edaa81ae 1182}
e2414cbe 1183
f135ff73 1184void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
e2414cbe 1185{
f135ff73 1186 m_imageListState = imageList;
edaa81ae 1187}
e2414cbe 1188
f135ff73
VZ
1189// -----------------------------------------------------------------------------
1190// helpers
1191// -----------------------------------------------------------------------------
0659e7ee 1192
74bedbeb 1193void wxTreeCtrl::AdjustMyScrollbars()
c801d85f 1194{
0659e7ee
RR
1195 if (m_anchor)
1196 {
1197 int x = 0;
1198 int y = 0;
1199 m_anchor->GetSize( x, y );
1200 y += 2*m_lineHeight;
1201 int x_pos = GetScrollPos( wxHORIZONTAL );
1202 int y_pos = GetScrollPos( wxVERTICAL );
1203 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
1204 }
1205 else
1206 {
1207 SetScrollbars( 0, 0, 0, 0 );
1208 }
edaa81ae 1209}
c801d85f 1210
ef44a621
VZ
1211void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1212{
f65635b5 1213 // render bold items in bold
bbe0af5b
RR
1214 wxFont fontOld;
1215 wxFont fontNew;
978f38c2 1216
bbe0af5b
RR
1217 if (item->IsBold())
1218 {
1219 fontOld = dc.GetFont();
1220 if (fontOld.Ok())
1221 {
f65635b5 1222 // VZ: is there any better way to make a bold variant of old font?
bbe0af5b
RR
1223 fontNew = wxFont( fontOld.GetPointSize(),
1224 fontOld.GetFamily(),
1225 fontOld.GetStyle(),
1226 wxBOLD,
1227 fontOld.GetUnderlined());
1228 dc.SetFont(fontNew);
1229 }
1230 else
1231 {
87138c52 1232 wxFAIL_MSG(_T("wxDC::GetFont() failed!"));
bbe0af5b
RR
1233 }
1234 }
ef44a621 1235
bbe0af5b
RR
1236 long text_w = 0;
1237 long text_h = 0;
1238 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
ef44a621 1239
bbe0af5b
RR
1240 int image_h = 0;
1241 int image_w = 0;
1242 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1243 {
1244 m_imageListNormal->GetSize( item->GetSelectedImage(), image_w, image_h );
1245 image_w += 4;
978f38c2 1246 }
bbe0af5b
RR
1247 else if (item->GetImage() != -1)
1248 {
1249 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
1250 image_w += 4;
1251 }
ef44a621 1252
d30b4d20 1253 int total_h = (image_h > text_h) ? image_h : text_h;
f0594f42
KB
1254 if(m_lineHeight > total_h) total_h = m_lineHeight;
1255
5679f335 1256 dc.DrawRectangle( item->GetX()-2, item->GetY(), image_w+text_w+4, total_h );
ef44a621 1257
bbe0af5b
RR
1258 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1259 {
d30b4d20 1260 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h );
bbe0af5b 1261 m_imageListNormal->Draw( item->GetSelectedImage(), dc,
49cd56ef 1262 item->GetX(),
d701d432 1263 item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0),
bbe0af5b
RR
1264 wxIMAGELIST_DRAW_TRANSPARENT );
1265 dc.DestroyClippingRegion();
1266 }
1267 else if (item->GetImage() != -1)
1268 {
d30b4d20 1269 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h );
bbe0af5b 1270 m_imageListNormal->Draw( item->GetImage(), dc,
49cd56ef 1271 item->GetX(),
d701d432 1272 item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0),
bbe0af5b
RR
1273 wxIMAGELIST_DRAW_TRANSPARENT );
1274 dc.DestroyClippingRegion();
1275 }
ef44a621 1276
bbe0af5b 1277 dc.SetBackgroundMode(wxTRANSPARENT);
d30b4d20 1278 dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY()
49cd56ef 1279 + ((total_h > text_h) ? (total_h - text_h)/2 : 0));
ef44a621 1280
f65635b5 1281 // restore normal font for bold items
bbe0af5b
RR
1282 if (fontOld.Ok())
1283 {
1284 dc.SetFont( fontOld);
1285 }
ef44a621
VZ
1286}
1287
16c1f7f3 1288void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
c801d85f 1289{
bbe0af5b 1290 int horizX = level*m_indent;
389cdc7a 1291
cf724bce 1292 item->SetX( horizX+m_indent+m_spacing );
f0594f42 1293 item->SetY( y-m_lineHeight/2 );
bbe0af5b 1294 item->SetHeight( m_lineHeight );
389cdc7a 1295
cf724bce 1296 item->SetCross( horizX+m_indent, y );
4c681997 1297
bbe0af5b 1298 int oldY = y;
389cdc7a 1299
bbe0af5b
RR
1300 int exposed_x = dc.LogicalToDeviceX( 0 );
1301 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
4832f7c0 1302
bbe0af5b
RR
1303 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
1304 {
1305 int startX = horizX;
cf724bce 1306 int endX = horizX + (m_indent-5);
29d87bba 1307
cf724bce 1308// if (!item->HasChildren()) endX += (m_indent+5);
bbe0af5b 1309 if (!item->HasChildren()) endX += 20;
4832f7c0 1310
bbe0af5b 1311 dc.DrawLine( startX, y, endX, y );
29d87bba 1312
bbe0af5b
RR
1313 if (item->HasPlus())
1314 {
cf724bce 1315 dc.DrawLine( horizX+(m_indent+5), y, horizX+(m_indent+15), y );
bbe0af5b
RR
1316 dc.SetPen( *wxGREY_PEN );
1317 dc.SetBrush( *wxWHITE_BRUSH );
cf724bce 1318 dc.DrawRectangle( horizX+(m_indent-5), y-4, 11, 9 );
bbe0af5b 1319 dc.SetPen( *wxBLACK_PEN );
cf724bce 1320 dc.DrawLine( horizX+(m_indent-2), y, horizX+(m_indent+3), y );
bbe0af5b
RR
1321
1322 if (!item->IsExpanded())
d30b4d20 1323 {
cf724bce 1324 dc.DrawLine( horizX+m_indent, y-2, horizX+m_indent, y+3 );
d30b4d20 1325 }
bbe0af5b 1326 }
c801d85f 1327
bbe0af5b
RR
1328 if (item->HasHilight())
1329 {
1330 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
a367b9b3 1331
bbe0af5b 1332 dc.SetBrush( *m_hilightBrush );
4832f7c0 1333
bbe0af5b
RR
1334 if (m_hasFocus)
1335 dc.SetPen( *wxBLACK_PEN );
1336 else
1337 dc.SetPen( *wxTRANSPARENT_PEN );
4832f7c0 1338
bbe0af5b 1339 PaintItem(item, dc);
f135ff73 1340
bbe0af5b
RR
1341 dc.SetPen( *wxBLACK_PEN );
1342 dc.SetTextForeground( *wxBLACK );
1343 dc.SetBrush( *wxWHITE_BRUSH );
1344 }
1345 else
1346 {
1347 dc.SetBrush( *wxWHITE_BRUSH );
1348 dc.SetPen( *wxTRANSPARENT_PEN );
4832f7c0 1349
bbe0af5b 1350 PaintItem(item, dc);
4832f7c0 1351
bbe0af5b
RR
1352 dc.SetPen( *wxBLACK_PEN );
1353 }
f135ff73 1354 }
e2414cbe 1355
bbe0af5b
RR
1356 if (item->IsExpanded())
1357 {
1358 int semiOldY = y;
389cdc7a 1359
bbe0af5b
RR
1360 wxArrayTreeItems& children = item->GetChildren();
1361 size_t count = children.Count();
1362 for ( size_t n = 0; n < count; n++ )
1363 {
1364 y += m_lineHeight;
1365 semiOldY = y;
1366 PaintLevel( children[n], dc, level+1, y );
1367 }
389cdc7a 1368
f65635b5
VZ
1369 // it may happen that the item is expanded but has no items (when you
1370 // delete all its children for example) - don't draw the vertical line
1371 // in this case
1372 if (count > 0)
cf724bce 1373 dc.DrawLine( horizX+m_indent, oldY+5, horizX+m_indent, semiOldY );
bbe0af5b 1374 }
4c681997 1375}
c801d85f 1376
f135ff73
VZ
1377// -----------------------------------------------------------------------------
1378// wxWindows callbacks
1379// -----------------------------------------------------------------------------
1380
3db7be80 1381void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
c801d85f 1382{
0659e7ee
RR
1383 if ( !m_anchor )
1384 return;
c801d85f 1385
0659e7ee
RR
1386 wxPaintDC dc(this);
1387 PrepareDC( dc );
29d87bba 1388
f60d0f94 1389 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT ) );
29d87bba 1390
0659e7ee 1391 dc.SetPen( m_dottedPen );
d30b4d20
KB
1392 if(GetImageList() == NULL)
1393 m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 1394
0659e7ee
RR
1395 int y = m_lineHeight / 2 + 2;
1396 PaintLevel( m_anchor, dc, 0, y );
edaa81ae 1397}
c801d85f 1398
3db7be80 1399void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
c801d85f 1400{
0659e7ee 1401 m_hasFocus = TRUE;
978f38c2 1402
bbe0af5b 1403 if (m_current) RefreshLine( m_current );
edaa81ae 1404}
c801d85f 1405
3db7be80 1406void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
c801d85f 1407{
0659e7ee 1408 m_hasFocus = FALSE;
978f38c2 1409
bbe0af5b 1410 if (m_current) RefreshLine( m_current );
edaa81ae 1411}
c801d85f
KB
1412
1413void wxTreeCtrl::OnChar( wxKeyEvent &event )
1414{
978f38c2
VZ
1415 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1416 te.m_code = event.KeyCode();
1417 te.SetEventObject( this );
1418 GetEventHandler()->ProcessEvent( te );
435fe83e 1419
978f38c2
VZ
1420 if (m_current == 0)
1421 {
1422 event.Skip();
1423 return;
1424 }
ef44a621 1425
88ac883a
VZ
1426 bool is_multiple=(GetWindowStyleFlag() & wxTR_MULTIPLE);
1427 bool extended_select=(event.ShiftDown() && is_multiple);
1428 bool unselect_others=!(extended_select || (event.ControlDown() && is_multiple));
1429
978f38c2
VZ
1430 switch (event.KeyCode())
1431 {
1432 case '+':
1433 case WXK_ADD:
1434 if (m_current->HasPlus() && !IsExpanded(m_current))
1435 {
1436 Expand(m_current);
1437 }
1438 break;
ef44a621 1439
978f38c2
VZ
1440 case '-':
1441 case WXK_SUBTRACT:
1442 if (IsExpanded(m_current))
1443 {
1444 Collapse(m_current);
1445 }
1446 break;
ef44a621 1447
978f38c2
VZ
1448 case '*':
1449 case WXK_MULTIPLY:
1450 Toggle(m_current);
1451 break;
ef44a621 1452
978f38c2
VZ
1453 case ' ':
1454 case WXK_RETURN:
1455 {
1456 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1457 event.m_item = m_current;
1458 event.m_code = 0;
1459 event.SetEventObject( this );
1460 GetEventHandler()->ProcessEvent( event );
1461 }
1462 break;
ef44a621 1463
978f38c2
VZ
1464 // up goes to the previous sibling or to the last of its children if
1465 // it's expanded
1466 case WXK_UP:
1467 {
1468 wxTreeItemId prev = GetPrevSibling( m_current );
1469 if (!prev)
1470 {
1471 prev = GetParent( m_current );
90e58684
RR
1472 if (prev)
1473 {
1474 long cockie = 0;
1475 wxTreeItemId current = m_current;
1476 if (current == GetFirstChild( prev, cockie ))
1477 {
1478 // otherwise we return to where we came from
88ac883a 1479 SelectItem( prev, unselect_others, extended_select );
90e58684
RR
1480 EnsureVisible( prev );
1481 break;
1482 }
978f38c2
VZ
1483 }
1484 }
1485 if (prev)
1486 {
69a282d4 1487 while ( IsExpanded(prev) && HasChildren(prev) )
978f38c2 1488 {
69a282d4
VZ
1489 wxTreeItemId child = GetLastChild(prev);
1490 if ( child )
1491 {
1492 prev = child;
1493 }
978f38c2 1494 }
69a282d4 1495
88ac883a 1496 SelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
1497 EnsureVisible( prev );
1498 }
1499 }
1500 break;
ef44a621 1501
978f38c2
VZ
1502 // left arrow goes to the parent
1503 case WXK_LEFT:
1504 {
1505 wxTreeItemId prev = GetParent( m_current );
1506 if (prev)
1507 {
1508 EnsureVisible( prev );
88ac883a 1509 SelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
1510 }
1511 }
1512 break;
ef44a621 1513
978f38c2
VZ
1514 case WXK_RIGHT:
1515 // this works the same as the down arrow except that we also expand the
1516 // item if it wasn't expanded yet
1517 Expand(m_current);
1518 // fall through
1519
1520 case WXK_DOWN:
ef44a621 1521 {
69a282d4 1522 if (IsExpanded(m_current) && HasChildren(m_current))
978f38c2
VZ
1523 {
1524 long cookie = 0;
1525 wxTreeItemId child = GetFirstChild( m_current, cookie );
88ac883a 1526 SelectItem( child, unselect_others, extended_select );
978f38c2
VZ
1527 EnsureVisible( child );
1528 }
1529 else
1530 {
1531 wxTreeItemId next = GetNextSibling( m_current );
1532 if (next == 0)
1533 {
1534 wxTreeItemId current = m_current;
1535 while (current && !next)
1536 {
1537 current = GetParent( current );
1538 if (current) next = GetNextSibling( current );
1539 }
1540 }
1541 if (next != 0)
1542 {
88ac883a 1543 SelectItem( next, unselect_others, extended_select );
978f38c2
VZ
1544 EnsureVisible( next );
1545 }
1546 }
ef44a621 1547 }
978f38c2 1548 break;
ef44a621 1549
978f38c2
VZ
1550 // <End> selects the last visible tree item
1551 case WXK_END:
1552 {
1553 wxTreeItemId last = GetRootItem();
1554
1555 while ( last.IsOk() && IsExpanded(last) )
1556 {
1557 wxTreeItemId lastChild = GetLastChild(last);
1558
1559 // it may happen if the item was expanded but then all of
1560 // its children have been deleted - so IsExpanded() returned
1561 // TRUE, but GetLastChild() returned invalid item
1562 if ( !lastChild )
1563 break;
1564
1565 last = lastChild;
1566 }
1567
1568 if ( last.IsOk() )
1569 {
1570 EnsureVisible( last );
88ac883a 1571 SelectItem( last, unselect_others, extended_select );
978f38c2
VZ
1572 }
1573 }
1574 break;
1575
1576 // <Home> selects the root item
1577 case WXK_HOME:
1578 {
1579 wxTreeItemId prev = GetRootItem();
1580 if (prev)
1581 {
1582 EnsureVisible( prev );
88ac883a 1583 SelectItem( prev, unselect_others, extended_select );
978f38c2
VZ
1584 }
1585 }
1586 break;
1587
1588 default:
1589 event.Skip();
1590 }
edaa81ae 1591}
c801d85f 1592
4f22cf8d
RR
1593wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& WXUNUSED(flags))
1594{
67a7abf7
VZ
1595 wxClientDC dc(this);
1596 PrepareDC(dc);
1597 long x = dc.DeviceToLogicalX( (long)point.x );
1598 long y = dc.DeviceToLogicalY( (long)point.y );
1599
bbe0af5b 1600 bool onButton = FALSE;
67a7abf7 1601 return m_anchor->HitTest( wxPoint(x, y), onButton );
4f22cf8d
RR
1602}
1603
3db7be80 1604void wxTreeCtrl::OnMouse( wxMouseEvent &event )
c801d85f 1605{
bbe0af5b 1606 if (!event.LeftIsDown()) m_dragCount = 0;
f135ff73 1607
bbe0af5b 1608 if ( !(event.LeftDown() || event.LeftDClick() || event.Dragging()) ) return;
29d87bba 1609
bbe0af5b 1610 if ( !m_anchor ) return;
978f38c2 1611
bbe0af5b
RR
1612 wxClientDC dc(this);
1613 PrepareDC(dc);
1614 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1615 long y = dc.DeviceToLogicalY( (long)event.GetY() );
29d87bba 1616
bbe0af5b
RR
1617 bool onButton = FALSE;
1618 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
978f38c2 1619
bbe0af5b 1620 if (item == NULL) return; /* we hit the blank area */
29d87bba 1621
bbe0af5b
RR
1622 if (event.Dragging())
1623 {
1624 if (m_dragCount == 2) /* small drag latency (3?) */
1625 {
1626 m_dragCount = 0;
978f38c2 1627
bbe0af5b
RR
1628 wxTreeEvent nevent(wxEVT_COMMAND_TREE_BEGIN_DRAG, GetId());
1629 nevent.m_item = m_current;
1630 nevent.SetEventObject(this);
1631 GetEventHandler()->ProcessEvent(nevent);
1632 }
1633 else
1634 {
1635 m_dragCount++;
1636 }
1637 return;
1638 }
978f38c2 1639
88ac883a
VZ
1640 bool is_multiple=(GetWindowStyleFlag() & wxTR_MULTIPLE);
1641 bool extended_select=(event.ShiftDown() && is_multiple);
1642 bool unselect_others=!(extended_select || (event.ControlDown() && is_multiple));
1643
1644 SelectItem(item, unselect_others, extended_select);
29d87bba 1645
bbe0af5b
RR
1646 if (event.LeftDClick())
1647 {
1648 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1649 event.m_item = item;
1650 event.m_code = 0;
1651 event.SetEventObject( this );
1652 GetEventHandler()->ProcessEvent( event );
1653 }
29d87bba 1654
bbe0af5b
RR
1655 if (onButton)
1656 {
1657 Toggle( item );
1658 }
edaa81ae 1659}
c801d85f 1660
3db7be80
RR
1661void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
1662{
bbe0af5b
RR
1663 /* after all changes have been done to the tree control,
1664 * we actually redraw the tree when everything is over */
ef44a621 1665
f65635b5
VZ
1666 if (!m_dirty)
1667 return;
ef44a621 1668
bbe0af5b 1669 m_dirty = FALSE;
3db7be80 1670
bbe0af5b
RR
1671 CalculatePositions();
1672
1673 AdjustMyScrollbars();
3db7be80
RR
1674}
1675
f135ff73 1676// -----------------------------------------------------------------------------
bbe0af5b
RR
1677
1678void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
c801d85f 1679{
bbe0af5b 1680 int horizX = level*m_indent;
389cdc7a 1681
cf724bce 1682 item->SetX( horizX+m_indent+m_spacing );
f0594f42 1683 item->SetY( y-m_lineHeight/2 );
bbe0af5b 1684 item->SetHeight( m_lineHeight );
4c681997 1685
bbe0af5b
RR
1686 if ( !item->IsExpanded() )
1687 {
f65635b5 1688 // we dont need to calculate collapsed branches
bbe0af5b
RR
1689 return;
1690 }
389cdc7a 1691
bbe0af5b
RR
1692 wxArrayTreeItems& children = item->GetChildren();
1693 size_t count = children.Count();
1694 for ( size_t n = 0; n < count; n++ )
1695 {
1696 y += m_lineHeight;
f65635b5 1697 CalculateLevel( children[n], dc, level+1, y ); // recurse
bbe0af5b 1698 }
edaa81ae 1699}
c801d85f 1700
74bedbeb 1701void wxTreeCtrl::CalculatePositions()
c801d85f 1702{
bbe0af5b 1703 if ( !m_anchor ) return;
29d87bba 1704
bbe0af5b
RR
1705 wxClientDC dc(this);
1706 PrepareDC( dc );
29d87bba 1707
bbe0af5b 1708 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT ) );
29d87bba 1709
bbe0af5b 1710 dc.SetPen( m_dottedPen );
d30b4d20
KB
1711 if(GetImageList() == NULL)
1712 m_lineHeight = (int)(dc.GetCharHeight() + 4);
29d87bba 1713
bbe0af5b 1714 int y = m_lineHeight / 2 + 2;
f65635b5 1715 CalculateLevel( m_anchor, dc, 0, y ); // start recursion
edaa81ae 1716}
c801d85f 1717
f135ff73 1718void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
c801d85f 1719{
bbe0af5b
RR
1720 wxClientDC dc(this);
1721 PrepareDC(dc);
4832f7c0 1722
bbe0af5b
RR
1723 int cw = 0;
1724 int ch = 0;
1725 GetClientSize( &cw, &ch );
4832f7c0 1726
bbe0af5b
RR
1727 wxRect rect;
1728 rect.x = dc.LogicalToDeviceX( 0 );
1729 rect.width = cw;
1730 rect.y = dc.LogicalToDeviceY( item->GetY() );
1731 rect.height = ch;
f135ff73 1732
bbe0af5b 1733 Refresh( TRUE, &rect );
f135ff73 1734
bbe0af5b 1735 AdjustMyScrollbars();
edaa81ae 1736}
c801d85f
KB
1737
1738void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1739{
bbe0af5b
RR
1740 wxClientDC dc(this);
1741 PrepareDC( dc );
1742
1743 wxRect rect;
1744 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1745 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1746 rect.width = 1000;
1747 rect.height = dc.GetCharHeight() + 6;
978f38c2 1748
bbe0af5b 1749 Refresh( TRUE, &rect );
edaa81ae 1750}
c801d85f 1751