]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectlg.cpp
71f27e271d417eeca16cff1d37e12100aabfbe78
[wxWidgets.git] / src / generic / treectlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: treectlg.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 "treectlg.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 #if wxUSE_TREECTRL
32
33 #include "wx/treebase.h"
34 #include "wx/generic/treectlg.h"
35 #include "wx/timer.h"
36 #include "wx/textctrl.h"
37 #include "wx/imaglist.h"
38 #include "wx/settings.h"
39 #include "wx/dcclient.h"
40
41 // -----------------------------------------------------------------------------
42 // array types
43 // -----------------------------------------------------------------------------
44
45 class WXDLLEXPORT wxGenericTreeItem;
46
47 WX_DEFINE_EXPORTED_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( wxGenericTreeCtrl *owner );
67
68 void Notify();
69
70 private:
71 wxGenericTreeCtrl *m_owner;
72 };
73
74 // control used for in-place edit
75 class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl
76 {
77 public:
78 wxTreeTextCtrl( wxWindow *parent,
79 const wxWindowID id,
80 bool *accept,
81 wxString *res,
82 wxGenericTreeCtrl *owner,
83 const wxString &value = wxEmptyString,
84 const wxPoint &pos = wxDefaultPosition,
85 const wxSize &size = wxDefaultSize,
86 int style = wxSIMPLE_BORDER,
87 const wxValidator& validator = wxDefaultValidator,
88 const wxString &name = wxTextCtrlNameStr );
89
90 void OnChar( wxKeyEvent &event );
91 void OnKeyUp( wxKeyEvent &event );
92 void OnKillFocus( wxFocusEvent &event );
93
94 private:
95 bool *m_accept;
96 wxString *m_res;
97 wxGenericTreeCtrl *m_owner;
98 wxString m_startValue;
99 bool m_finished;
100
101 DECLARE_EVENT_TABLE()
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 int image,
113 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 wxGenericTreeItem *GetParent() const { return m_parent; }
151
152 // operations
153 // deletes all children notifying the treectrl about it if !NULL
154 // pointer given
155 void DeleteChildren(wxGenericTreeCtrl *tree = NULL);
156
157 // get count of all children (and grand children if 'recursively')
158 size_t GetChildrenCount(bool recursively = TRUE) const;
159
160 void Insert(wxGenericTreeItem *child, size_t index)
161 { m_children.Insert(child, index); }
162
163 void GetSize( int &x, int &y, const wxGenericTreeCtrl* );
164
165 // return the item at given position (or NULL if no item), onButton is
166 // TRUE if the point belongs to the item's button, otherwise it lies
167 // on the button's label
168 wxGenericTreeItem *HitTest( const wxPoint& point,
169 const wxGenericTreeCtrl *,
170 int &flags,
171 int level );
172
173 void Expand() { m_isCollapsed = FALSE; }
174 void Collapse() { m_isCollapsed = TRUE; }
175
176 void SetHilight( bool set = TRUE ) { m_hasHilight = set; }
177
178 // status inquiries
179 bool HasChildren() const { return !m_children.IsEmpty(); }
180 bool IsSelected() const { return m_hasHilight != 0; }
181 bool IsExpanded() const { return !m_isCollapsed; }
182 bool HasPlus() const { return m_hasPlus || HasChildren(); }
183 bool IsBold() const { return m_isBold != 0; }
184
185 // attributes
186 // get them - may be NULL
187 wxTreeItemAttr *GetAttributes() const { return m_attr; }
188 // get them ensuring that the pointer is not NULL
189 wxTreeItemAttr& Attr()
190 {
191 if ( !m_attr )
192 {
193 m_attr = new wxTreeItemAttr;
194 m_ownsAttr = TRUE;
195 }
196 return *m_attr;
197 }
198 // set them
199 void SetAttributes(wxTreeItemAttr *attr)
200 {
201 if ( m_ownsAttr ) delete m_attr;
202 m_attr = attr;
203 m_ownsAttr = FALSE;
204 }
205 // set them and delete when done
206 void AssignAttributes(wxTreeItemAttr *attr)
207 {
208 SetAttributes(attr);
209 m_ownsAttr = TRUE;
210 }
211
212 private:
213 // since there can be very many of these, we save size by chosing
214 // the smallest representation for the elements and by ordering
215 // the members to avoid padding.
216 wxString m_text; // label to be rendered for item
217
218 wxTreeItemData *m_data; // user-provided data
219
220 wxArrayGenericTreeItems m_children; // list of children
221 wxGenericTreeItem *m_parent; // parent of this item
222
223 wxTreeItemAttr *m_attr; // attributes???
224
225 // tree ctrl images for the normal, selected, expanded and
226 // expanded+selected states
227 short m_images[wxTreeItemIcon_Max];
228
229 wxCoord m_x; // (virtual) offset from top
230 short m_y; // (virtual) offset from left
231 short m_width; // width of this item
232 unsigned char m_height; // height of this item
233
234 // use bitfields to save size
235 int m_isCollapsed :1;
236 int m_hasHilight :1; // same as focused
237 int m_hasPlus :1; // used for item which doesn't have
238 // children but has a [+] button
239 int m_isBold :1; // render the label in bold font
240 int m_ownsAttr :1; // delete attribute when done
241 };
242
243 // =============================================================================
244 // implementation
245 // =============================================================================
246
247 // ----------------------------------------------------------------------------
248 // private functions
249 // ----------------------------------------------------------------------------
250
251 // translate the key or mouse event flags to the type of selection we're
252 // dealing with
253 static void EventFlagsToSelType(long style,
254 bool shiftDown,
255 bool ctrlDown,
256 bool &is_multiple,
257 bool &extended_select,
258 bool &unselect_others)
259 {
260 is_multiple = (style & wxTR_MULTIPLE) != 0;
261 extended_select = shiftDown && is_multiple;
262 unselect_others = !(extended_select || (ctrlDown && is_multiple));
263 }
264
265 // -----------------------------------------------------------------------------
266 // wxTreeRenameTimer (internal)
267 // -----------------------------------------------------------------------------
268
269 wxTreeRenameTimer::wxTreeRenameTimer( wxGenericTreeCtrl *owner )
270 {
271 m_owner = owner;
272 }
273
274 void wxTreeRenameTimer::Notify()
275 {
276 m_owner->OnRenameTimer();
277 }
278
279 //-----------------------------------------------------------------------------
280 // wxTreeTextCtrl (internal)
281 //-----------------------------------------------------------------------------
282
283 BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl)
284 EVT_CHAR (wxTreeTextCtrl::OnChar)
285 EVT_KEY_UP (wxTreeTextCtrl::OnKeyUp)
286 EVT_KILL_FOCUS (wxTreeTextCtrl::OnKillFocus)
287 END_EVENT_TABLE()
288
289 wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent,
290 const wxWindowID id,
291 bool *accept,
292 wxString *res,
293 wxGenericTreeCtrl *owner,
294 const wxString &value,
295 const wxPoint &pos,
296 const wxSize &size,
297 int style,
298 const wxValidator& validator,
299 const wxString &name )
300 : wxTextCtrl( parent, id, value, pos, size, style, validator, name )
301 {
302 m_res = res;
303 m_accept = accept;
304 m_owner = owner;
305 (*m_accept) = FALSE;
306 (*m_res) = wxEmptyString;
307 m_startValue = value;
308 m_finished = FALSE;
309 }
310
311 void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
312 {
313 if (event.m_keyCode == WXK_RETURN)
314 {
315 (*m_accept) = TRUE;
316 (*m_res) = GetValue();
317
318 if ((*m_res) != m_startValue)
319 m_owner->OnRenameAccept();
320
321 if (!wxPendingDelete.Member(this))
322 wxPendingDelete.Append(this);
323
324 m_finished = TRUE;
325 m_owner->SetFocus(); // This doesn't work. TODO.
326
327 return;
328 }
329 if (event.m_keyCode == WXK_ESCAPE)
330 {
331 (*m_accept) = FALSE;
332 (*m_res) = "";
333
334 if (!wxPendingDelete.Member(this))
335 wxPendingDelete.Append(this);
336
337 m_finished = TRUE;
338 m_owner->SetFocus(); // This doesn't work. TODO.
339
340 return;
341 }
342 event.Skip();
343 }
344
345 void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event )
346 {
347 if (m_finished)
348 {
349 event.Skip();
350 return;
351 }
352
353 // auto-grow the textctrl:
354 wxSize parentSize = m_owner->GetSize();
355 wxPoint myPos = GetPosition();
356 wxSize mySize = GetSize();
357 int sx, sy;
358 GetTextExtent(GetValue() + _T("M"), &sx, &sy);
359 if (myPos.x + sx > parentSize.x) sx = parentSize.x - myPos.x;
360 if (mySize.x > sx) sx = mySize.x;
361 SetSize(sx, -1);
362
363 event.Skip();
364 }
365
366 void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event )
367 {
368 if (m_finished)
369 {
370 event.Skip();
371 return;
372 }
373
374 if (!wxPendingDelete.Member(this))
375 wxPendingDelete.Append(this);
376
377 (*m_accept) = TRUE;
378 (*m_res) = GetValue();
379
380 if ((*m_res) != m_startValue)
381 m_owner->OnRenameAccept();
382 }
383
384 // -----------------------------------------------------------------------------
385 // wxGenericTreeItem
386 // -----------------------------------------------------------------------------
387
388 wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent,
389 const wxString& text,
390 int image, int selImage,
391 wxTreeItemData *data)
392 : m_text(text)
393 {
394 m_images[wxTreeItemIcon_Normal] = image;
395 m_images[wxTreeItemIcon_Selected] = selImage;
396 m_images[wxTreeItemIcon_Expanded] = NO_IMAGE;
397 m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE;
398
399 m_data = data;
400 m_x = m_y = 0;
401
402 m_isCollapsed = TRUE;
403 m_hasHilight = FALSE;
404 m_hasPlus = FALSE;
405 m_isBold = FALSE;
406
407 m_parent = parent;
408
409 m_attr = (wxTreeItemAttr *)NULL;
410 m_ownsAttr = FALSE;
411
412 // We don't know the height here yet.
413 m_width = 0;
414 m_height = 0;
415 }
416
417 wxGenericTreeItem::~wxGenericTreeItem()
418 {
419 delete m_data;
420
421 if (m_ownsAttr) delete m_attr;
422
423 wxASSERT_MSG( m_children.IsEmpty(),
424 wxT("please call DeleteChildren() before deleting the item") );
425 }
426
427 void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree)
428 {
429 size_t count = m_children.Count();
430 for ( size_t n = 0; n < count; n++ )
431 {
432 wxGenericTreeItem *child = m_children[n];
433 if (tree)
434 tree->SendDeleteEvent(child);
435
436 child->DeleteChildren(tree);
437 delete child;
438 }
439
440 m_children.Empty();
441 }
442
443 void wxGenericTreeItem::SetText( const wxString &text )
444 {
445 m_text = text;
446 }
447
448 size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const
449 {
450 size_t count = m_children.Count();
451 if ( !recursively )
452 return count;
453
454 size_t total = count;
455 for (size_t n = 0; n < count; ++n)
456 {
457 total += m_children[n]->GetChildrenCount();
458 }
459
460 return total;
461 }
462
463 void wxGenericTreeItem::GetSize( int &x, int &y,
464 const wxGenericTreeCtrl *theButton )
465 {
466 int bottomY=m_y+theButton->GetLineHeight(this);
467 if ( y < bottomY ) y = bottomY;
468 int width = m_x + m_width;
469 if ( x < width ) x = width;
470
471 if (IsExpanded())
472 {
473 size_t count = m_children.Count();
474 for ( size_t n = 0; n < count; ++n )
475 {
476 m_children[n]->GetSize( x, y, theButton );
477 }
478 }
479 }
480
481 wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point,
482 const wxGenericTreeCtrl *theCtrl,
483 int &flags,
484 int level)
485 {
486 // for a hidden root node, don't evaluate it, but do evaluate children
487 if ( !(level == 0 && theCtrl->HasFlag(wxTR_HIDE_ROOT)) )
488 {
489 // evaluate the item
490 int h = theCtrl->GetLineHeight(this);
491 if ((point.y > m_y) && (point.y < m_y + h))
492 {
493 int y_mid = m_y + h/2;
494 if (point.y < y_mid )
495 flags |= wxTREE_HITTEST_ONITEMUPPERPART;
496 else
497 flags |= wxTREE_HITTEST_ONITEMLOWERPART;
498
499 // 5 is the size of the plus sign
500 int xCross = m_x - theCtrl->GetSpacing();
501 if ((point.x > xCross-5) && (point.x < xCross+5) &&
502 (point.y > y_mid-5) && (point.y < y_mid+5) &&
503 HasPlus() && theCtrl->HasButtons() )
504 {
505 flags |= wxTREE_HITTEST_ONITEMBUTTON;
506 return this;
507 }
508
509 if ((point.x >= m_x) && (point.x <= m_x+m_width))
510 {
511 int image_w = -1;
512 int image_h;
513
514 // assuming every image (normal and selected) has the same size!
515 if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal )
516 theCtrl->m_imageListNormal->GetSize(GetImage(),
517 image_w, image_h);
518
519 if ((image_w != -1) && (point.x <= m_x + image_w + 1))
520 flags |= wxTREE_HITTEST_ONITEMICON;
521 else
522 flags |= wxTREE_HITTEST_ONITEMLABEL;
523
524 return this;
525 }
526
527 if (point.x < m_x)
528 flags |= wxTREE_HITTEST_ONITEMINDENT;
529 if (point.x > m_x+m_width)
530 flags |= wxTREE_HITTEST_ONITEMRIGHT;
531
532 return this;
533 }
534
535 // if children are expanded, fall through to evaluate them
536 if (m_isCollapsed) return (wxGenericTreeItem*) NULL;
537 }
538
539 // evaluate children
540 size_t count = m_children.Count();
541 for ( size_t n = 0; n < count; n++ )
542 {
543 wxGenericTreeItem *res = m_children[n]->HitTest( point,
544 theCtrl,
545 flags,
546 level + 1 );
547 if ( res != NULL )
548 return res;
549 }
550
551 return (wxGenericTreeItem*) NULL;
552 }
553
554 int wxGenericTreeItem::GetCurrentImage() const
555 {
556 int image = NO_IMAGE;
557 if ( IsExpanded() )
558 {
559 if ( IsSelected() )
560 {
561 image = GetImage(wxTreeItemIcon_SelectedExpanded);
562 }
563
564 if ( image == NO_IMAGE )
565 {
566 // we usually fall back to the normal item, but try just the
567 // expanded one (and not selected) first in this case
568 image = GetImage(wxTreeItemIcon_Expanded);
569 }
570 }
571 else // not expanded
572 {
573 if ( IsSelected() )
574 image = GetImage(wxTreeItemIcon_Selected);
575 }
576
577 // maybe it doesn't have the specific image we want,
578 // try the default one instead
579 if ( image == NO_IMAGE ) image = GetImage();
580
581 return image;
582 }
583
584 // -----------------------------------------------------------------------------
585 // wxGenericTreeCtrl implementation
586 // -----------------------------------------------------------------------------
587
588 IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeCtrl, wxScrolledWindow)
589
590 BEGIN_EVENT_TABLE(wxGenericTreeCtrl,wxScrolledWindow)
591 EVT_PAINT (wxGenericTreeCtrl::OnPaint)
592 EVT_MOUSE_EVENTS (wxGenericTreeCtrl::OnMouse)
593 EVT_CHAR (wxGenericTreeCtrl::OnChar)
594 EVT_SET_FOCUS (wxGenericTreeCtrl::OnSetFocus)
595 EVT_KILL_FOCUS (wxGenericTreeCtrl::OnKillFocus)
596 EVT_IDLE (wxGenericTreeCtrl::OnIdle)
597 END_EVENT_TABLE()
598
599 #if !defined(__WXMSW__) || defined(__WIN16__) || defined(__WXUNIVERSAL__)
600 /*
601 * wxTreeCtrl has to be a real class or we have problems with
602 * the run-time information.
603 */
604
605 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl)
606 #endif
607
608 // -----------------------------------------------------------------------------
609 // construction/destruction
610 // -----------------------------------------------------------------------------
611
612 void wxGenericTreeCtrl::Init()
613 {
614 m_current = m_key_current = m_anchor = (wxGenericTreeItem *) NULL;
615 m_hasFocus = FALSE;
616 m_dirty = FALSE;
617
618 m_lineHeight = 10;
619 m_indent = 15;
620 m_spacing = 18;
621
622 m_hilightBrush = new wxBrush
623 (
624 wxSystemSettings::GetSystemColour
625 (
626 wxSYS_COLOUR_HIGHLIGHT
627 ),
628 wxSOLID
629 );
630
631 m_hilightUnfocusedBrush = new wxBrush
632 (
633 wxSystemSettings::GetSystemColour
634 (
635 wxSYS_COLOUR_BTNSHADOW
636 ),
637 wxSOLID
638 );
639
640 m_imageListNormal = m_imageListButtons =
641 m_imageListState = (wxImageList *) NULL;
642 m_ownsImageListNormal = m_ownsImageListButtons =
643 m_ownsImageListState = FALSE;
644
645 m_dragCount = 0;
646 m_isDragging = FALSE;
647 m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL;
648
649 m_renameTimer = new wxTreeRenameTimer( this );
650 m_lastOnSame = FALSE;
651
652 m_normalFont = wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT );
653 m_boldFont = wxFont( m_normalFont.GetPointSize(),
654 m_normalFont.GetFamily(),
655 m_normalFont.GetStyle(),
656 wxBOLD,
657 m_normalFont.GetUnderlined());
658 }
659
660 bool wxGenericTreeCtrl::Create(wxWindow *parent,
661 wxWindowID id,
662 const wxPoint& pos,
663 const wxSize& size,
664 long style,
665 const wxValidator &validator,
666 const wxString& name )
667 {
668 wxScrolledWindow::Create( parent, id, pos, size,
669 style|wxHSCROLL|wxVSCROLL, name );
670
671 // If the tree display has no buttons, but does have
672 // connecting lines, we can use a narrower layout.
673 // It may not be a good idea to force this...
674 if (!HasButtons() && !HasFlag(wxTR_NO_LINES))
675 {
676 m_indent= 10;
677 m_spacing = 10;
678 }
679
680 #if wxUSE_VALIDATORS
681 SetValidator( validator );
682 #endif
683
684 SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
685
686 // m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86
687 m_dottedPen = wxPen( wxT("grey"), 0, 0 );
688
689 return TRUE;
690 }
691
692 wxGenericTreeCtrl::~wxGenericTreeCtrl()
693 {
694 delete m_hilightBrush;
695 delete m_hilightUnfocusedBrush;
696
697 DeleteAllItems();
698
699 delete m_renameTimer;
700 if (m_ownsImageListNormal) delete m_imageListNormal;
701 if (m_ownsImageListState) delete m_imageListState;
702 if (m_ownsImageListButtons) delete m_imageListButtons;
703 }
704
705 // -----------------------------------------------------------------------------
706 // accessors
707 // -----------------------------------------------------------------------------
708
709 size_t wxGenericTreeCtrl::GetCount() const
710 {
711 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
712 }
713
714 void wxGenericTreeCtrl::SetIndent(unsigned int indent)
715 {
716 m_indent = indent;
717 m_dirty = TRUE;
718 }
719
720 void wxGenericTreeCtrl::SetSpacing(unsigned int spacing)
721 {
722 m_spacing = spacing;
723 m_dirty = TRUE;
724 }
725
726 size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
727 {
728 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
729
730 return ((wxGenericTreeItem*) item.m_pItem)->GetChildrenCount(recursively);
731 }
732
733 void wxGenericTreeCtrl::SetWindowStyle(const long styles)
734 {
735 // right now, just sets the styles. Eventually, we may
736 // want to update the inherited styles, but right now
737 // none of the parents has updatable styles
738 m_windowStyle = styles;
739 m_dirty = TRUE;
740 }
741
742 // -----------------------------------------------------------------------------
743 // functions to work with tree items
744 // -----------------------------------------------------------------------------
745
746 wxString wxGenericTreeCtrl::GetItemText(const wxTreeItemId& item) const
747 {
748 wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") );
749
750 return ((wxGenericTreeItem*) item.m_pItem)->GetText();
751 }
752
753 int wxGenericTreeCtrl::GetItemImage(const wxTreeItemId& item,
754 wxTreeItemIcon which) const
755 {
756 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
757
758 return ((wxGenericTreeItem*) item.m_pItem)->GetImage(which);
759 }
760
761 wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const
762 {
763 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
764
765 return ((wxGenericTreeItem*) item.m_pItem)->GetData();
766 }
767
768 void wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
769 {
770 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
771
772 wxClientDC dc(this);
773 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
774 pItem->SetText(text);
775 CalculateSize(pItem, dc);
776 RefreshLine(pItem);
777 }
778
779 void wxGenericTreeCtrl::SetItemImage(const wxTreeItemId& item,
780 int image,
781 wxTreeItemIcon which)
782 {
783 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
784
785 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
786 pItem->SetImage(image, which);
787
788 wxClientDC dc(this);
789 CalculateSize(pItem, dc);
790 RefreshLine(pItem);
791 }
792
793 void wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
794 {
795 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
796
797 ((wxGenericTreeItem*) item.m_pItem)->SetData(data);
798 }
799
800 void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
801 {
802 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
803
804 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
805 pItem->SetHasPlus(has);
806 RefreshLine(pItem);
807 }
808
809 void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
810 {
811 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
812
813 // avoid redrawing the tree if no real change
814 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
815 if ( pItem->IsBold() != bold )
816 {
817 pItem->SetBold(bold);
818 RefreshLine(pItem);
819 }
820 }
821
822 void wxGenericTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
823 const wxColour& col)
824 {
825 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
826
827 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
828 pItem->Attr().SetTextColour(col);
829 RefreshLine(pItem);
830 }
831
832 void wxGenericTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
833 const wxColour& col)
834 {
835 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
836
837 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
838 pItem->Attr().SetBackgroundColour(col);
839 RefreshLine(pItem);
840 }
841
842 void wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
843 {
844 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
845
846 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
847 pItem->Attr().SetFont(font);
848 RefreshLine(pItem);
849 }
850
851 bool wxGenericTreeCtrl::SetFont( const wxFont &font )
852 {
853 wxScrolledWindow::SetFont(font);
854
855 m_normalFont = font ;
856 m_boldFont = wxFont( m_normalFont.GetPointSize(),
857 m_normalFont.GetFamily(),
858 m_normalFont.GetStyle(),
859 wxBOLD,
860 m_normalFont.GetUnderlined());
861
862 return TRUE;
863 }
864
865
866 // -----------------------------------------------------------------------------
867 // item status inquiries
868 // -----------------------------------------------------------------------------
869
870 bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const
871 {
872 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
873
874 // An item is only visible if it's not a descendant of a collapsed item
875 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
876 wxGenericTreeItem* parent = pItem->GetParent();
877 while (parent)
878 {
879 if (!parent->IsExpanded())
880 return FALSE;
881 parent = parent->GetParent();
882 }
883
884 int startX, startY;
885 GetViewStart(& startX, & startY);
886
887 wxSize clientSize = GetClientSize();
888
889 wxRect rect;
890 if (!GetBoundingRect(item, rect))
891 return FALSE;
892 if (rect.GetWidth() == 0 || rect.GetHeight() == 0)
893 return FALSE;
894 if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y)
895 return FALSE;
896 if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x)
897 return FALSE;
898
899 return TRUE;
900 }
901
902 bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
903 {
904 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
905
906 // consider that the item does have children if it has the "+" button: it
907 // might not have them (if it had never been expanded yet) but then it
908 // could have them as well and it's better to err on this side rather than
909 // disabling some operations which are restricted to the items with
910 // children for an item which does have them
911 return ((wxGenericTreeItem*) item.m_pItem)->HasPlus();
912 }
913
914 bool wxGenericTreeCtrl::IsExpanded(const wxTreeItemId& item) const
915 {
916 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
917
918 return ((wxGenericTreeItem*) item.m_pItem)->IsExpanded();
919 }
920
921 bool wxGenericTreeCtrl::IsSelected(const wxTreeItemId& item) const
922 {
923 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
924
925 return ((wxGenericTreeItem*) item.m_pItem)->IsSelected();
926 }
927
928 bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const
929 {
930 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
931
932 return ((wxGenericTreeItem*) item.m_pItem)->IsBold();
933 }
934
935 // -----------------------------------------------------------------------------
936 // navigation
937 // -----------------------------------------------------------------------------
938
939 wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const
940 {
941 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
942
943 return ((wxGenericTreeItem*) item.m_pItem)->GetParent();
944 }
945
946 wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
947 {
948 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
949
950 cookie = 0;
951 return GetNextChild(item, cookie);
952 }
953
954 wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
955 {
956 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
957
958 wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
959 if ( (size_t)cookie < children.Count() )
960 {
961 return children.Item((size_t)cookie++);
962 }
963 else
964 {
965 // there are no more of them
966 return wxTreeItemId();
967 }
968 }
969
970 wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const
971 {
972 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
973
974 wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
975 return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
976 }
977
978 wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
979 {
980 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
981
982 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
983 wxGenericTreeItem *parent = i->GetParent();
984 if ( parent == NULL )
985 {
986 // root item doesn't have any siblings
987 return wxTreeItemId();
988 }
989
990 wxArrayGenericTreeItems& siblings = parent->GetChildren();
991 int index = siblings.Index(i);
992 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
993
994 size_t n = (size_t)(index + 1);
995 return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
996 }
997
998 wxTreeItemId wxGenericTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
999 {
1000 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1001
1002 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1003 wxGenericTreeItem *parent = i->GetParent();
1004 if ( parent == NULL )
1005 {
1006 // root item doesn't have any siblings
1007 return wxTreeItemId();
1008 }
1009
1010 wxArrayGenericTreeItems& siblings = parent->GetChildren();
1011 int index = siblings.Index(i);
1012 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
1013
1014 return index == 0 ? wxTreeItemId()
1015 : wxTreeItemId(siblings[(size_t)(index - 1)]);
1016 }
1017
1018 // Only for internal use right now, but should probably be public
1019 wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const
1020 {
1021 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1022
1023 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1024
1025 // First see if there are any children.
1026 wxArrayGenericTreeItems& children = i->GetChildren();
1027 if (children.GetCount() > 0)
1028 {
1029 return children.Item(0);
1030 }
1031 else
1032 {
1033 // Try a sibling of this or ancestor instead
1034 wxTreeItemId p = item;
1035 wxTreeItemId toFind;
1036 do
1037 {
1038 toFind = GetNextSibling(p);
1039 p = GetParent(p);
1040 } while (p.IsOk() && !toFind.IsOk());
1041 return toFind;
1042 }
1043 }
1044
1045 wxTreeItemId wxGenericTreeCtrl::GetFirstVisibleItem() const
1046 {
1047 wxTreeItemId id = GetRootItem();
1048 if (!id.IsOk())
1049 return id;
1050
1051 do
1052 {
1053 if (IsVisible(id))
1054 return id;
1055 id = GetNext(id);
1056 } while (id.IsOk());
1057
1058 return wxTreeItemId();
1059 }
1060
1061 wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
1062 {
1063 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1064
1065 wxTreeItemId id = item;
1066 if (id.IsOk())
1067 {
1068 while (id = GetNext(id), id.IsOk())
1069 {
1070 if (IsVisible(id))
1071 return id;
1072 }
1073 }
1074 return wxTreeItemId();
1075 }
1076
1077 wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
1078 {
1079 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1080
1081 wxFAIL_MSG(wxT("not implemented"));
1082
1083 return wxTreeItemId();
1084 }
1085
1086 // -----------------------------------------------------------------------------
1087 // operations
1088 // -----------------------------------------------------------------------------
1089
1090 wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
1091 size_t previous,
1092 const wxString& text,
1093 int image, int selImage,
1094 wxTreeItemData *data)
1095 {
1096 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1097 if ( !parent )
1098 {
1099 // should we give a warning here?
1100 return AddRoot(text, image, selImage, data);
1101 }
1102
1103 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
1104
1105 wxGenericTreeItem *item =
1106 new wxGenericTreeItem( parent, text, image, selImage, data );
1107
1108 if ( data != NULL )
1109 {
1110 data->m_pItem = (long) item;
1111 }
1112
1113 parent->Insert( item, previous );
1114
1115 return item;
1116 }
1117
1118 wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text,
1119 int image, int selImage,
1120 wxTreeItemData *data)
1121 {
1122 wxCHECK_MSG( !m_anchor, wxTreeItemId(), wxT("tree can have only one root") );
1123
1124 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
1125
1126 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text,
1127 image, selImage, data);
1128 if (HasFlag(wxTR_HIDE_ROOT))
1129 {
1130 // if root is hidden, make sure we can navigate
1131 // into children
1132 m_anchor->SetHasPlus();
1133 Expand(m_anchor);
1134 }
1135 if ( data != NULL )
1136 {
1137 data->m_pItem = (long) m_anchor;
1138 }
1139
1140 if (!HasFlag(wxTR_MULTIPLE))
1141 {
1142 m_current = m_key_current = m_anchor;
1143 m_current->SetHilight( TRUE );
1144 }
1145
1146 return m_anchor;
1147 }
1148
1149 wxTreeItemId wxGenericTreeCtrl::PrependItem(const wxTreeItemId& parent,
1150 const wxString& text,
1151 int image, int selImage,
1152 wxTreeItemData *data)
1153 {
1154 return DoInsertItem(parent, 0u, text, image, selImage, data);
1155 }
1156
1157 wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId,
1158 const wxTreeItemId& idPrevious,
1159 const wxString& text,
1160 int image, int selImage,
1161 wxTreeItemData *data)
1162 {
1163 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1164 if ( !parent )
1165 {
1166 // should we give a warning here?
1167 return AddRoot(text, image, selImage, data);
1168 }
1169
1170 int index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem);
1171 wxASSERT_MSG( index != wxNOT_FOUND,
1172 wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") );
1173
1174 return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
1175 }
1176
1177 wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId,
1178 size_t before,
1179 const wxString& text,
1180 int image, int selImage,
1181 wxTreeItemData *data)
1182 {
1183 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1184 if ( !parent )
1185 {
1186 // should we give a warning here?
1187 return AddRoot(text, image, selImage, data);
1188 }
1189
1190 return DoInsertItem(parentId, before, text, image, selImage, data);
1191 }
1192
1193 wxTreeItemId wxGenericTreeCtrl::AppendItem(const wxTreeItemId& parentId,
1194 const wxString& text,
1195 int image, int selImage,
1196 wxTreeItemData *data)
1197 {
1198 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1199 if ( !parent )
1200 {
1201 // should we give a warning here?
1202 return AddRoot(text, image, selImage, data);
1203 }
1204
1205 return DoInsertItem( parent, parent->GetChildren().Count(), text,
1206 image, selImage, data);
1207 }
1208
1209 void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
1210 {
1211 wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
1212 event.m_item = (long) item;
1213 event.SetEventObject( this );
1214 ProcessEvent( event );
1215 }
1216
1217 void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
1218 {
1219 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
1220
1221 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1222 item->DeleteChildren(this);
1223 }
1224
1225 void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId)
1226 {
1227 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
1228
1229 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1230
1231 // don't stay with invalid m_key_current or we will crash in
1232 // the next call to OnChar()
1233 bool changeKeyCurrent = FALSE;
1234 wxGenericTreeItem *itemKey = m_key_current;
1235 while ( itemKey )
1236 {
1237 if ( itemKey == item )
1238 {
1239 // m_key_current is a descendant of the item being deleted
1240 changeKeyCurrent = TRUE;
1241 break;
1242 }
1243 itemKey = itemKey->GetParent();
1244 }
1245
1246 wxGenericTreeItem *parent = item->GetParent();
1247 if ( parent )
1248 {
1249 parent->GetChildren().Remove( item ); // remove by value
1250 }
1251
1252 if ( changeKeyCurrent )
1253 {
1254 // may be NULL or not
1255 m_key_current = parent;
1256 }
1257
1258 item->DeleteChildren(this);
1259 SendDeleteEvent(item);
1260 delete item;
1261 }
1262
1263 void wxGenericTreeCtrl::DeleteAllItems()
1264 {
1265 if ( m_anchor )
1266 {
1267 m_dirty = TRUE;
1268
1269 m_anchor->DeleteChildren(this);
1270 delete m_anchor;
1271
1272 m_anchor = NULL;
1273 }
1274 }
1275
1276 void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
1277 {
1278 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1279
1280 wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") );
1281
1282 if ( !item->HasPlus() )
1283 return;
1284
1285 if ( item->IsExpanded() )
1286 return;
1287
1288 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
1289 event.m_item = (long) item;
1290 event.SetEventObject( this );
1291
1292 if ( ProcessEvent( event ) && !event.IsAllowed() )
1293 {
1294 // cancelled by program
1295 return;
1296 }
1297
1298 item->Expand();
1299 CalculatePositions();
1300
1301 RefreshSubtree(item);
1302
1303 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
1304 ProcessEvent( event );
1305 }
1306
1307 void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item)
1308 {
1309 Expand(item);
1310 if ( IsExpanded(item) )
1311 {
1312 long cookie;
1313 wxTreeItemId child = GetFirstChild(item, cookie);
1314 while ( child.IsOk() )
1315 {
1316 ExpandAll(child);
1317
1318 child = GetNextChild(item, cookie);
1319 }
1320 }
1321 }
1322
1323 void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
1324 {
1325 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1326
1327 if ( !item->IsExpanded() )
1328 return;
1329
1330 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
1331 event.m_item = (long) item;
1332 event.SetEventObject( this );
1333 if ( ProcessEvent( event ) && !event.IsAllowed() )
1334 {
1335 // cancelled by program
1336 return;
1337 }
1338
1339 item->Collapse();
1340
1341 #if 0 // TODO why should items be collapsed recursively?
1342 wxArrayGenericTreeItems& children = item->GetChildren();
1343 size_t count = children.Count();
1344 for ( size_t n = 0; n < count; n++ )
1345 {
1346 Collapse(children[n]);
1347 }
1348 #endif
1349
1350 CalculatePositions();
1351
1352 RefreshSubtree(item);
1353
1354 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
1355 ProcessEvent( event );
1356 }
1357
1358 void wxGenericTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
1359 {
1360 Collapse(item);
1361 DeleteChildren(item);
1362 }
1363
1364 void wxGenericTreeCtrl::Toggle(const wxTreeItemId& itemId)
1365 {
1366 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1367
1368 if (item->IsExpanded())
1369 Collapse(itemId);
1370 else
1371 Expand(itemId);
1372 }
1373
1374 void wxGenericTreeCtrl::Unselect()
1375 {
1376 if (m_current)
1377 {
1378 m_current->SetHilight( FALSE );
1379 RefreshLine( m_current );
1380 }
1381 }
1382
1383 void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
1384 {
1385 if (item->IsSelected())
1386 {
1387 item->SetHilight(FALSE);
1388 RefreshLine(item);
1389 }
1390
1391 if (item->HasChildren())
1392 {
1393 wxArrayGenericTreeItems& children = item->GetChildren();
1394 size_t count = children.Count();
1395 for ( size_t n = 0; n < count; ++n )
1396 {
1397 UnselectAllChildren(children[n]);
1398 }
1399 }
1400 }
1401
1402 void wxGenericTreeCtrl::UnselectAll()
1403 {
1404 UnselectAllChildren((wxGenericTreeItem*) GetRootItem().m_pItem);
1405 }
1406
1407 // Recursive function !
1408 // To stop we must have crt_item<last_item
1409 // Algorithm :
1410 // Tag all next children, when no more children,
1411 // Move to parent (not to tag)
1412 // Keep going... if we found last_item, we stop.
1413 bool wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1414 {
1415 wxGenericTreeItem *parent = crt_item->GetParent();
1416
1417 if (parent == NULL) // This is root item
1418 return TagAllChildrenUntilLast(crt_item, last_item, select);
1419
1420 wxArrayGenericTreeItems& children = parent->GetChildren();
1421 int index = children.Index(crt_item);
1422 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
1423
1424 size_t count = children.Count();
1425 for (size_t n=(size_t)(index+1); n<count; ++n)
1426 {
1427 if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
1428 }
1429
1430 return TagNextChildren(parent, last_item, select);
1431 }
1432
1433 bool wxGenericTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1434 {
1435 crt_item->SetHilight(select);
1436 RefreshLine(crt_item);
1437
1438 if (crt_item==last_item)
1439 return TRUE;
1440
1441 if (crt_item->HasChildren())
1442 {
1443 wxArrayGenericTreeItems& children = crt_item->GetChildren();
1444 size_t count = children.Count();
1445 for ( size_t n = 0; n < count; ++n )
1446 {
1447 if (TagAllChildrenUntilLast(children[n], last_item, select))
1448 return TRUE;
1449 }
1450 }
1451
1452 return FALSE;
1453 }
1454
1455 void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *item2)
1456 {
1457 // item2 is not necessary after item1
1458 wxGenericTreeItem *first=NULL, *last=NULL;
1459
1460 // choice first' and 'last' between item1 and item2
1461 if (item1->GetY()<item2->GetY())
1462 {
1463 first=item1;
1464 last=item2;
1465 }
1466 else
1467 {
1468 first=item2;
1469 last=item1;
1470 }
1471
1472 bool select = m_current->IsSelected();
1473
1474 if ( TagAllChildrenUntilLast(first,last,select) )
1475 return;
1476
1477 TagNextChildren(first,last,select);
1478 }
1479
1480 void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId,
1481 bool unselect_others,
1482 bool extended_select)
1483 {
1484 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
1485
1486 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
1487 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1488
1489 //wxCHECK_RET( ( (!unselect_others) && is_single),
1490 // wxT("this is a single selection tree") );
1491
1492 // to keep going anyhow !!!
1493 if (is_single)
1494 {
1495 if (item->IsSelected())
1496 return; // nothing to do
1497 unselect_others = TRUE;
1498 extended_select = FALSE;
1499 }
1500 else if ( unselect_others && item->IsSelected() )
1501 {
1502 // selection change if there is more than one item currently selected
1503 wxArrayTreeItemIds selected_items;
1504 if ( GetSelections(selected_items) == 1 )
1505 return;
1506 }
1507
1508 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
1509 event.m_item = (long) item;
1510 event.m_itemOld = (long) m_current;
1511 event.SetEventObject( this );
1512 // TODO : Here we don't send any selection mode yet !
1513
1514 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
1515 return;
1516
1517 wxTreeItemId parent = GetParent( itemId );
1518 while (parent.IsOk())
1519 {
1520 if (!IsExpanded(parent))
1521 Expand( parent );
1522
1523 parent = GetParent( parent );
1524 }
1525
1526 EnsureVisible( itemId );
1527
1528 // ctrl press
1529 if (unselect_others)
1530 {
1531 if (is_single) Unselect(); // to speed up thing
1532 else UnselectAll();
1533 }
1534
1535 // shift press
1536 if (extended_select)
1537 {
1538 if ( !m_current )
1539 {
1540 m_current = m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem;
1541 }
1542
1543 // don't change the mark (m_current)
1544 SelectItemRange(m_current, item);
1545 }
1546 else
1547 {
1548 bool select=TRUE; // the default
1549
1550 // Check if we need to toggle hilight (ctrl mode)
1551 if (!unselect_others)
1552 select=!item->IsSelected();
1553
1554 m_current = m_key_current = item;
1555 m_current->SetHilight(select);
1556 RefreshLine( m_current );
1557 }
1558
1559 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
1560 GetEventHandler()->ProcessEvent( event );
1561 }
1562
1563 void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
1564 wxArrayTreeItemIds &array) const
1565 {
1566 if ( item->IsSelected() )
1567 array.Add(wxTreeItemId(item));
1568
1569 if ( item->HasChildren() )
1570 {
1571 wxArrayGenericTreeItems& children = item->GetChildren();
1572 size_t count = children.GetCount();
1573 for ( size_t n = 0; n < count; ++n )
1574 FillArray(children[n], array);
1575 }
1576 }
1577
1578 size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
1579 {
1580 array.Empty();
1581 wxTreeItemId idRoot = GetRootItem();
1582 if ( idRoot.IsOk() )
1583 {
1584 FillArray((wxGenericTreeItem*) idRoot.m_pItem, array);
1585 }
1586 //else: the tree is empty, so no selections
1587
1588 return array.Count();
1589 }
1590
1591 void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item)
1592 {
1593 if (!item.IsOk()) return;
1594
1595 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
1596
1597 // first expand all parent branches
1598 wxGenericTreeItem *parent = gitem->GetParent();
1599 while ( parent )
1600 {
1601 Expand(parent);
1602 parent = parent->GetParent();
1603 }
1604
1605 //if (parent) CalculatePositions();
1606
1607 ScrollTo(item);
1608 }
1609
1610 void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item)
1611 {
1612 if (!item.IsOk()) return;
1613
1614 // We have to call this here because the label in
1615 // question might just have been added and no screen
1616 // update taken place.
1617 if (m_dirty) wxYieldIfNeeded();
1618
1619 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
1620
1621 // now scroll to the item
1622 int item_y = gitem->GetY();
1623
1624 int start_x = 0;
1625 int start_y = 0;
1626 GetViewStart( &start_x, &start_y );
1627 start_y *= PIXELS_PER_UNIT;
1628
1629 int client_h = 0;
1630 int client_w = 0;
1631 GetClientSize( &client_w, &client_h );
1632
1633 if (item_y < start_y+3)
1634 {
1635 // going down
1636 int x = 0;
1637 int y = 0;
1638 m_anchor->GetSize( x, y, this );
1639 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1640 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1641 int x_pos = GetScrollPos( wxHORIZONTAL );
1642 // Item should appear at top
1643 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, item_y/PIXELS_PER_UNIT );
1644 }
1645 else if (item_y+GetLineHeight(gitem) > start_y+client_h)
1646 {
1647 // going up
1648 int x = 0;
1649 int y = 0;
1650 m_anchor->GetSize( x, y, this );
1651 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1652 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1653 item_y += PIXELS_PER_UNIT+2;
1654 int x_pos = GetScrollPos( wxHORIZONTAL );
1655 // Item should appear at bottom
1656 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 );
1657 }
1658 }
1659
1660 // FIXME: tree sorting functions are not reentrant and not MT-safe!
1661 static wxGenericTreeCtrl *s_treeBeingSorted = NULL;
1662
1663 static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
1664 wxGenericTreeItem **item2)
1665 {
1666 wxCHECK_MSG( s_treeBeingSorted, 0, wxT("bug in wxGenericTreeCtrl::SortChildren()") );
1667
1668 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
1669 }
1670
1671 int wxGenericTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
1672 const wxTreeItemId& item2)
1673 {
1674 return wxStrcmp(GetItemText(item1), GetItemText(item2));
1675 }
1676
1677 void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId)
1678 {
1679 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
1680
1681 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1682
1683 wxCHECK_RET( !s_treeBeingSorted,
1684 wxT("wxGenericTreeCtrl::SortChildren is not reentrant") );
1685
1686 wxArrayGenericTreeItems& children = item->GetChildren();
1687 if ( children.Count() > 1 )
1688 {
1689 m_dirty = TRUE;
1690
1691 s_treeBeingSorted = this;
1692 children.Sort(tree_ctrl_compare_func);
1693 s_treeBeingSorted = NULL;
1694 }
1695 //else: don't make the tree dirty as nothing changed
1696 }
1697
1698 wxImageList *wxGenericTreeCtrl::GetImageList() const
1699 {
1700 return m_imageListNormal;
1701 }
1702
1703 wxImageList *wxGenericTreeCtrl::GetButtonsImageList() const
1704 {
1705 return m_imageListButtons;
1706 }
1707
1708 wxImageList *wxGenericTreeCtrl::GetStateImageList() const
1709 {
1710 return m_imageListState;
1711 }
1712
1713 void wxGenericTreeCtrl::CalculateLineHeight()
1714 {
1715 wxClientDC dc(this);
1716 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1717
1718 if ( m_imageListNormal )
1719 {
1720 // Calculate a m_lineHeight value from the normal Image sizes.
1721 // May be toggle off. Then wxGenericTreeCtrl will spread when
1722 // necessary (which might look ugly).
1723 int n = m_imageListNormal->GetImageCount();
1724 for (int i = 0; i < n ; i++)
1725 {
1726 int width = 0, height = 0;
1727 m_imageListNormal->GetSize(i, width, height);
1728 if (height > m_lineHeight) m_lineHeight = height;
1729 }
1730 }
1731
1732 if (m_imageListButtons)
1733 {
1734 // Calculate a m_lineHeight value from the Button image sizes.
1735 // May be toggle off. Then wxGenericTreeCtrl will spread when
1736 // necessary (which might look ugly).
1737 int n = m_imageListButtons->GetImageCount();
1738 for (int i = 0; i < n ; i++)
1739 {
1740 int width = 0, height = 0;
1741 m_imageListButtons->GetSize(i, width, height);
1742 if (height > m_lineHeight) m_lineHeight = height;
1743 }
1744 }
1745
1746 if (m_lineHeight < 30)
1747 m_lineHeight += 2; // at least 2 pixels
1748 else
1749 m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
1750 }
1751
1752 void wxGenericTreeCtrl::SetImageList(wxImageList *imageList)
1753 {
1754 if (m_ownsImageListNormal) delete m_imageListNormal;
1755 m_imageListNormal = imageList;
1756 m_ownsImageListNormal = FALSE;
1757 m_dirty = TRUE;
1758 CalculateLineHeight();
1759 }
1760
1761 void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList)
1762 {
1763 if (m_ownsImageListState) delete m_imageListState;
1764 m_imageListState = imageList;
1765 m_ownsImageListState = FALSE;
1766 }
1767
1768 void wxGenericTreeCtrl::SetButtonsImageList(wxImageList *imageList)
1769 {
1770 if (m_ownsImageListButtons) delete m_imageListButtons;
1771 m_imageListButtons = imageList;
1772 m_ownsImageListButtons = FALSE;
1773 m_dirty = TRUE;
1774 CalculateLineHeight();
1775 }
1776
1777 void wxGenericTreeCtrl::AssignImageList(wxImageList *imageList)
1778 {
1779 SetImageList(imageList);
1780 m_ownsImageListNormal = TRUE;
1781 }
1782
1783 void wxGenericTreeCtrl::AssignStateImageList(wxImageList *imageList)
1784 {
1785 SetStateImageList(imageList);
1786 m_ownsImageListState = TRUE;
1787 }
1788
1789 void wxGenericTreeCtrl::AssignButtonsImageList(wxImageList *imageList)
1790 {
1791 SetButtonsImageList(imageList);
1792 m_ownsImageListButtons = TRUE;
1793 }
1794
1795 // -----------------------------------------------------------------------------
1796 // helpers
1797 // -----------------------------------------------------------------------------
1798
1799 void wxGenericTreeCtrl::AdjustMyScrollbars()
1800 {
1801 if (m_anchor)
1802 {
1803 int x = 0, y = 0;
1804 m_anchor->GetSize( x, y, this );
1805 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1806 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1807 int x_pos = GetScrollPos( wxHORIZONTAL );
1808 int y_pos = GetScrollPos( wxVERTICAL );
1809 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, y_pos );
1810 }
1811 else
1812 {
1813 SetScrollbars( 0, 0, 0, 0 );
1814 }
1815 }
1816
1817 int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
1818 {
1819 if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
1820 return item->GetHeight();
1821 else
1822 return m_lineHeight;
1823 }
1824
1825 void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1826 {
1827 // TODO implement "state" icon on items
1828
1829 wxTreeItemAttr *attr = item->GetAttributes();
1830 if ( attr && attr->HasFont() )
1831 dc.SetFont(attr->GetFont());
1832 else if (item->IsBold())
1833 dc.SetFont(m_boldFont);
1834
1835 long text_w = 0, text_h = 0;
1836 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
1837
1838 int image_h = 0, image_w = 0;
1839 int image = item->GetCurrentImage();
1840 if ( image != NO_IMAGE )
1841 {
1842 if ( m_imageListNormal )
1843 {
1844 m_imageListNormal->GetSize( image, image_w, image_h );
1845 image_w += 4;
1846 }
1847 else
1848 {
1849 image = NO_IMAGE;
1850 }
1851 }
1852
1853 int total_h = GetLineHeight(item);
1854
1855 if ( item->IsSelected() )
1856 {
1857 dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
1858 }
1859 else
1860 {
1861 wxColour colBg;
1862 if ( attr && attr->HasBackgroundColour() )
1863 colBg = attr->GetBackgroundColour();
1864 else
1865 colBg = m_backgroundColour;
1866 dc.SetBrush(wxBrush(colBg, wxSOLID));
1867 }
1868
1869 int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0;
1870
1871 if ( item->IsSelected() && image != NO_IMAGE )
1872 {
1873 // If it's selected, and there's an image, then we should
1874 // take care to leave the area under the image painted in the
1875 // background colour.
1876 dc.DrawRectangle( item->GetX() + image_w - 2, item->GetY()+offset,
1877 item->GetWidth() - image_w + 2, total_h-offset );
1878 }
1879 else
1880 {
1881 dc.DrawRectangle( item->GetX()-2, item->GetY()+offset,
1882 item->GetWidth()+2, total_h-offset );
1883 }
1884
1885 if ( image != NO_IMAGE )
1886 {
1887 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h );
1888 m_imageListNormal->Draw( image, dc,
1889 item->GetX(),
1890 item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0),
1891 wxIMAGELIST_DRAW_TRANSPARENT );
1892 dc.DestroyClippingRegion();
1893 }
1894
1895 dc.SetBackgroundMode(wxTRANSPARENT);
1896 int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0;
1897 dc.DrawText( item->GetText(),
1898 (wxCoord)(image_w + item->GetX()),
1899 (wxCoord)(item->GetY() + extraH));
1900
1901 // restore normal font
1902 dc.SetFont( m_normalFont );
1903 }
1904
1905 // Now y stands for the top of the item, whereas it used to stand for middle !
1906 void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
1907 {
1908 int x = level*m_indent;
1909 if (!HasFlag(wxTR_HIDE_ROOT))
1910 {
1911 x += m_indent;
1912 }
1913 else if (level == 0)
1914 {
1915 // always expand hidden root
1916 int origY = y;
1917 wxArrayGenericTreeItems& children = item->GetChildren();
1918 int count = children.Count();
1919 if (count > 0)
1920 {
1921 int n = 0, oldY;
1922 do {
1923 oldY = y;
1924 PaintLevel(children[n], dc, 1, y);
1925 } while (++n < count);
1926
1927 if (!HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT) && count > 0)
1928 {
1929 // draw line down to last child
1930 origY += GetLineHeight(children[0])>>1;
1931 oldY += GetLineHeight(children[n-1])>>1;
1932 dc.DrawLine(3, origY, 3, oldY);
1933 }
1934 }
1935 return;
1936 }
1937
1938 item->SetX(x+m_spacing);
1939 item->SetY(y);
1940
1941 int h = GetLineHeight(item);
1942 int y_top = y;
1943 int y_mid = y_top + (h>>1);
1944 y += h;
1945
1946 int exposed_x = dc.LogicalToDeviceX(0);
1947 int exposed_y = dc.LogicalToDeviceY(y_top);
1948
1949 if (IsExposed(exposed_x, exposed_y, 10000, h)) // 10000 = very much
1950 {
1951 if (item->HasPlus() && HasButtons()) // should the item show a button?
1952 {
1953 if (!HasFlag(wxTR_NO_LINES))
1954 {
1955 if (x > (signed)m_indent)
1956 dc.DrawLine(x - m_indent, y_mid, x - 5, y_mid);
1957 else if (HasFlag(wxTR_LINES_AT_ROOT))
1958 dc.DrawLine(3, y_mid, x - 5, y_mid);
1959 dc.DrawLine(x + 5, y_mid, x + m_spacing, y_mid);
1960 }
1961
1962 if (m_imageListButtons != NULL)
1963 {
1964 // draw the image button here
1965 int image_h = 0, image_w = 0, image = wxTreeItemIcon_Normal;
1966 if (item->IsExpanded()) image = wxTreeItemIcon_Expanded;
1967 if (item->IsSelected())
1968 image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal;
1969 m_imageListButtons->GetSize(image, image_w, image_h);
1970 int xx = x - (image_w>>1);
1971 int yy = y_mid - (image_h>>1);
1972 dc.SetClippingRegion(xx, yy, image_w, image_h);
1973 m_imageListButtons->Draw(image, dc, xx, yy,
1974 wxIMAGELIST_DRAW_TRANSPARENT);
1975 dc.DestroyClippingRegion();
1976 }
1977 else if (HasFlag(wxTR_TWIST_BUTTONS))
1978 {
1979 // draw the twisty button here
1980 dc.SetPen(*wxBLACK_PEN);
1981 dc.SetBrush(*m_hilightBrush);
1982
1983 wxPoint button[3];
1984
1985 if (item->IsExpanded())
1986 {
1987 button[0].x = x-5;
1988 button[0].y = y_mid-2;
1989 button[1].x = x+5;
1990 button[1].y = y_mid-2;
1991 button[2].x = x;
1992 button[2].y = y_mid+3;
1993 }
1994 else
1995 {
1996 button[0].y = y_mid-5;
1997 button[0].x = x-2;
1998 button[1].y = y_mid+5;
1999 button[1].x = x-2;
2000 button[2].y = y_mid;
2001 button[2].x = x+3;
2002 }
2003 dc.DrawPolygon(3, button);
2004
2005 dc.SetPen(m_dottedPen);
2006 }
2007 else // if (HasFlag(wxTR_HAS_BUTTONS))
2008 {
2009 // draw the plus sign here
2010 dc.SetPen(*wxGREY_PEN);
2011 dc.SetBrush(*wxWHITE_BRUSH);
2012 dc.DrawRectangle(x-5, y_mid-4, 11, 9);
2013 dc.SetPen(*wxBLACK_PEN);
2014 dc.DrawLine(x-2, y_mid, x+3, y_mid);
2015 if (!item->IsExpanded())
2016 dc.DrawLine(x, y_mid-2, x, y_mid+3);
2017 dc.SetPen(m_dottedPen);
2018 }
2019 }
2020 else if (!HasFlag(wxTR_NO_LINES)) // no button; maybe a line?
2021 {
2022 // draw the horizontal line here
2023 int x_start = x;
2024 if (x > (signed)m_indent)
2025 x_start -= m_indent;
2026 else if (HasFlag(wxTR_LINES_AT_ROOT))
2027 x_start = 3;
2028 dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
2029 }
2030
2031 wxPen *pen =
2032 #ifndef __WXMAC__
2033 // don't draw rect outline if we already have the
2034 // background color under Mac
2035 (item->IsSelected() && m_hasFocus) ? wxBLACK_PEN :
2036 #endif // !__WXMAC__
2037 wxTRANSPARENT_PEN;
2038
2039 wxColour colText;
2040 if ( item->IsSelected() )
2041 {
2042 colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
2043 }
2044 else
2045 {
2046 wxTreeItemAttr *attr = item->GetAttributes();
2047 if (attr && attr->HasTextColour())
2048 colText = attr->GetTextColour();
2049 else
2050 colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT);
2051 }
2052
2053 // prepare to draw
2054 dc.SetTextForeground(colText);
2055 dc.SetPen(*pen);
2056
2057 // draw
2058 PaintItem(item, dc);
2059
2060 if (HasFlag(wxTR_ROW_LINES))
2061 {
2062 // if the background colour is white, choose a
2063 // contrasting color for the lines
2064 dc.SetPen(*((GetBackgroundColour() == *wxWHITE)
2065 ? wxMEDIUM_GREY_PEN : wxWHITE_PEN));
2066 dc.DrawLine(0, y_top, 10000, y_top);
2067 dc.DrawLine(0, y, 10000, y);
2068 }
2069
2070 // restore DC objects
2071 dc.SetBrush(*wxWHITE_BRUSH);
2072 dc.SetPen(m_dottedPen);
2073 dc.SetTextForeground(*wxBLACK);
2074 }
2075
2076 if (item->IsExpanded())
2077 {
2078 wxArrayGenericTreeItems& children = item->GetChildren();
2079 int count = children.Count();
2080 if (count > 0)
2081 {
2082 int n = 0, oldY;
2083 ++level;
2084 do {
2085 oldY = y;
2086 PaintLevel(children[n], dc, level, y);
2087 } while (++n < count);
2088
2089 if (!HasFlag(wxTR_NO_LINES) && count > 0)
2090 {
2091 // draw line down to last child
2092 oldY += GetLineHeight(children[n-1])>>1;
2093 if (HasButtons()) y_mid += 5;
2094 dc.DrawLine(x, y_mid, x, oldY);
2095 }
2096 }
2097 }
2098 }
2099
2100 void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item)
2101 {
2102 if ( item )
2103 {
2104 if ( item->HasPlus() )
2105 {
2106 // it's a folder, indicate it by a border
2107 DrawBorder(item);
2108 }
2109 else
2110 {
2111 // draw a line under the drop target because the item will be
2112 // dropped there
2113 DrawLine(item, TRUE /* below */);
2114 }
2115
2116 SetCursor(wxCURSOR_BULLSEYE);
2117 }
2118 else
2119 {
2120 // can't drop here
2121 SetCursor(wxCURSOR_NO_ENTRY);
2122 }
2123 }
2124
2125 void wxGenericTreeCtrl::DrawBorder(const wxTreeItemId &item)
2126 {
2127 wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") );
2128
2129 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
2130
2131 wxClientDC dc(this);
2132 PrepareDC( dc );
2133 dc.SetLogicalFunction(wxINVERT);
2134 dc.SetBrush(*wxTRANSPARENT_BRUSH);
2135
2136 int w = i->GetWidth() + 2;
2137 int h = GetLineHeight(i) + 2;
2138
2139 dc.DrawRectangle( i->GetX() - 1, i->GetY() - 1, w, h);
2140 }
2141
2142 void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below)
2143 {
2144 wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") );
2145
2146 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
2147
2148 wxClientDC dc(this);
2149 PrepareDC( dc );
2150 dc.SetLogicalFunction(wxINVERT);
2151
2152 int x = i->GetX(),
2153 y = i->GetY();
2154 if ( below )
2155 {
2156 y += GetLineHeight(i) - 1;
2157 }
2158
2159 dc.DrawLine( x, y, x + i->GetWidth(), y);
2160 }
2161
2162 // -----------------------------------------------------------------------------
2163 // wxWindows callbacks
2164 // -----------------------------------------------------------------------------
2165
2166 void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
2167 {
2168 wxPaintDC dc(this);
2169 PrepareDC( dc );
2170
2171 if ( !m_anchor)
2172 return;
2173
2174 dc.SetFont( m_normalFont );
2175 dc.SetPen( m_dottedPen );
2176
2177 // this is now done dynamically
2178 //if(GetImageList() == NULL)
2179 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
2180
2181 int y = 2;
2182 PaintLevel( m_anchor, dc, 0, y );
2183 }
2184
2185 void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event )
2186 {
2187 m_hasFocus = TRUE;
2188
2189 RefreshSelected();
2190
2191 event.Skip();
2192 }
2193
2194 void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event )
2195 {
2196 m_hasFocus = FALSE;
2197
2198 RefreshSelected();
2199
2200 event.Skip();
2201 }
2202
2203 void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
2204 {
2205 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
2206 te.m_evtKey = event;
2207 te.SetEventObject( this );
2208 if ( GetEventHandler()->ProcessEvent( te ) )
2209 {
2210 // intercepted by the user code
2211 return;
2212 }
2213
2214 if ( (m_current == 0) || (m_key_current == 0) )
2215 {
2216 event.Skip();
2217 return;
2218 }
2219
2220 // how should the selection work for this event?
2221 bool is_multiple, extended_select, unselect_others;
2222 EventFlagsToSelType(GetWindowStyleFlag(),
2223 event.ShiftDown(),
2224 event.ControlDown(),
2225 is_multiple, extended_select, unselect_others);
2226
2227 // + : Expand
2228 // - : Collaspe
2229 // * : Expand all/Collapse all
2230 // ' ' | return : activate
2231 // up : go up (not last children!)
2232 // down : go down
2233 // left : go to parent
2234 // right : open if parent and go next
2235 // home : go to root
2236 // end : go to last item without opening parents
2237 switch (event.KeyCode())
2238 {
2239 case '+':
2240 case WXK_ADD:
2241 if (m_current->HasPlus() && !IsExpanded(m_current))
2242 {
2243 Expand(m_current);
2244 }
2245 break;
2246
2247 case '*':
2248 case WXK_MULTIPLY:
2249 if ( !IsExpanded(m_current) )
2250 {
2251 // expand all
2252 ExpandAll(m_current);
2253 break;
2254 }
2255 //else: fall through to Collapse() it
2256
2257 case '-':
2258 case WXK_SUBTRACT:
2259 if (IsExpanded(m_current))
2260 {
2261 Collapse(m_current);
2262 }
2263 break;
2264
2265 case ' ':
2266 case WXK_RETURN:
2267 {
2268 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
2269 event.m_item = (long) m_current;
2270 event.SetEventObject( this );
2271 GetEventHandler()->ProcessEvent( event );
2272 }
2273 break;
2274
2275 // up goes to the previous sibling or to the last
2276 // of its children if it's expanded
2277 case WXK_UP:
2278 {
2279 wxTreeItemId prev = GetPrevSibling( m_key_current );
2280 if (!prev)
2281 {
2282 prev = GetParent( m_key_current );
2283 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
2284 {
2285 break; // don't go to root if it is hidden
2286 }
2287 if (prev)
2288 {
2289 long cookie = 0;
2290 wxTreeItemId current = m_key_current;
2291 // TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
2292 if (current == GetFirstChild( prev, cookie ))
2293 {
2294 // otherwise we return to where we came from
2295 SelectItem( prev, unselect_others, extended_select );
2296 m_key_current= (wxGenericTreeItem*) prev.m_pItem;
2297 EnsureVisible( prev );
2298 break;
2299 }
2300 }
2301 }
2302 if (prev)
2303 {
2304 while ( IsExpanded(prev) && HasChildren(prev) )
2305 {
2306 wxTreeItemId child = GetLastChild(prev);
2307 if ( child )
2308 {
2309 prev = child;
2310 }
2311 }
2312
2313 SelectItem( prev, unselect_others, extended_select );
2314 m_key_current=(wxGenericTreeItem*) prev.m_pItem;
2315 EnsureVisible( prev );
2316 }
2317 }
2318 break;
2319
2320 // left arrow goes to the parent
2321 case WXK_LEFT:
2322 {
2323 wxTreeItemId prev = GetParent( m_current );
2324 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
2325 {
2326 // don't go to root if it is hidden
2327 prev = GetPrevSibling( m_current );
2328 }
2329 if (prev)
2330 {
2331 EnsureVisible( prev );
2332 SelectItem( prev, unselect_others, extended_select );
2333 }
2334 }
2335 break;
2336
2337 case WXK_RIGHT:
2338 // this works the same as the down arrow except that we
2339 // also expand the item if it wasn't expanded yet
2340 Expand(m_current);
2341 // fall through
2342
2343 case WXK_DOWN:
2344 {
2345 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
2346 {
2347 long cookie = 0;
2348 wxTreeItemId child = GetFirstChild( m_key_current, cookie );
2349 SelectItem( child, unselect_others, extended_select );
2350 m_key_current=(wxGenericTreeItem*) child.m_pItem;
2351 EnsureVisible( child );
2352 }
2353 else
2354 {
2355 wxTreeItemId next = GetNextSibling( m_key_current );
2356 if (!next)
2357 {
2358 wxTreeItemId current = m_key_current;
2359 while (current && !next)
2360 {
2361 current = GetParent( current );
2362 if (current) next = GetNextSibling( current );
2363 }
2364 }
2365 if (next)
2366 {
2367 SelectItem( next, unselect_others, extended_select );
2368 m_key_current=(wxGenericTreeItem*) next.m_pItem;
2369 EnsureVisible( next );
2370 }
2371 }
2372 }
2373 break;
2374
2375 // <End> selects the last visible tree item
2376 case WXK_END:
2377 {
2378 wxTreeItemId last = GetRootItem();
2379
2380 while ( last.IsOk() && IsExpanded(last) )
2381 {
2382 wxTreeItemId lastChild = GetLastChild(last);
2383
2384 // it may happen if the item was expanded but then all of
2385 // its children have been deleted - so IsExpanded() returned
2386 // TRUE, but GetLastChild() returned invalid item
2387 if ( !lastChild )
2388 break;
2389
2390 last = lastChild;
2391 }
2392
2393 if ( last.IsOk() )
2394 {
2395 EnsureVisible( last );
2396 SelectItem( last, unselect_others, extended_select );
2397 }
2398 }
2399 break;
2400
2401 // <Home> selects the root item
2402 case WXK_HOME:
2403 {
2404 wxTreeItemId prev = GetRootItem();
2405 if (!prev) break;
2406 if (HasFlag(wxTR_HIDE_ROOT))
2407 {
2408 long dummy;
2409 prev = GetFirstChild(prev, dummy);
2410 if (!prev) break;
2411 }
2412 EnsureVisible( prev );
2413 SelectItem( prev, unselect_others, extended_select );
2414 }
2415 break;
2416
2417 default:
2418 event.Skip();
2419 }
2420 }
2421
2422 wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags)
2423 {
2424 // JACS: removed wxYieldIfNeeded() because it can cause the window
2425 // to be deleted from under us if a close window event is pending
2426
2427 int w, h;
2428 GetSize(&w, &h);
2429 flags=0;
2430 if (point.x<0) flags |= wxTREE_HITTEST_TOLEFT;
2431 if (point.x>w) flags |= wxTREE_HITTEST_TORIGHT;
2432 if (point.y<0) flags |= wxTREE_HITTEST_ABOVE;
2433 if (point.y>h) flags |= wxTREE_HITTEST_BELOW;
2434 if (flags) return wxTreeItemId();
2435
2436 if (m_anchor == NULL)
2437 {
2438 flags = wxTREE_HITTEST_NOWHERE;
2439 return wxTreeItemId();
2440 }
2441
2442 wxClientDC dc(this);
2443 PrepareDC(dc);
2444 wxCoord x = dc.DeviceToLogicalX( point.x );
2445 wxCoord y = dc.DeviceToLogicalY( point.y );
2446 wxGenericTreeItem *hit = m_anchor->HitTest(wxPoint(x, y),
2447 this,
2448 flags,
2449 0 );
2450 if (hit == NULL)
2451 {
2452 flags = wxTREE_HITTEST_NOWHERE;
2453 return wxTreeItemId();
2454 }
2455 return hit;
2456 }
2457
2458 // get the bounding rectangle of the item (or of its label only)
2459 bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
2460 wxRect& rect,
2461 bool WXUNUSED(textOnly)) const
2462 {
2463 wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") );
2464
2465 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
2466
2467 int startX, startY;
2468 GetViewStart(& startX, & startY);
2469
2470 rect.x = i->GetX() - startX*PIXELS_PER_UNIT;
2471 rect.y = i->GetY() - startY*PIXELS_PER_UNIT;
2472 rect.width = i->GetWidth();
2473 //rect.height = i->GetHeight();
2474 rect.height = GetLineHeight(i);
2475
2476 return TRUE;
2477 }
2478
2479 /* **** */
2480
2481 void wxGenericTreeCtrl::Edit( const wxTreeItemId& item )
2482 {
2483 if (!item.IsOk()) return;
2484
2485 m_currentEdit = (wxGenericTreeItem*) item.m_pItem;
2486
2487 wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() );
2488 te.m_item = (long) m_currentEdit;
2489 te.SetEventObject( this );
2490 GetEventHandler()->ProcessEvent( te );
2491
2492 if (!te.IsAllowed()) return;
2493
2494 // We have to call this here because the label in
2495 // question might just have been added and no screen
2496 // update taken place.
2497 if (m_dirty) wxYieldIfNeeded();
2498
2499 wxString s = m_currentEdit->GetText();
2500 int x = m_currentEdit->GetX();
2501 int y = m_currentEdit->GetY();
2502 int w = m_currentEdit->GetWidth();
2503 int h = m_currentEdit->GetHeight();
2504
2505 int image_h = 0;
2506 int image_w = 0;
2507
2508 int image = m_currentEdit->GetCurrentImage();
2509 if ( image != NO_IMAGE )
2510 {
2511 if ( m_imageListNormal )
2512 {
2513 m_imageListNormal->GetSize( image, image_w, image_h );
2514 image_w += 4;
2515 }
2516 else
2517 {
2518 wxFAIL_MSG(_T("you must create an image list to use images!"));
2519 }
2520 }
2521 x += image_w;
2522 w -= image_w + 4; // I don't know why +4 is needed
2523
2524 wxClientDC dc(this);
2525 PrepareDC( dc );
2526 x = dc.LogicalToDeviceX( x );
2527 y = dc.LogicalToDeviceY( y );
2528
2529 wxTreeTextCtrl *text = new wxTreeTextCtrl(this, -1,
2530 &m_renameAccept,
2531 &m_renameRes,
2532 this,
2533 s,
2534 wxPoint(x-4,y-4),
2535 wxSize(w+11,h+8));
2536 text->SetFocus();
2537 }
2538
2539 void wxGenericTreeCtrl::OnRenameTimer()
2540 {
2541 Edit( m_current );
2542 }
2543
2544 void wxGenericTreeCtrl::OnRenameAccept()
2545 {
2546 // TODO if the validator fails this causes a crash
2547 wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
2548 le.m_item = (long) m_currentEdit;
2549 le.SetEventObject( this );
2550 le.m_label = m_renameRes;
2551 GetEventHandler()->ProcessEvent( le );
2552
2553 if (!le.IsAllowed()) return;
2554
2555 SetItemText( m_currentEdit, m_renameRes );
2556 }
2557
2558 void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
2559 {
2560 if ( !m_anchor ) return;
2561
2562 // we process left mouse up event (enables in-place edit), right down
2563 // (pass to the user code), left dbl click (activate item) and
2564 // dragging/moving events for items drag-and-drop
2565 if ( !(event.LeftDown() ||
2566 event.LeftUp() ||
2567 event.RightDown() ||
2568 event.LeftDClick() ||
2569 event.Dragging() ||
2570 ((event.Moving() || event.RightUp()) && m_isDragging)) )
2571 {
2572 event.Skip();
2573
2574 return;
2575 }
2576
2577 wxClientDC dc(this);
2578 PrepareDC(dc);
2579 wxCoord x = dc.DeviceToLogicalX( event.GetX() );
2580 wxCoord y = dc.DeviceToLogicalY( event.GetY() );
2581
2582 int flags = 0;
2583 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y),
2584 this,
2585 flags,
2586 0 );
2587
2588 if ( event.Dragging() && !m_isDragging )
2589 {
2590 if (m_dragCount == 0)
2591 m_dragStart = wxPoint(x,y);
2592
2593 m_dragCount++;
2594
2595 if (m_dragCount != 3)
2596 {
2597 // wait until user drags a bit further...
2598 return;
2599 }
2600
2601 wxEventType command = event.RightIsDown()
2602 ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
2603 : wxEVT_COMMAND_TREE_BEGIN_DRAG;
2604
2605 wxTreeEvent nevent( command, GetId() );
2606 nevent.m_item = (long) m_current;
2607 nevent.SetEventObject(this);
2608
2609 // by default the dragging is not supported, the user code must
2610 // explicitly allow the event for it to take place
2611 nevent.Veto();
2612
2613 if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() )
2614 {
2615 // we're going to drag this item
2616 m_isDragging = TRUE;
2617
2618 // remember the old cursor because we will change it while
2619 // dragging
2620 m_oldCursor = m_cursor;
2621
2622 // in a single selection control, hide the selection temporarily
2623 if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
2624 {
2625 m_oldSelection = (wxGenericTreeItem*) GetSelection().m_pItem;
2626
2627 if ( m_oldSelection )
2628 {
2629 m_oldSelection->SetHilight(FALSE);
2630 RefreshLine(m_oldSelection);
2631 }
2632 }
2633
2634 CaptureMouse();
2635 }
2636 }
2637 else if ( event.Moving() )
2638 {
2639 if ( item != m_dropTarget )
2640 {
2641 // unhighlight the previous drop target
2642 DrawDropEffect(m_dropTarget);
2643
2644 m_dropTarget = item;
2645
2646 // highlight the current drop target if any
2647 DrawDropEffect(m_dropTarget);
2648
2649 wxYieldIfNeeded();
2650 }
2651 }
2652 else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
2653 {
2654 // erase the highlighting
2655 DrawDropEffect(m_dropTarget);
2656
2657 if ( m_oldSelection )
2658 {
2659 m_oldSelection->SetHilight(TRUE);
2660 RefreshLine(m_oldSelection);
2661 m_oldSelection = (wxGenericTreeItem *)NULL;
2662 }
2663
2664 // generate the drag end event
2665 wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId());
2666
2667 event.m_item = (long) item;
2668 event.m_pointDrag = wxPoint(x, y);
2669 event.SetEventObject(this);
2670
2671 (void)GetEventHandler()->ProcessEvent(event);
2672
2673 m_isDragging = FALSE;
2674 m_dropTarget = (wxGenericTreeItem *)NULL;
2675
2676 ReleaseMouse();
2677
2678 SetCursor(m_oldCursor);
2679
2680 wxYieldIfNeeded();
2681 }
2682 else
2683 {
2684 // here we process only the messages which happen on tree items
2685
2686 m_dragCount = 0;
2687
2688 if (item == NULL) return; /* we hit the blank area */
2689
2690 if ( event.RightDown() )
2691 {
2692 wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId());
2693 nevent.m_item = (long) item;
2694 CalcScrolledPosition(x, y,
2695 &nevent.m_pointDrag.x,
2696 &nevent.m_pointDrag.y);
2697 nevent.SetEventObject(this);
2698 GetEventHandler()->ProcessEvent(nevent);
2699 }
2700 else if ( event.LeftUp() )
2701 {
2702 if ( m_lastOnSame )
2703 {
2704 if ( (item == m_current) &&
2705 (flags & wxTREE_HITTEST_ONITEMLABEL) &&
2706 HasFlag(wxTR_EDIT_LABELS) )
2707 {
2708 if ( m_renameTimer->IsRunning() )
2709 m_renameTimer->Stop();
2710
2711 m_renameTimer->Start( 100, TRUE );
2712 }
2713
2714 m_lastOnSame = FALSE;
2715 }
2716 }
2717 else // !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
2718 {
2719 if ( event.LeftDown() )
2720 {
2721 m_lastOnSame = item == m_current;
2722 }
2723
2724 if ( flags & wxTREE_HITTEST_ONITEMBUTTON )
2725 {
2726 // only toggle the item for a single click, double click on
2727 // the button doesn't do anything (it toggles the item twice)
2728 if ( event.LeftDown() )
2729 {
2730 Toggle( item );
2731 }
2732
2733 // don't select the item if the button was clicked
2734 return;
2735 }
2736
2737 // how should the selection work for this event?
2738 bool is_multiple, extended_select, unselect_others;
2739 EventFlagsToSelType(GetWindowStyleFlag(),
2740 event.ShiftDown(),
2741 event.ControlDown(),
2742 is_multiple, extended_select, unselect_others);
2743
2744 SelectItem(item, unselect_others, extended_select);
2745
2746 // For some reason, Windows isn't recognizing a left double-click,
2747 // so we need to simulate it here. Allow 200 milliseconds for now.
2748 if ( event.LeftDClick() )
2749 {
2750 // double clicking should not start editing the item label
2751 m_renameTimer->Stop();
2752 m_lastOnSame = FALSE;
2753
2754 // send activate event first
2755 wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
2756 nevent.m_item = (long) item;
2757 CalcScrolledPosition(x, y,
2758 &nevent.m_pointDrag.x,
2759 &nevent.m_pointDrag.y);
2760 nevent.SetEventObject( this );
2761 if ( !GetEventHandler()->ProcessEvent( nevent ) )
2762 {
2763 // if the user code didn't process the activate event,
2764 // handle it ourselves by toggling the item when it is
2765 // double clicked
2766 if ( item->HasPlus() )
2767 {
2768 Toggle(item);
2769 }
2770 }
2771 }
2772 }
2773 }
2774 }
2775
2776 void wxGenericTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
2777 {
2778 /* after all changes have been done to the tree control,
2779 * we actually redraw the tree when everything is over */
2780
2781 if (!m_dirty) return;
2782
2783 m_dirty = FALSE;
2784
2785 CalculatePositions();
2786 Refresh();
2787 AdjustMyScrollbars();
2788 }
2789
2790 void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc )
2791 {
2792 wxCoord text_w = 0;
2793 wxCoord text_h = 0;
2794
2795 if (item->IsBold())
2796 dc.SetFont(m_boldFont);
2797
2798 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
2799 text_h+=2;
2800
2801 // restore normal font
2802 dc.SetFont( m_normalFont );
2803
2804 int image_h = 0;
2805 int image_w = 0;
2806 int image = item->GetCurrentImage();
2807 if ( image != NO_IMAGE )
2808 {
2809 if ( m_imageListNormal )
2810 {
2811 m_imageListNormal->GetSize( image, image_w, image_h );
2812 image_w += 4;
2813 }
2814 }
2815
2816 int total_h = (image_h > text_h) ? image_h : text_h;
2817
2818 if (total_h < 30)
2819 total_h += 2; // at least 2 pixels
2820 else
2821 total_h += total_h/10; // otherwise 10% extra spacing
2822
2823 item->SetHeight(total_h);
2824 if (total_h>m_lineHeight)
2825 m_lineHeight=total_h;
2826
2827 item->SetWidth(image_w+text_w+2);
2828 }
2829
2830 // -----------------------------------------------------------------------------
2831 // for developper : y is now the top of the level
2832 // not the middle of it !
2833 void wxGenericTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
2834 {
2835 int x = level*m_indent;
2836 if (!HasFlag(wxTR_HIDE_ROOT))
2837 {
2838 x += m_indent;
2839 }
2840 else if (level == 0)
2841 {
2842 // a hidden root is not evaluated, but its
2843 // children are always calculated
2844 goto Recurse;
2845 }
2846
2847 CalculateSize( item, dc );
2848
2849 // set its position
2850 item->SetX( x+m_spacing );
2851 item->SetY( y );
2852 y += GetLineHeight(item);
2853
2854 if ( !item->IsExpanded() )
2855 {
2856 // we don't need to calculate collapsed branches
2857 return;
2858 }
2859
2860 Recurse:
2861 wxArrayGenericTreeItems& children = item->GetChildren();
2862 size_t n, count = children.Count();
2863 ++level;
2864 for (n = 0; n < count; ++n )
2865 CalculateLevel( children[n], dc, level, y ); // recurse
2866 }
2867
2868 void wxGenericTreeCtrl::CalculatePositions()
2869 {
2870 if ( !m_anchor ) return;
2871
2872 wxClientDC dc(this);
2873 PrepareDC( dc );
2874
2875 dc.SetFont( m_normalFont );
2876
2877 dc.SetPen( m_dottedPen );
2878 //if(GetImageList() == NULL)
2879 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
2880
2881 int y = 2;
2882 CalculateLevel( m_anchor, dc, 0, y ); // start recursion
2883 }
2884
2885 void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
2886 {
2887 if (m_dirty) return;
2888
2889 wxClientDC dc(this);
2890 PrepareDC(dc);
2891
2892 int cw = 0;
2893 int ch = 0;
2894 GetClientSize( &cw, &ch );
2895
2896 wxRect rect;
2897 rect.x = dc.LogicalToDeviceX( 0 );
2898 rect.width = cw;
2899 rect.y = dc.LogicalToDeviceY( item->GetY() );
2900 rect.height = ch;
2901
2902 Refresh( TRUE, &rect );
2903
2904 AdjustMyScrollbars();
2905 }
2906
2907 void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
2908 {
2909 if (m_dirty) return;
2910
2911 wxClientDC dc(this);
2912 PrepareDC( dc );
2913
2914 int cw = 0;
2915 int ch = 0;
2916 GetClientSize( &cw, &ch );
2917
2918 wxRect rect;
2919 rect.x = dc.LogicalToDeviceX( 0 );
2920 rect.y = dc.LogicalToDeviceY( item->GetY() );
2921 rect.width = cw;
2922 rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
2923
2924 Refresh( TRUE, &rect );
2925 }
2926
2927 void wxGenericTreeCtrl::RefreshSelected()
2928 {
2929 // TODO: this is awfully inefficient, we should keep the list of all
2930 // selected items internally, should be much faster
2931 if ( m_anchor )
2932 RefreshSelectedUnder(m_anchor);
2933 }
2934
2935 void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item)
2936 {
2937 if ( item->IsSelected() )
2938 RefreshLine(item);
2939
2940 const wxArrayGenericTreeItems& children = item->GetChildren();
2941 size_t count = children.GetCount();
2942 for ( size_t n = 0; n < count; n++ )
2943 {
2944 RefreshSelectedUnder(children[n]);
2945 }
2946 }
2947
2948 // ----------------------------------------------------------------------------
2949 // changing colours: we need to refresh the tree control
2950 // ----------------------------------------------------------------------------
2951
2952 bool wxGenericTreeCtrl::SetBackgroundColour(const wxColour& colour)
2953 {
2954 if ( !wxWindow::SetBackgroundColour(colour) )
2955 return FALSE;
2956
2957 Refresh();
2958
2959 return TRUE;
2960 }
2961
2962 bool wxGenericTreeCtrl::SetForegroundColour(const wxColour& colour)
2963 {
2964 if ( !wxWindow::SetForegroundColour(colour) )
2965 return FALSE;
2966
2967 Refresh();
2968
2969 return TRUE;
2970 }
2971
2972 #endif // wxUSE_TREECTRL