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