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