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