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