Never show tree lines when there are tree buttons under OS X and GTK+
[wxWidgets.git] / src / generic / treectlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/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 and Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // =============================================================================
13 // declarations
14 // =============================================================================
15
16 // -----------------------------------------------------------------------------
17 // headers
18 // -----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_TREECTRL
28
29 #include "wx/treectrl.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/dcclient.h"
33 #include "wx/timer.h"
34 #include "wx/settings.h"
35 #include "wx/listbox.h"
36 #include "wx/textctrl.h"
37 #endif
38
39 #include "wx/generic/treectlg.h"
40 #include "wx/imaglist.h"
41
42 #include "wx/renderer.h"
43
44 #ifdef __WXMAC__
45 #include "wx/osx/private.h"
46 #endif
47
48 // -----------------------------------------------------------------------------
49 // array types
50 // -----------------------------------------------------------------------------
51
52 class WXDLLIMPEXP_FWD_CORE wxGenericTreeItem;
53
54 WX_DEFINE_ARRAY_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems);
55
56 // ----------------------------------------------------------------------------
57 // constants
58 // ----------------------------------------------------------------------------
59
60 static const int NO_IMAGE = -1;
61
62 static const int PIXELS_PER_UNIT = 10;
63
64 // the margin between the item state image and the item normal image
65 static const int MARGIN_BETWEEN_STATE_AND_IMAGE = 2;
66
67 // the margin between the item image and the item text
68 static const int MARGIN_BETWEEN_IMAGE_AND_TEXT = 4;
69
70 // -----------------------------------------------------------------------------
71 // private classes
72 // -----------------------------------------------------------------------------
73
74 // timer used for enabling in-place edit
75 class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
76 {
77 public:
78 // start editing the current item after half a second (if the mouse hasn't
79 // been clicked/moved)
80 enum { DELAY = 500 };
81
82 wxTreeRenameTimer( wxGenericTreeCtrl *owner );
83
84 virtual void Notify();
85
86 private:
87 wxGenericTreeCtrl *m_owner;
88
89 wxDECLARE_NO_COPY_CLASS(wxTreeRenameTimer);
90 };
91
92 // control used for in-place edit
93 class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl
94 {
95 public:
96 wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item);
97
98 void EndEdit( bool discardChanges );
99
100 const wxGenericTreeItem* item() const { return m_itemEdited; }
101
102 protected:
103 void OnChar( wxKeyEvent &event );
104 void OnKeyUp( wxKeyEvent &event );
105 void OnKillFocus( wxFocusEvent &event );
106
107 bool AcceptChanges();
108 void Finish( bool setfocus );
109
110 private:
111 wxGenericTreeCtrl *m_owner;
112 wxGenericTreeItem *m_itemEdited;
113 wxString m_startValue;
114 bool m_aboutToFinish;
115
116 DECLARE_EVENT_TABLE()
117 wxDECLARE_NO_COPY_CLASS(wxTreeTextCtrl);
118 };
119
120 // timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
121 // for a sufficiently long time
122 class WXDLLEXPORT wxTreeFindTimer : public wxTimer
123 {
124 public:
125 // reset the current prefix after half a second of inactivity
126 enum { DELAY = 500 };
127
128 wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; }
129
130 virtual void Notify() { m_owner->m_findPrefix.clear(); }
131
132 private:
133 wxGenericTreeCtrl *m_owner;
134
135 wxDECLARE_NO_COPY_CLASS(wxTreeFindTimer);
136 };
137
138 // a tree item
139 class WXDLLEXPORT wxGenericTreeItem
140 {
141 public:
142 // ctors & dtor
143 wxGenericTreeItem()
144 {
145 m_data = NULL;
146 m_widthText =
147 m_heightText = -1;
148 }
149
150 wxGenericTreeItem( wxGenericTreeItem *parent,
151 const wxString& text,
152 int image,
153 int selImage,
154 wxTreeItemData *data );
155
156 ~wxGenericTreeItem();
157
158 // trivial accessors
159 wxArrayGenericTreeItems& GetChildren() { return m_children; }
160
161 const wxString& GetText() const { return m_text; }
162 int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const
163 { return m_images[which]; }
164 wxTreeItemData *GetData() const { return m_data; }
165 int GetState() const { return m_state; }
166
167 // returns the current image for the item (depending on its
168 // selected/expanded/whatever state)
169 int GetCurrentImage() const;
170
171 void SetText(const wxString& text)
172 {
173 m_text = text;
174
175 ResetTextSize();
176 }
177
178 void SetImage(int image, wxTreeItemIcon which)
179 {
180 m_images[which] = image;
181 m_width = 0;
182 }
183
184 void SetData(wxTreeItemData *data) { m_data = data; }
185 void SetState(int state) { m_state = state; m_width = 0; }
186
187 void SetHasPlus(bool has = true) { m_hasPlus = has; }
188
189 void SetBold(bool bold)
190 {
191 m_isBold = bold;
192
193 ResetTextSize();
194 }
195
196 int GetX() const { return m_x; }
197 int GetY() const { return m_y; }
198
199 void SetX(int x) { m_x = x; }
200 void SetY(int y) { m_y = y; }
201
202 int GetHeight() const { return m_height; }
203 int GetWidth() const { return m_width; }
204
205 int GetTextHeight() const
206 {
207 wxASSERT_MSG( m_heightText != -1, "must call CalculateSize() first" );
208
209 return m_heightText;
210 }
211
212 int GetTextWidth() const
213 {
214 wxASSERT_MSG( m_widthText != -1, "must call CalculateSize() first" );
215
216 return m_widthText;
217 }
218
219 wxGenericTreeItem *GetParent() const { return m_parent; }
220
221 // sets the items font for the specified DC if it uses any special font or
222 // simply returns false otherwise
223 bool SetFont(wxGenericTreeCtrl *control, wxDC& dc) const
224 {
225 wxFont font;
226
227 wxTreeItemAttr * const attr = GetAttributes();
228 if ( attr && attr->HasFont() )
229 font = attr->GetFont();
230 else if ( IsBold() )
231 font = control->m_boldFont;
232 else
233 return false;
234
235 dc.SetFont(font);
236 return true;
237 }
238
239 // operations
240
241 // deletes all children notifying the treectrl about it
242 void DeleteChildren(wxGenericTreeCtrl *tree);
243
244 // get count of all children (and grand children if 'recursively')
245 size_t GetChildrenCount(bool recursively = true) const;
246
247 void Insert(wxGenericTreeItem *child, size_t index)
248 { m_children.Insert(child, index); }
249
250 // calculate and cache the item size using either the provided DC (which is
251 // supposed to have wxGenericTreeCtrl::m_normalFont selected into it!) or a
252 // wxClientDC on the control window
253 void CalculateSize(wxGenericTreeCtrl *control, wxDC& dc)
254 { DoCalculateSize(control, dc, true /* dc uses normal font */); }
255 void CalculateSize(wxGenericTreeCtrl *control);
256
257 void GetSize( int &x, int &y, const wxGenericTreeCtrl* );
258
259 void ResetSize() { m_width = 0; }
260 void ResetTextSize() { m_width = 0; m_widthText = -1; }
261 void RecursiveResetSize();
262 void RecursiveResetTextSize();
263
264 // return the item at given position (or NULL if no item), onButton is
265 // true if the point belongs to the item's button, otherwise it lies
266 // on the item's label
267 wxGenericTreeItem *HitTest( const wxPoint& point,
268 const wxGenericTreeCtrl *,
269 int &flags,
270 int level );
271
272 void Expand() { m_isCollapsed = false; }
273 void Collapse() { m_isCollapsed = true; }
274
275 void SetHilight( bool set = true ) { m_hasHilight = set; }
276
277 // status inquiries
278 bool HasChildren() const { return !m_children.IsEmpty(); }
279 bool IsSelected() const { return m_hasHilight != 0; }
280 bool IsExpanded() const { return !m_isCollapsed; }
281 bool HasPlus() const { return m_hasPlus || HasChildren(); }
282 bool IsBold() const { return m_isBold != 0; }
283
284 // attributes
285 // get them - may be NULL
286 wxTreeItemAttr *GetAttributes() const { return m_attr; }
287 // get them ensuring that the pointer is not NULL
288 wxTreeItemAttr& Attr()
289 {
290 if ( !m_attr )
291 {
292 m_attr = new wxTreeItemAttr;
293 m_ownsAttr = true;
294 }
295 return *m_attr;
296 }
297 // set them
298 void SetAttributes(wxTreeItemAttr *attr)
299 {
300 if ( m_ownsAttr ) delete m_attr;
301 m_attr = attr;
302 m_ownsAttr = false;
303 m_width = 0;
304 m_widthText = -1;
305 }
306 // set them and delete when done
307 void AssignAttributes(wxTreeItemAttr *attr)
308 {
309 SetAttributes(attr);
310 m_ownsAttr = true;
311 m_width = 0;
312 m_widthText = -1;
313 }
314
315 private:
316 // calculate the size of this item, i.e. set m_width, m_height and
317 // m_widthText and m_heightText properly
318 //
319 // if dcUsesNormalFont is true, the current dc font must be the normal tree
320 // control font
321 void DoCalculateSize(wxGenericTreeCtrl *control,
322 wxDC& dc,
323 bool dcUsesNormalFont);
324
325 // since there can be very many of these, we save size by chosing
326 // the smallest representation for the elements and by ordering
327 // the members to avoid padding.
328 wxString m_text; // label to be rendered for item
329 int m_widthText;
330 int m_heightText;
331
332 wxTreeItemData *m_data; // user-provided data
333
334 int m_state; // item state
335
336 wxArrayGenericTreeItems m_children; // list of children
337 wxGenericTreeItem *m_parent; // parent of this item
338
339 wxTreeItemAttr *m_attr; // attributes???
340
341 // tree ctrl images for the normal, selected, expanded and
342 // expanded+selected states
343 int m_images[wxTreeItemIcon_Max];
344
345 wxCoord m_x; // (virtual) offset from top
346 wxCoord m_y; // (virtual) offset from left
347 int m_width; // width of this item
348 int m_height; // height of this item
349
350 // use bitfields to save size
351 unsigned int m_isCollapsed :1;
352 unsigned int m_hasHilight :1; // same as focused
353 unsigned int m_hasPlus :1; // used for item which doesn't have
354 // children but has a [+] button
355 unsigned int m_isBold :1; // render the label in bold font
356 unsigned int m_ownsAttr :1; // delete attribute when done
357
358 wxDECLARE_NO_COPY_CLASS(wxGenericTreeItem);
359 };
360
361 // =============================================================================
362 // implementation
363 // =============================================================================
364
365 // ----------------------------------------------------------------------------
366 // private functions
367 // ----------------------------------------------------------------------------
368
369 // translate the key or mouse event flags to the type of selection we're
370 // dealing with
371 static void EventFlagsToSelType(long style,
372 bool shiftDown,
373 bool ctrlDown,
374 bool &is_multiple,
375 bool &extended_select,
376 bool &unselect_others)
377 {
378 is_multiple = (style & wxTR_MULTIPLE) != 0;
379 extended_select = shiftDown && is_multiple;
380 unselect_others = !(extended_select || (ctrlDown && is_multiple));
381 }
382
383 // check if the given item is under another one
384 static bool
385 IsDescendantOf(const wxGenericTreeItem *parent, const wxGenericTreeItem *item)
386 {
387 while ( item )
388 {
389 if ( item == parent )
390 {
391 // item is a descendant of parent
392 return true;
393 }
394
395 item = item->GetParent();
396 }
397
398 return false;
399 }
400
401 // -----------------------------------------------------------------------------
402 // wxTreeRenameTimer (internal)
403 // -----------------------------------------------------------------------------
404
405 wxTreeRenameTimer::wxTreeRenameTimer( wxGenericTreeCtrl *owner )
406 {
407 m_owner = owner;
408 }
409
410 void wxTreeRenameTimer::Notify()
411 {
412 m_owner->OnRenameTimer();
413 }
414
415 //-----------------------------------------------------------------------------
416 // wxTreeTextCtrl (internal)
417 //-----------------------------------------------------------------------------
418
419 BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl)
420 EVT_CHAR (wxTreeTextCtrl::OnChar)
421 EVT_KEY_UP (wxTreeTextCtrl::OnKeyUp)
422 EVT_KILL_FOCUS (wxTreeTextCtrl::OnKillFocus)
423 END_EVENT_TABLE()
424
425 wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner,
426 wxGenericTreeItem *item)
427 : m_itemEdited(item), m_startValue(item->GetText())
428 {
429 m_owner = owner;
430 m_aboutToFinish = false;
431
432 wxRect rect;
433 m_owner->GetBoundingRect(m_itemEdited, rect, true);
434
435 // corrects position and size for better appearance
436 #ifdef __WXMSW__
437 rect.x -= 5;
438 rect.width += 10;
439 #elif defined(__WXGTK__)
440 rect.x -= 5;
441 rect.y -= 2;
442 rect.width += 8;
443 rect.height += 4;
444 #elif defined(__WXMAC__)
445 int bestHeight = GetBestSize().y - 8;
446 if ( rect.height > bestHeight )
447 {
448 int diff = rect.height - bestHeight;
449 rect.height -= diff;
450 rect.y += diff / 2;
451 }
452 #endif // platforms
453
454 (void)Create(m_owner, wxID_ANY, m_startValue,
455 rect.GetPosition(), rect.GetSize());
456
457 SetSelection(-1, -1);
458 }
459
460 void wxTreeTextCtrl::EndEdit(bool discardChanges)
461 {
462 m_aboutToFinish = true;
463
464 if ( discardChanges )
465 {
466 m_owner->OnRenameCancelled(m_itemEdited);
467
468 Finish( true );
469 }
470 else
471 {
472 // Notify the owner about the changes
473 AcceptChanges();
474
475 // Even if vetoed, close the control (consistent with MSW)
476 Finish( true );
477 }
478 }
479
480 bool wxTreeTextCtrl::AcceptChanges()
481 {
482 const wxString value = GetValue();
483
484 if ( value == m_startValue )
485 {
486 // nothing changed, always accept
487 // when an item remains unchanged, the owner
488 // needs to be notified that the user decided
489 // not to change the tree item label, and that
490 // the edit has been cancelled
491
492 m_owner->OnRenameCancelled(m_itemEdited);
493 return true;
494 }
495
496 if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
497 {
498 // vetoed by the user
499 return false;
500 }
501
502 // accepted, do rename the item
503 m_owner->SetItemText(m_itemEdited, value);
504
505 return true;
506 }
507
508 void wxTreeTextCtrl::Finish( bool setfocus )
509 {
510 m_owner->ResetTextControl();
511
512 wxPendingDelete.Append(this);
513
514 if (setfocus)
515 m_owner->SetFocus();
516 }
517
518 void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
519 {
520 switch ( event.m_keyCode )
521 {
522 case WXK_RETURN:
523 EndEdit( false );
524 break;
525
526 case WXK_ESCAPE:
527 EndEdit( true );
528 break;
529
530 default:
531 event.Skip();
532 }
533 }
534
535 void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event )
536 {
537 if ( !m_aboutToFinish )
538 {
539 // auto-grow the textctrl:
540 wxSize parentSize = m_owner->GetSize();
541 wxPoint myPos = GetPosition();
542 wxSize mySize = GetSize();
543 int sx, sy;
544 GetTextExtent(GetValue() + _T("M"), &sx, &sy);
545 if (myPos.x + sx > parentSize.x)
546 sx = parentSize.x - myPos.x;
547 if (mySize.x > sx)
548 sx = mySize.x;
549 SetSize(sx, wxDefaultCoord);
550 }
551
552 event.Skip();
553 }
554
555 void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event )
556 {
557 if ( !m_aboutToFinish )
558 {
559 if ( !AcceptChanges() )
560 m_owner->OnRenameCancelled( m_itemEdited );
561
562 Finish( false );
563 }
564
565 // We should let the native text control handle focus, too.
566 event.Skip();
567 }
568
569 // -----------------------------------------------------------------------------
570 // wxGenericTreeItem
571 // -----------------------------------------------------------------------------
572
573 wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent,
574 const wxString& text,
575 int image, int selImage,
576 wxTreeItemData *data)
577 : m_text(text)
578 {
579 m_images[wxTreeItemIcon_Normal] = image;
580 m_images[wxTreeItemIcon_Selected] = selImage;
581 m_images[wxTreeItemIcon_Expanded] = NO_IMAGE;
582 m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE;
583
584 m_data = data;
585 m_state = wxTREE_ITEMSTATE_NONE;
586 m_x = m_y = 0;
587
588 m_isCollapsed = true;
589 m_hasHilight = false;
590 m_hasPlus = false;
591 m_isBold = false;
592
593 m_parent = parent;
594
595 m_attr = NULL;
596 m_ownsAttr = false;
597
598 // We don't know the height here yet.
599 m_width = 0;
600 m_height = 0;
601
602 m_widthText = -1;
603 m_heightText = -1;
604 }
605
606 wxGenericTreeItem::~wxGenericTreeItem()
607 {
608 delete m_data;
609
610 if (m_ownsAttr) delete m_attr;
611
612 wxASSERT_MSG( m_children.IsEmpty(),
613 "must call DeleteChildren() before deleting the item" );
614 }
615
616 void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree)
617 {
618 size_t count = m_children.GetCount();
619 for ( size_t n = 0; n < count; n++ )
620 {
621 wxGenericTreeItem *child = m_children[n];
622 tree->SendDeleteEvent(child);
623
624 child->DeleteChildren(tree);
625 if ( child == tree->m_select_me )
626 tree->m_select_me = NULL;
627 delete child;
628 }
629
630 m_children.Empty();
631 }
632
633 size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const
634 {
635 size_t count = m_children.GetCount();
636 if ( !recursively )
637 return count;
638
639 size_t total = count;
640 for (size_t n = 0; n < count; ++n)
641 {
642 total += m_children[n]->GetChildrenCount();
643 }
644
645 return total;
646 }
647
648 void wxGenericTreeItem::GetSize( int &x, int &y,
649 const wxGenericTreeCtrl *theButton )
650 {
651 int bottomY=m_y+theButton->GetLineHeight(this);
652 if ( y < bottomY ) y = bottomY;
653 int width = m_x + m_width;
654 if ( x < width ) x = width;
655
656 if (IsExpanded())
657 {
658 size_t count = m_children.GetCount();
659 for ( size_t n = 0; n < count; ++n )
660 {
661 m_children[n]->GetSize( x, y, theButton );
662 }
663 }
664 }
665
666 wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point,
667 const wxGenericTreeCtrl *theCtrl,
668 int &flags,
669 int level)
670 {
671 // for a hidden root node, don't evaluate it, but do evaluate children
672 if ( !(level == 0 && theCtrl->HasFlag(wxTR_HIDE_ROOT)) )
673 {
674 // evaluate the item
675 int h = theCtrl->GetLineHeight(this);
676 if ((point.y > m_y) && (point.y < m_y + h))
677 {
678 int y_mid = m_y + h/2;
679 if (point.y < y_mid )
680 flags |= wxTREE_HITTEST_ONITEMUPPERPART;
681 else
682 flags |= wxTREE_HITTEST_ONITEMLOWERPART;
683
684 int xCross = m_x - theCtrl->GetSpacing();
685 #ifdef __WXMAC__
686 // according to the drawing code the triangels are drawn
687 // at -4 , -4 from the position up to +10/+10 max
688 if ((point.x > xCross-4) && (point.x < xCross+10) &&
689 (point.y > y_mid-4) && (point.y < y_mid+10) &&
690 HasPlus() && theCtrl->HasButtons() )
691 #else
692 // 5 is the size of the plus sign
693 if ((point.x > xCross-6) && (point.x < xCross+6) &&
694 (point.y > y_mid-6) && (point.y < y_mid+6) &&
695 HasPlus() && theCtrl->HasButtons() )
696 #endif
697 {
698 flags |= wxTREE_HITTEST_ONITEMBUTTON;
699 return this;
700 }
701
702 if ((point.x >= m_x) && (point.x <= m_x+m_width))
703 {
704 int image_w = -1;
705 int image_h;
706
707 // assuming every image (normal and selected) has the same size!
708 if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal )
709 {
710 theCtrl->m_imageListNormal->GetSize(GetImage(),
711 image_w, image_h);
712 }
713
714 int state_w = -1;
715 int state_h;
716
717 if ( (GetState() != wxTREE_ITEMSTATE_NONE) &&
718 theCtrl->m_imageListState )
719 {
720 theCtrl->m_imageListState->GetSize(GetState(),
721 state_w, state_h);
722 }
723
724 if ((state_w != -1) && (point.x <= m_x + state_w + 1))
725 flags |= wxTREE_HITTEST_ONITEMSTATEICON;
726 else if ((image_w != -1) &&
727 (point.x <= m_x +
728 (state_w != -1 ? state_w +
729 MARGIN_BETWEEN_STATE_AND_IMAGE
730 : 0)
731 + image_w + 1))
732 flags |= wxTREE_HITTEST_ONITEMICON;
733 else
734 flags |= wxTREE_HITTEST_ONITEMLABEL;
735
736 return this;
737 }
738
739 if (point.x < m_x)
740 flags |= wxTREE_HITTEST_ONITEMINDENT;
741 if (point.x > m_x+m_width)
742 flags |= wxTREE_HITTEST_ONITEMRIGHT;
743
744 return this;
745 }
746
747 // if children are expanded, fall through to evaluate them
748 if (m_isCollapsed) return NULL;
749 }
750
751 // evaluate children
752 size_t count = m_children.GetCount();
753 for ( size_t n = 0; n < count; n++ )
754 {
755 wxGenericTreeItem *res = m_children[n]->HitTest( point,
756 theCtrl,
757 flags,
758 level + 1 );
759 if ( res != NULL )
760 return res;
761 }
762
763 return NULL;
764 }
765
766 int wxGenericTreeItem::GetCurrentImage() const
767 {
768 int image = NO_IMAGE;
769 if ( IsExpanded() )
770 {
771 if ( IsSelected() )
772 {
773 image = GetImage(wxTreeItemIcon_SelectedExpanded);
774 }
775
776 if ( image == NO_IMAGE )
777 {
778 // we usually fall back to the normal item, but try just the
779 // expanded one (and not selected) first in this case
780 image = GetImage(wxTreeItemIcon_Expanded);
781 }
782 }
783 else // not expanded
784 {
785 if ( IsSelected() )
786 image = GetImage(wxTreeItemIcon_Selected);
787 }
788
789 // maybe it doesn't have the specific image we want,
790 // try the default one instead
791 if ( image == NO_IMAGE ) image = GetImage();
792
793 return image;
794 }
795
796 void wxGenericTreeItem::CalculateSize(wxGenericTreeCtrl* control)
797 {
798 // check if we need to do anything before creating the DC
799 if ( m_width != 0 )
800 return;
801
802 wxClientDC dc(control);
803 DoCalculateSize(control, dc, false /* normal font not used */);
804 }
805
806 void
807 wxGenericTreeItem::DoCalculateSize(wxGenericTreeCtrl* control,
808 wxDC& dc,
809 bool dcUsesNormalFont)
810 {
811 if ( m_width != 0 ) // Size known, nothing to do
812 return;
813
814 if ( m_widthText == -1 )
815 {
816 bool fontChanged;
817 if ( SetFont(control, dc) )
818 {
819 fontChanged = true;
820 }
821 else // we have no special font
822 {
823 if ( !dcUsesNormalFont )
824 {
825 // but we do need to ensure that the normal font is used: notice
826 // that this doesn't count as changing the font as we don't need
827 // to restore it
828 dc.SetFont(control->m_normalFont);
829 }
830
831 fontChanged = false;
832 }
833
834 dc.GetTextExtent( GetText(), &m_widthText, &m_heightText );
835
836 // restore normal font if the DC used it previously and we changed it
837 if ( fontChanged )
838 dc.SetFont(control->m_normalFont);
839 }
840
841 int text_h = m_heightText + 2;
842
843 int image_h = 0, image_w = 0;
844 int image = GetCurrentImage();
845 if ( image != NO_IMAGE && control->m_imageListNormal )
846 {
847 control->m_imageListNormal->GetSize(image, image_w, image_h);
848 image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
849 }
850
851 int state_h = 0, state_w = 0;
852 int state = GetState();
853 if ( state != wxTREE_ITEMSTATE_NONE && control->m_imageListState )
854 {
855 control->m_imageListState->GetSize(state, state_w, state_h);
856 if ( image_w != 0 )
857 state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
858 else
859 state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
860 }
861
862 int img_h = wxMax(state_h, image_h);
863 m_height = wxMax(img_h, text_h);
864
865 if (m_height < 30)
866 m_height += 2; // at least 2 pixels
867 else
868 m_height += m_height / 10; // otherwise 10% extra spacing
869
870 if (m_height > control->m_lineHeight)
871 control->m_lineHeight = m_height;
872
873 m_width = state_w + image_w + m_widthText + 2;
874 }
875
876 void wxGenericTreeItem::RecursiveResetSize()
877 {
878 m_width = 0;
879
880 const size_t count = m_children.Count();
881 for (size_t i = 0; i < count; i++ )
882 m_children[i]->RecursiveResetSize();
883 }
884
885 void wxGenericTreeItem::RecursiveResetTextSize()
886 {
887 m_width = 0;
888 m_widthText = -1;
889
890 const size_t count = m_children.Count();
891 for (size_t i = 0; i < count; i++ )
892 m_children[i]->RecursiveResetTextSize();
893 }
894
895 // -----------------------------------------------------------------------------
896 // wxGenericTreeCtrl implementation
897 // -----------------------------------------------------------------------------
898
899 IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeCtrl, wxControl)
900
901 BEGIN_EVENT_TABLE(wxGenericTreeCtrl, wxTreeCtrlBase)
902 EVT_PAINT (wxGenericTreeCtrl::OnPaint)
903 EVT_SIZE (wxGenericTreeCtrl::OnSize)
904 EVT_MOUSE_EVENTS (wxGenericTreeCtrl::OnMouse)
905 EVT_CHAR (wxGenericTreeCtrl::OnChar)
906 EVT_SET_FOCUS (wxGenericTreeCtrl::OnSetFocus)
907 EVT_KILL_FOCUS (wxGenericTreeCtrl::OnKillFocus)
908 EVT_TREE_ITEM_GETTOOLTIP(wxID_ANY, wxGenericTreeCtrl::OnGetToolTip)
909 END_EVENT_TABLE()
910
911 #if !defined(__WXMSW__) || defined(__WXUNIVERSAL__)
912 /*
913 * wxTreeCtrl has to be a real class or we have problems with
914 * the run-time information.
915 */
916
917 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl)
918 #endif
919
920 // -----------------------------------------------------------------------------
921 // construction/destruction
922 // -----------------------------------------------------------------------------
923
924 void wxGenericTreeCtrl::Init()
925 {
926 m_current =
927 m_key_current =
928 m_anchor =
929 m_select_me = NULL;
930 m_hasFocus = false;
931 m_dirty = false;
932
933 m_lineHeight = 10;
934 m_indent = 15;
935 m_spacing = 18;
936
937 m_hilightBrush = new wxBrush
938 (
939 wxSystemSettings::GetColour
940 (
941 wxSYS_COLOUR_HIGHLIGHT
942 ),
943 wxBRUSHSTYLE_SOLID
944 );
945
946 m_hilightUnfocusedBrush = new wxBrush
947 (
948 wxSystemSettings::GetColour
949 (
950 wxSYS_COLOUR_BTNSHADOW
951 ),
952 wxBRUSHSTYLE_SOLID
953 );
954
955 m_imageListButtons = NULL;
956 m_ownsImageListButtons = false;
957
958 m_dragCount = 0;
959 m_isDragging = false;
960 m_dropTarget = m_oldSelection = NULL;
961 m_underMouse = NULL;
962 m_textCtrl = NULL;
963
964 m_renameTimer = NULL;
965
966 m_findTimer = NULL;
967
968 m_dropEffectAboveItem = false;
969
970 m_dndEffect = NoEffect;
971 m_dndEffectItem = NULL;
972
973 m_lastOnSame = false;
974
975 #if defined( __WXMAC__ )
976 #if wxOSX_USE_ATSU_TEXT
977 m_normalFont.MacCreateFromThemeFont( kThemeViewsFont ) ;
978 #else
979 m_normalFont.MacCreateFromUIFont( kCTFontViewsFontType ) ;
980 #endif
981 #else
982 m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
983 #endif
984 m_boldFont = wxFont(m_normalFont.GetPointSize(),
985 m_normalFont.GetFamily(),
986 m_normalFont.GetStyle(),
987 wxBOLD,
988 m_normalFont.GetUnderlined(),
989 m_normalFont.GetFaceName(),
990 m_normalFont.GetEncoding());
991 }
992
993 bool wxGenericTreeCtrl::Create(wxWindow *parent,
994 wxWindowID id,
995 const wxPoint& pos,
996 const wxSize& size,
997 long style,
998 const wxValidator& validator,
999 const wxString& name )
1000 {
1001 #ifdef __WXMAC__
1002 int major, minor;
1003 wxGetOsVersion(&major, &minor);
1004
1005 if (major < 10)
1006 style |= wxTR_ROW_LINES;
1007
1008 if (style & wxTR_HAS_BUTTONS)
1009 style |= wxTR_NO_LINES;
1010 #endif // __WXMAC__
1011
1012 #ifdef __WXGTK20__
1013 if (style & wxTR_HAS_BUTTONS)
1014 style |= wxTR_NO_LINES;
1015 #endif
1016
1017 if ( !wxControl::Create( parent, id, pos, size,
1018 style|wxHSCROLL|wxVSCROLL,
1019 validator,
1020 name ) )
1021 return false;
1022
1023 // If the tree display has no buttons, but does have
1024 // connecting lines, we can use a narrower layout.
1025 // It may not be a good idea to force this...
1026 if (!HasButtons() && !HasFlag(wxTR_NO_LINES))
1027 {
1028 m_indent= 10;
1029 m_spacing = 10;
1030 }
1031
1032 wxVisualAttributes attr = GetDefaultAttributes();
1033 SetOwnForegroundColour( attr.colFg );
1034 SetOwnBackgroundColour( attr.colBg );
1035 if (!m_hasFont)
1036 SetOwnFont(attr.font);
1037
1038 // this is a misnomer: it's called "dotted pen" but uses (default) wxSOLID
1039 // style because we apparently get performance problems when using dotted
1040 // pen for drawing in some ports -- but under MSW it seems to work fine
1041 #ifdef __WXMSW__
1042 m_dottedPen = wxPen(*wxLIGHT_GREY, 0, wxPENSTYLE_DOT);
1043 #else
1044 m_dottedPen = *wxGREY_PEN;
1045 #endif
1046
1047 SetInitialSize(size);
1048
1049 return true;
1050 }
1051
1052 wxGenericTreeCtrl::~wxGenericTreeCtrl()
1053 {
1054 delete m_hilightBrush;
1055 delete m_hilightUnfocusedBrush;
1056
1057 DeleteAllItems();
1058
1059 delete m_renameTimer;
1060 delete m_findTimer;
1061
1062 if (m_ownsImageListButtons)
1063 delete m_imageListButtons;
1064 }
1065
1066 // -----------------------------------------------------------------------------
1067 // accessors
1068 // -----------------------------------------------------------------------------
1069
1070 unsigned int wxGenericTreeCtrl::GetCount() const
1071 {
1072 if ( !m_anchor )
1073 {
1074 // the tree is empty
1075 return 0;
1076 }
1077
1078 unsigned int count = m_anchor->GetChildrenCount();
1079 if ( !HasFlag(wxTR_HIDE_ROOT) )
1080 {
1081 // take the root itself into account
1082 count++;
1083 }
1084
1085 return count;
1086 }
1087
1088 void wxGenericTreeCtrl::SetIndent(unsigned int indent)
1089 {
1090 m_indent = (unsigned short) indent;
1091 m_dirty = true;
1092 }
1093
1094 size_t
1095 wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item,
1096 bool recursively) const
1097 {
1098 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
1099
1100 return ((wxGenericTreeItem*) item.m_pItem)->GetChildrenCount(recursively);
1101 }
1102
1103 void wxGenericTreeCtrl::SetWindowStyle(const long styles)
1104 {
1105 // Do not try to expand the root node if it hasn't been created yet
1106 if (m_anchor && !HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT))
1107 {
1108 // if we will hide the root, make sure children are visible
1109 m_anchor->SetHasPlus();
1110 m_anchor->Expand();
1111 CalculatePositions();
1112 }
1113
1114 // right now, just sets the styles. Eventually, we may
1115 // want to update the inherited styles, but right now
1116 // none of the parents has updatable styles
1117 m_windowStyle = styles;
1118 m_dirty = true;
1119 }
1120
1121 // -----------------------------------------------------------------------------
1122 // functions to work with tree items
1123 // -----------------------------------------------------------------------------
1124
1125 wxString wxGenericTreeCtrl::GetItemText(const wxTreeItemId& item) const
1126 {
1127 wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") );
1128
1129 return ((wxGenericTreeItem*) item.m_pItem)->GetText();
1130 }
1131
1132 int wxGenericTreeCtrl::GetItemImage(const wxTreeItemId& item,
1133 wxTreeItemIcon which) const
1134 {
1135 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
1136
1137 return ((wxGenericTreeItem*) item.m_pItem)->GetImage(which);
1138 }
1139
1140 wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const
1141 {
1142 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
1143
1144 return ((wxGenericTreeItem*) item.m_pItem)->GetData();
1145 }
1146
1147 int wxGenericTreeCtrl::DoGetItemState(const wxTreeItemId& item) const
1148 {
1149 wxCHECK_MSG( item.IsOk(), wxTREE_ITEMSTATE_NONE, wxT("invalid tree item") );
1150
1151 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1152 return pItem->GetState();
1153 }
1154
1155 wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const
1156 {
1157 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1158
1159 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1160 return pItem->Attr().GetTextColour();
1161 }
1162
1163 wxColour
1164 wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const
1165 {
1166 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1167
1168 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1169 return pItem->Attr().GetBackgroundColour();
1170 }
1171
1172 wxFont wxGenericTreeCtrl::GetItemFont(const wxTreeItemId& item) const
1173 {
1174 wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
1175
1176 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1177 return pItem->Attr().GetFont();
1178 }
1179
1180 void
1181 wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
1182 {
1183 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1184
1185 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1186 pItem->SetText(text);
1187 pItem->CalculateSize(this);
1188 RefreshLine(pItem);
1189 }
1190
1191 void wxGenericTreeCtrl::SetItemImage(const wxTreeItemId& item,
1192 int image,
1193 wxTreeItemIcon which)
1194 {
1195 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1196
1197 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1198 pItem->SetImage(image, which);
1199 pItem->CalculateSize(this);
1200 RefreshLine(pItem);
1201 }
1202
1203 void
1204 wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
1205 {
1206 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1207
1208 if (data)
1209 data->SetId( item );
1210
1211 ((wxGenericTreeItem*) item.m_pItem)->SetData(data);
1212 }
1213
1214 void wxGenericTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state)
1215 {
1216 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1217
1218 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1219 pItem->SetState(state);
1220 pItem->CalculateSize(this);
1221 RefreshLine(pItem);
1222 }
1223
1224 void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
1225 {
1226 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1227
1228 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1229 pItem->SetHasPlus(has);
1230 RefreshLine(pItem);
1231 }
1232
1233 void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
1234 {
1235 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1236
1237 // avoid redrawing the tree if no real change
1238 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1239 if ( pItem->IsBold() != bold )
1240 {
1241 pItem->SetBold(bold);
1242
1243 // recalculate the item size as bold and non bold fonts have different
1244 // widths
1245 pItem->CalculateSize(this);
1246 RefreshLine(pItem);
1247 }
1248 }
1249
1250 void wxGenericTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item,
1251 bool highlight)
1252 {
1253 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1254
1255 wxColour fg, bg;
1256
1257 if (highlight)
1258 {
1259 bg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
1260 fg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
1261 }
1262
1263 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1264 pItem->Attr().SetTextColour(fg);
1265 pItem->Attr().SetBackgroundColour(bg);
1266 RefreshLine(pItem);
1267 }
1268
1269 void wxGenericTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
1270 const wxColour& col)
1271 {
1272 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1273
1274 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1275 pItem->Attr().SetTextColour(col);
1276 RefreshLine(pItem);
1277 }
1278
1279 void wxGenericTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
1280 const wxColour& col)
1281 {
1282 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1283
1284 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1285 pItem->Attr().SetBackgroundColour(col);
1286 RefreshLine(pItem);
1287 }
1288
1289 void
1290 wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
1291 {
1292 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1293
1294 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1295 pItem->Attr().SetFont(font);
1296 pItem->ResetTextSize();
1297 pItem->CalculateSize(this);
1298 RefreshLine(pItem);
1299 }
1300
1301 bool wxGenericTreeCtrl::SetFont( const wxFont &font )
1302 {
1303 wxTreeCtrlBase::SetFont(font);
1304
1305 m_normalFont = font ;
1306 m_boldFont = wxFont(m_normalFont.GetPointSize(),
1307 m_normalFont.GetFamily(),
1308 m_normalFont.GetStyle(),
1309 wxBOLD,
1310 m_normalFont.GetUnderlined(),
1311 m_normalFont.GetFaceName(),
1312 m_normalFont.GetEncoding());
1313
1314 if (m_anchor)
1315 m_anchor->RecursiveResetTextSize();
1316
1317 return true;
1318 }
1319
1320
1321 // -----------------------------------------------------------------------------
1322 // item status inquiries
1323 // -----------------------------------------------------------------------------
1324
1325 bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const
1326 {
1327 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1328
1329 // An item is only visible if it's not a descendant of a collapsed item
1330 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
1331 wxGenericTreeItem* parent = pItem->GetParent();
1332 while (parent)
1333 {
1334 if (!parent->IsExpanded())
1335 return false;
1336 parent = parent->GetParent();
1337 }
1338
1339 int startX, startY;
1340 GetViewStart(& startX, & startY);
1341
1342 wxSize clientSize = GetClientSize();
1343
1344 wxRect rect;
1345 if (!GetBoundingRect(item, rect))
1346 return false;
1347 if (rect.GetWidth() == 0 || rect.GetHeight() == 0)
1348 return false;
1349 if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y)
1350 return false;
1351 if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x)
1352 return false;
1353
1354 return true;
1355 }
1356
1357 bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
1358 {
1359 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1360
1361 // consider that the item does have children if it has the "+" button: it
1362 // might not have them (if it had never been expanded yet) but then it
1363 // could have them as well and it's better to err on this side rather than
1364 // disabling some operations which are restricted to the items with
1365 // children for an item which does have them
1366 return ((wxGenericTreeItem*) item.m_pItem)->HasPlus();
1367 }
1368
1369 bool wxGenericTreeCtrl::IsExpanded(const wxTreeItemId& item) const
1370 {
1371 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1372
1373 return ((wxGenericTreeItem*) item.m_pItem)->IsExpanded();
1374 }
1375
1376 bool wxGenericTreeCtrl::IsSelected(const wxTreeItemId& item) const
1377 {
1378 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1379
1380 return ((wxGenericTreeItem*) item.m_pItem)->IsSelected();
1381 }
1382
1383 bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const
1384 {
1385 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1386
1387 return ((wxGenericTreeItem*) item.m_pItem)->IsBold();
1388 }
1389
1390 // -----------------------------------------------------------------------------
1391 // navigation
1392 // -----------------------------------------------------------------------------
1393
1394 wxTreeItemId wxGenericTreeCtrl::GetItemParent(const wxTreeItemId& item) const
1395 {
1396 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1397
1398 return ((wxGenericTreeItem*) item.m_pItem)->GetParent();
1399 }
1400
1401 wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item,
1402 wxTreeItemIdValue& cookie) const
1403 {
1404 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1405
1406 cookie = 0;
1407 return GetNextChild(item, cookie);
1408 }
1409
1410 wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item,
1411 wxTreeItemIdValue& cookie) const
1412 {
1413 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1414
1415 wxArrayGenericTreeItems&
1416 children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
1417
1418 // it's ok to cast cookie to size_t, we never have indices big enough to
1419 // overflow "void *"
1420 size_t *pIndex = (size_t *)&cookie;
1421 if ( *pIndex < children.GetCount() )
1422 {
1423 return children.Item((*pIndex)++);
1424 }
1425 else
1426 {
1427 // there are no more of them
1428 return wxTreeItemId();
1429 }
1430 }
1431
1432 wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const
1433 {
1434 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1435
1436 wxArrayGenericTreeItems&
1437 children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
1438 return children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last());
1439 }
1440
1441 wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
1442 {
1443 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1444
1445 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1446 wxGenericTreeItem *parent = i->GetParent();
1447 if ( parent == NULL )
1448 {
1449 // root item doesn't have any siblings
1450 return wxTreeItemId();
1451 }
1452
1453 wxArrayGenericTreeItems& siblings = parent->GetChildren();
1454 int index = siblings.Index(i);
1455 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
1456
1457 size_t n = (size_t)(index + 1);
1458 return n == siblings.GetCount() ? wxTreeItemId()
1459 : wxTreeItemId(siblings[n]);
1460 }
1461
1462 wxTreeItemId wxGenericTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
1463 {
1464 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1465
1466 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1467 wxGenericTreeItem *parent = i->GetParent();
1468 if ( parent == NULL )
1469 {
1470 // root item doesn't have any siblings
1471 return wxTreeItemId();
1472 }
1473
1474 wxArrayGenericTreeItems& siblings = parent->GetChildren();
1475 int index = siblings.Index(i);
1476 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
1477
1478 return index == 0 ? wxTreeItemId()
1479 : wxTreeItemId(siblings[(size_t)(index - 1)]);
1480 }
1481
1482 // Only for internal use right now, but should probably be public
1483 wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const
1484 {
1485 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1486
1487 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1488
1489 // First see if there are any children.
1490 wxArrayGenericTreeItems& children = i->GetChildren();
1491 if (children.GetCount() > 0)
1492 {
1493 return children.Item(0);
1494 }
1495 else
1496 {
1497 // Try a sibling of this or ancestor instead
1498 wxTreeItemId p = item;
1499 wxTreeItemId toFind;
1500 do
1501 {
1502 toFind = GetNextSibling(p);
1503 p = GetItemParent(p);
1504 } while (p.IsOk() && !toFind.IsOk());
1505 return toFind;
1506 }
1507 }
1508
1509 wxTreeItemId wxGenericTreeCtrl::GetFirstVisibleItem() const
1510 {
1511 wxTreeItemId id = GetRootItem();
1512 if (!id.IsOk())
1513 return id;
1514
1515 do
1516 {
1517 if (IsVisible(id))
1518 return id;
1519 id = GetNext(id);
1520 } while (id.IsOk());
1521
1522 return wxTreeItemId();
1523 }
1524
1525 wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
1526 {
1527 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1528 wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") );
1529
1530 wxTreeItemId id = item;
1531 if (id.IsOk())
1532 {
1533 while (id = GetNext(id), id.IsOk())
1534 {
1535 if (IsVisible(id))
1536 return id;
1537 }
1538 }
1539 return wxTreeItemId();
1540 }
1541
1542 wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
1543 {
1544 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1545 wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") );
1546
1547 // find out the starting point
1548 wxTreeItemId prevItem = GetPrevSibling(item);
1549 if ( !prevItem.IsOk() )
1550 {
1551 prevItem = GetItemParent(item);
1552 }
1553
1554 // find the first visible item after it
1555 while ( prevItem.IsOk() && !IsVisible(prevItem) )
1556 {
1557 prevItem = GetNext(prevItem);
1558 if ( !prevItem.IsOk() || prevItem == item )
1559 {
1560 // there are no visible items before item
1561 return wxTreeItemId();
1562 }
1563 }
1564
1565 // from there we must be able to navigate until this item
1566 while ( prevItem.IsOk() )
1567 {
1568 const wxTreeItemId nextItem = GetNextVisible(prevItem);
1569 if ( !nextItem.IsOk() || nextItem == item )
1570 break;
1571
1572 prevItem = nextItem;
1573 }
1574
1575 return prevItem;
1576 }
1577
1578 // called by wxTextTreeCtrl when it marks itself for deletion
1579 void wxGenericTreeCtrl::ResetTextControl()
1580 {
1581 m_textCtrl = NULL;
1582 }
1583
1584 // find the first item starting with the given prefix after the given item
1585 wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent,
1586 const wxString& prefixOrig) const
1587 {
1588 // match is case insensitive as this is more convenient to the user: having
1589 // to press Shift-letter to go to the item starting with a capital letter
1590 // would be too bothersome
1591 wxString prefix = prefixOrig.Lower();
1592
1593 // determine the starting point: we shouldn't take the current item (this
1594 // allows to switch between two items starting with the same letter just by
1595 // pressing it) but we shouldn't jump to the next one if the user is
1596 // continuing to type as otherwise he might easily skip the item he wanted
1597 wxTreeItemId id = idParent;
1598 if ( prefix.length() == 1 )
1599 {
1600 id = GetNext(id);
1601 }
1602
1603 // look for the item starting with the given prefix after it
1604 while ( id.IsOk() && !GetItemText(id).Lower().StartsWith(prefix) )
1605 {
1606 id = GetNext(id);
1607 }
1608
1609 // if we haven't found anything...
1610 if ( !id.IsOk() )
1611 {
1612 // ... wrap to the beginning
1613 id = GetRootItem();
1614 if ( HasFlag(wxTR_HIDE_ROOT) )
1615 {
1616 // can't select virtual root
1617 id = GetNext(id);
1618 }
1619
1620 // and try all the items (stop when we get to the one we started from)
1621 while ( id.IsOk() && id != idParent &&
1622 !GetItemText(id).Lower().StartsWith(prefix) )
1623 {
1624 id = GetNext(id);
1625 }
1626 // If we haven't found the item, id.IsOk() will be false, as per
1627 // documentation
1628 }
1629
1630 return id;
1631 }
1632
1633 // -----------------------------------------------------------------------------
1634 // operations
1635 // -----------------------------------------------------------------------------
1636
1637 wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
1638 size_t previous,
1639 const wxString& text,
1640 int image,
1641 int selImage,
1642 wxTreeItemData *data)
1643 {
1644 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1645 if ( !parent )
1646 {
1647 // should we give a warning here?
1648 return AddRoot(text, image, selImage, data);
1649 }
1650
1651 m_dirty = true; // do this first so stuff below doesn't cause flicker
1652
1653 wxGenericTreeItem *item =
1654 new wxGenericTreeItem( parent, text, image, selImage, data );
1655
1656 if ( data != NULL )
1657 {
1658 data->m_pItem = item;
1659 }
1660
1661 parent->Insert( item, previous == (size_t)-1 ? parent->GetChildren().size()
1662 : previous );
1663
1664 InvalidateBestSize();
1665 return item;
1666 }
1667
1668 wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text,
1669 int image,
1670 int selImage,
1671 wxTreeItemData *data)
1672 {
1673 wxCHECK_MSG( !m_anchor, wxTreeItemId(), "tree can have only one root" );
1674
1675 m_dirty = true; // do this first so stuff below doesn't cause flicker
1676
1677 m_anchor = new wxGenericTreeItem(NULL, text,
1678 image, selImage, data);
1679 if ( data != NULL )
1680 {
1681 data->m_pItem = m_anchor;
1682 }
1683
1684 if (HasFlag(wxTR_HIDE_ROOT))
1685 {
1686 // if root is hidden, make sure we can navigate
1687 // into children
1688 m_anchor->SetHasPlus();
1689 m_anchor->Expand();
1690 CalculatePositions();
1691 }
1692
1693 if (!HasFlag(wxTR_MULTIPLE))
1694 {
1695 m_current = m_key_current = m_anchor;
1696 m_current->SetHilight( true );
1697 }
1698
1699 InvalidateBestSize();
1700 return m_anchor;
1701 }
1702
1703 wxTreeItemId wxGenericTreeCtrl::DoInsertAfter(const wxTreeItemId& parentId,
1704 const wxTreeItemId& idPrevious,
1705 const wxString& text,
1706 int image, int selImage,
1707 wxTreeItemData *data)
1708 {
1709 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1710 if ( !parent )
1711 {
1712 // should we give a warning here?
1713 return AddRoot(text, image, selImage, data);
1714 }
1715
1716 int index = -1;
1717 if (idPrevious.IsOk())
1718 {
1719 index = parent->GetChildren().Index(
1720 (wxGenericTreeItem*) idPrevious.m_pItem);
1721 wxASSERT_MSG( index != wxNOT_FOUND,
1722 "previous item in wxGenericTreeCtrl::InsertItem() "
1723 "is not a sibling" );
1724 }
1725
1726 return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
1727 }
1728
1729
1730 void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
1731 {
1732 wxTreeEvent event(wxEVT_COMMAND_TREE_DELETE_ITEM, this, item);
1733 GetEventHandler()->ProcessEvent( event );
1734 }
1735
1736 // Don't leave edit or selection on a child which is about to disappear
1737 void wxGenericTreeCtrl::ChildrenClosing(wxGenericTreeItem* item)
1738 {
1739 if ( m_textCtrl && item != m_textCtrl->item() &&
1740 IsDescendantOf(item, m_textCtrl->item()) )
1741 {
1742 m_textCtrl->EndEdit( true );
1743 }
1744
1745 if ( item != m_key_current && IsDescendantOf(item, m_key_current) )
1746 {
1747 m_key_current = NULL;
1748 }
1749
1750 if ( IsDescendantOf(item, m_select_me) )
1751 {
1752 m_select_me = item;
1753 }
1754
1755 if ( item != m_current && IsDescendantOf(item, m_current) )
1756 {
1757 m_current->SetHilight( false );
1758 m_current = NULL;
1759 m_select_me = item;
1760 }
1761 }
1762
1763 void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
1764 {
1765 m_dirty = true; // do this first so stuff below doesn't cause flicker
1766
1767 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1768 ChildrenClosing(item);
1769 item->DeleteChildren(this);
1770 InvalidateBestSize();
1771 }
1772
1773 void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId)
1774 {
1775 m_dirty = true; // do this first so stuff below doesn't cause flicker
1776
1777 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1778
1779 if (m_textCtrl != NULL && IsDescendantOf(item, m_textCtrl->item()))
1780 {
1781 // can't delete the item being edited, cancel editing it first
1782 m_textCtrl->EndEdit( true );
1783 }
1784
1785 wxGenericTreeItem *parent = item->GetParent();
1786
1787 // if the selected item will be deleted, select the parent ...
1788 wxGenericTreeItem *to_be_selected = parent;
1789 if (parent)
1790 {
1791 // .. unless there is a next sibling like wxMSW does it
1792 int pos = parent->GetChildren().Index( item );
1793 if ((int)(parent->GetChildren().GetCount()) > pos+1)
1794 to_be_selected = parent->GetChildren().Item( pos+1 );
1795 }
1796
1797 // don't keep stale pointers around!
1798 if ( IsDescendantOf(item, m_key_current) )
1799 {
1800 // Don't silently change the selection:
1801 // do it properly in idle time, so event
1802 // handlers get called.
1803
1804 // m_key_current = parent;
1805 m_key_current = NULL;
1806 }
1807
1808 // m_select_me records whether we need to select
1809 // a different item, in idle time.
1810 if ( m_select_me && IsDescendantOf(item, m_select_me) )
1811 {
1812 m_select_me = to_be_selected;
1813 }
1814
1815 if ( IsDescendantOf(item, m_current) )
1816 {
1817 // Don't silently change the selection:
1818 // do it properly in idle time, so event
1819 // handlers get called.
1820
1821 // m_current = parent;
1822 m_current = NULL;
1823 m_select_me = to_be_selected;
1824 }
1825
1826 // remove the item from the tree
1827 if ( parent )
1828 {
1829 parent->GetChildren().Remove( item ); // remove by value
1830 }
1831 else // deleting the root
1832 {
1833 // nothing will be left in the tree
1834 m_anchor = NULL;
1835 }
1836
1837 // and delete all of its children and the item itself now
1838 item->DeleteChildren(this);
1839 SendDeleteEvent(item);
1840
1841 if (item == m_select_me)
1842 m_select_me = NULL;
1843
1844 delete item;
1845
1846 InvalidateBestSize();
1847 }
1848
1849 void wxGenericTreeCtrl::DeleteAllItems()
1850 {
1851 if ( m_anchor )
1852 {
1853 Delete(m_anchor);
1854 }
1855 }
1856
1857 void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
1858 {
1859 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1860
1861 wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") );
1862 wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
1863 _T("can't expand hidden root") );
1864
1865 if ( !item->HasPlus() )
1866 return;
1867
1868 if ( item->IsExpanded() )
1869 return;
1870
1871 wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_EXPANDING, this, item);
1872
1873 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
1874 {
1875 // cancelled by program
1876 return;
1877 }
1878
1879 item->Expand();
1880 if ( !IsFrozen() )
1881 {
1882 CalculatePositions();
1883
1884 RefreshSubtree(item);
1885 }
1886 else // frozen
1887 {
1888 m_dirty = true;
1889 }
1890
1891 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
1892 GetEventHandler()->ProcessEvent( event );
1893 }
1894
1895 void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
1896 {
1897 wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
1898 _T("can't collapse hidden root") );
1899
1900 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1901
1902 if ( !item->IsExpanded() )
1903 return;
1904
1905 wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_COLLAPSING, this, item);
1906 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
1907 {
1908 // cancelled by program
1909 return;
1910 }
1911
1912 ChildrenClosing(item);
1913 item->Collapse();
1914
1915 #if 0 // TODO why should items be collapsed recursively?
1916 wxArrayGenericTreeItems& children = item->GetChildren();
1917 size_t count = children.GetCount();
1918 for ( size_t n = 0; n < count; n++ )
1919 {
1920 Collapse(children[n]);
1921 }
1922 #endif
1923
1924 CalculatePositions();
1925
1926 RefreshSubtree(item);
1927
1928 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
1929 GetEventHandler()->ProcessEvent( event );
1930 }
1931
1932 void wxGenericTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
1933 {
1934 Collapse(item);
1935 DeleteChildren(item);
1936 }
1937
1938 void wxGenericTreeCtrl::Toggle(const wxTreeItemId& itemId)
1939 {
1940 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1941
1942 if (item->IsExpanded())
1943 Collapse(itemId);
1944 else
1945 Expand(itemId);
1946 }
1947
1948 void wxGenericTreeCtrl::Unselect()
1949 {
1950 if (m_current)
1951 {
1952 m_current->SetHilight( false );
1953 RefreshLine( m_current );
1954
1955 m_current = NULL;
1956 m_select_me = NULL;
1957 }
1958 }
1959
1960 void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
1961 {
1962 if (item->IsSelected())
1963 {
1964 item->SetHilight(false);
1965 RefreshLine(item);
1966 }
1967
1968 if (item->HasChildren())
1969 {
1970 wxArrayGenericTreeItems& children = item->GetChildren();
1971 size_t count = children.GetCount();
1972 for ( size_t n = 0; n < count; ++n )
1973 {
1974 UnselectAllChildren(children[n]);
1975 }
1976 }
1977 }
1978
1979 void wxGenericTreeCtrl::UnselectAll()
1980 {
1981 wxTreeItemId rootItem = GetRootItem();
1982
1983 // the tree might not have the root item at all
1984 if ( rootItem )
1985 {
1986 UnselectAllChildren((wxGenericTreeItem*) rootItem.m_pItem);
1987 }
1988 }
1989
1990 // Recursive function !
1991 // To stop we must have crt_item<last_item
1992 // Algorithm :
1993 // Tag all next children, when no more children,
1994 // Move to parent (not to tag)
1995 // Keep going... if we found last_item, we stop.
1996 bool
1997 wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item,
1998 wxGenericTreeItem *last_item,
1999 bool select)
2000 {
2001 wxGenericTreeItem *parent = crt_item->GetParent();
2002
2003 if (parent == NULL) // This is root item
2004 return TagAllChildrenUntilLast(crt_item, last_item, select);
2005
2006 wxArrayGenericTreeItems& children = parent->GetChildren();
2007 int index = children.Index(crt_item);
2008 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
2009
2010 size_t count = children.GetCount();
2011 for (size_t n=(size_t)(index+1); n<count; ++n)
2012 {
2013 if ( TagAllChildrenUntilLast(children[n], last_item, select) )
2014 return true;
2015 }
2016
2017 return TagNextChildren(parent, last_item, select);
2018 }
2019
2020 bool
2021 wxGenericTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item,
2022 wxGenericTreeItem *last_item,
2023 bool select)
2024 {
2025 crt_item->SetHilight(select);
2026 RefreshLine(crt_item);
2027
2028 if (crt_item==last_item)
2029 return true;
2030
2031 if (crt_item->HasChildren())
2032 {
2033 wxArrayGenericTreeItems& children = crt_item->GetChildren();
2034 size_t count = children.GetCount();
2035 for ( size_t n = 0; n < count; ++n )
2036 {
2037 if (TagAllChildrenUntilLast(children[n], last_item, select))
2038 return true;
2039 }
2040 }
2041
2042 return false;
2043 }
2044
2045 void
2046 wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1,
2047 wxGenericTreeItem *item2)
2048 {
2049 m_select_me = NULL;
2050
2051 // item2 is not necessary after item1
2052 // choice first' and 'last' between item1 and item2
2053 wxGenericTreeItem *first= (item1->GetY()<item2->GetY()) ? item1 : item2;
2054 wxGenericTreeItem *last = (item1->GetY()<item2->GetY()) ? item2 : item1;
2055
2056 bool select = m_current->IsSelected();
2057
2058 if ( TagAllChildrenUntilLast(first,last,select) )
2059 return;
2060
2061 TagNextChildren(first,last,select);
2062 }
2063
2064 void wxGenericTreeCtrl::DoSelectItem(const wxTreeItemId& itemId,
2065 bool unselect_others,
2066 bool extended_select)
2067 {
2068 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
2069
2070 m_select_me = NULL;
2071
2072 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
2073 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
2074
2075 //wxCHECK_RET( ( (!unselect_others) && is_single),
2076 // wxT("this is a single selection tree") );
2077
2078 // to keep going anyhow !!!
2079 if (is_single)
2080 {
2081 if (item->IsSelected())
2082 return; // nothing to do
2083 unselect_others = true;
2084 extended_select = false;
2085 }
2086 else if ( unselect_others && item->IsSelected() )
2087 {
2088 // selection change if there is more than one item currently selected
2089 wxArrayTreeItemIds selected_items;
2090 if ( GetSelections(selected_items) == 1 )
2091 return;
2092 }
2093
2094 wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
2095 event.m_itemOld = m_current;
2096 // TODO : Here we don't send any selection mode yet !
2097
2098 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
2099 return;
2100
2101 wxTreeItemId parent = GetItemParent( itemId );
2102 while (parent.IsOk())
2103 {
2104 if (!IsExpanded(parent))
2105 Expand( parent );
2106
2107 parent = GetItemParent( parent );
2108 }
2109
2110 // ctrl press
2111 if (unselect_others)
2112 {
2113 if (is_single) Unselect(); // to speed up thing
2114 else UnselectAll();
2115 }
2116
2117 // shift press
2118 if (extended_select)
2119 {
2120 if ( !m_current )
2121 {
2122 m_current =
2123 m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem;
2124 }
2125
2126 // don't change the mark (m_current)
2127 SelectItemRange(m_current, item);
2128 }
2129 else
2130 {
2131 bool select = true; // the default
2132
2133 // Check if we need to toggle hilight (ctrl mode)
2134 if (!unselect_others)
2135 select=!item->IsSelected();
2136
2137 m_current = m_key_current = item;
2138 m_current->SetHilight(select);
2139 RefreshLine( m_current );
2140 }
2141
2142 // This can cause idle processing to select the root
2143 // if no item is selected, so it must be after the
2144 // selection is set
2145 EnsureVisible( itemId );
2146
2147 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
2148 GetEventHandler()->ProcessEvent( event );
2149 }
2150
2151 void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, bool select)
2152 {
2153 if ( select )
2154 {
2155 DoSelectItem(itemId, !HasFlag(wxTR_MULTIPLE));
2156 }
2157 else // deselect
2158 {
2159 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
2160 wxCHECK_RET( item, wxT("SelectItem(): invalid tree item") );
2161
2162 wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
2163 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
2164 return;
2165
2166 item->SetHilight(false);
2167 RefreshLine(item);
2168
2169 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
2170 GetEventHandler()->ProcessEvent( event );
2171 }
2172 }
2173
2174 void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
2175 wxArrayTreeItemIds &array) const
2176 {
2177 if ( item->IsSelected() )
2178 array.Add(wxTreeItemId(item));
2179
2180 if ( item->HasChildren() )
2181 {
2182 wxArrayGenericTreeItems& children = item->GetChildren();
2183 size_t count = children.GetCount();
2184 for ( size_t n = 0; n < count; ++n )
2185 FillArray(children[n], array);
2186 }
2187 }
2188
2189 size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
2190 {
2191 array.Empty();
2192 wxTreeItemId idRoot = GetRootItem();
2193 if ( idRoot.IsOk() )
2194 {
2195 FillArray((wxGenericTreeItem*) idRoot.m_pItem, array);
2196 }
2197 //else: the tree is empty, so no selections
2198
2199 return array.GetCount();
2200 }
2201
2202 void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item)
2203 {
2204 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2205
2206 if (!item.IsOk()) return;
2207
2208 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
2209
2210 // first expand all parent branches
2211 wxGenericTreeItem *parent = gitem->GetParent();
2212
2213 if ( HasFlag(wxTR_HIDE_ROOT) )
2214 {
2215 while ( parent && parent != m_anchor )
2216 {
2217 Expand(parent);
2218 parent = parent->GetParent();
2219 }
2220 }
2221 else
2222 {
2223 while ( parent )
2224 {
2225 Expand(parent);
2226 parent = parent->GetParent();
2227 }
2228 }
2229
2230 //if (parent) CalculatePositions();
2231
2232 ScrollTo(item);
2233 }
2234
2235 void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item)
2236 {
2237 if (!item.IsOk()) return;
2238
2239 // We have to call this here because the label in
2240 // question might just have been added and no screen
2241 // update taken place.
2242 if (m_dirty)
2243 #if defined( __WXMSW__ ) || defined(__WXMAC__)
2244 Update();
2245 #else
2246 DoDirtyProcessing();
2247 #endif
2248 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
2249
2250 // now scroll to the item
2251 int item_y = gitem->GetY();
2252
2253 int start_x = 0;
2254 int start_y = 0;
2255 GetViewStart( &start_x, &start_y );
2256 start_y *= PIXELS_PER_UNIT;
2257
2258 int client_h = 0;
2259 int client_w = 0;
2260 GetClientSize( &client_w, &client_h );
2261
2262 if (item_y < start_y+3)
2263 {
2264 // going down
2265 int x = 0;
2266 int y = 0;
2267 m_anchor->GetSize( x, y, this );
2268 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2269 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2270 int x_pos = GetScrollPos( wxHORIZONTAL );
2271 // Item should appear at top
2272 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT,
2273 x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT,
2274 x_pos, item_y/PIXELS_PER_UNIT );
2275 }
2276 else if (item_y+GetLineHeight(gitem) > start_y+client_h)
2277 {
2278 // going up
2279 int x = 0;
2280 int y = 0;
2281 m_anchor->GetSize( x, y, this );
2282 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2283 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2284 item_y += PIXELS_PER_UNIT+2;
2285 int x_pos = GetScrollPos( wxHORIZONTAL );
2286 // Item should appear at bottom
2287 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT,
2288 x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT,
2289 x_pos,
2290 (item_y+GetLineHeight(gitem)-client_h)/PIXELS_PER_UNIT );
2291 }
2292 }
2293
2294 // FIXME: tree sorting functions are not reentrant and not MT-safe!
2295 static wxGenericTreeCtrl *s_treeBeingSorted = NULL;
2296
2297 static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
2298 wxGenericTreeItem **item2)
2299 {
2300 wxCHECK_MSG( s_treeBeingSorted, 0,
2301 "bug in wxGenericTreeCtrl::SortChildren()" );
2302
2303 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
2304 }
2305
2306 void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId)
2307 {
2308 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
2309
2310 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
2311
2312 wxCHECK_RET( !s_treeBeingSorted,
2313 wxT("wxGenericTreeCtrl::SortChildren is not reentrant") );
2314
2315 wxArrayGenericTreeItems& children = item->GetChildren();
2316 if ( children.GetCount() > 1 )
2317 {
2318 m_dirty = true;
2319
2320 s_treeBeingSorted = this;
2321 children.Sort(tree_ctrl_compare_func);
2322 s_treeBeingSorted = NULL;
2323 }
2324 //else: don't make the tree dirty as nothing changed
2325 }
2326
2327 void wxGenericTreeCtrl::CalculateLineHeight()
2328 {
2329 wxClientDC dc(this);
2330 m_lineHeight = (int)(dc.GetCharHeight() + 4);
2331
2332 if ( m_imageListNormal )
2333 {
2334 // Calculate a m_lineHeight value from the normal Image sizes.
2335 // May be toggle off. Then wxGenericTreeCtrl will spread when
2336 // necessary (which might look ugly).
2337 int n = m_imageListNormal->GetImageCount();
2338 for (int i = 0; i < n ; i++)
2339 {
2340 int width = 0, height = 0;
2341 m_imageListNormal->GetSize(i, width, height);
2342 if (height > m_lineHeight) m_lineHeight = height;
2343 }
2344 }
2345
2346 if ( m_imageListState )
2347 {
2348 // Calculate a m_lineHeight value from the state Image sizes.
2349 // May be toggle off. Then wxGenericTreeCtrl will spread when
2350 // necessary (which might look ugly).
2351 int n = m_imageListState->GetImageCount();
2352 for (int i = 0; i < n ; i++)
2353 {
2354 int width = 0, height = 0;
2355 m_imageListState->GetSize(i, width, height);
2356 if (height > m_lineHeight) m_lineHeight = height;
2357 }
2358 }
2359
2360 if (m_imageListButtons)
2361 {
2362 // Calculate a m_lineHeight value from the Button image sizes.
2363 // May be toggle off. Then wxGenericTreeCtrl will spread when
2364 // necessary (which might look ugly).
2365 int n = m_imageListButtons->GetImageCount();
2366 for (int i = 0; i < n ; i++)
2367 {
2368 int width = 0, height = 0;
2369 m_imageListButtons->GetSize(i, width, height);
2370 if (height > m_lineHeight) m_lineHeight = height;
2371 }
2372 }
2373
2374 if (m_lineHeight < 30)
2375 m_lineHeight += 2; // at least 2 pixels
2376 else
2377 m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
2378 }
2379
2380 void wxGenericTreeCtrl::SetImageList(wxImageList *imageList)
2381 {
2382 if (m_ownsImageListNormal) delete m_imageListNormal;
2383 m_imageListNormal = imageList;
2384 m_ownsImageListNormal = false;
2385 m_dirty = true;
2386
2387 if (m_anchor)
2388 m_anchor->RecursiveResetSize();
2389
2390 // Don't do any drawing if we're setting the list to NULL,
2391 // since we may be in the process of deleting the tree control.
2392 if (imageList)
2393 CalculateLineHeight();
2394 }
2395
2396 void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList)
2397 {
2398 if (m_ownsImageListState) delete m_imageListState;
2399 m_imageListState = imageList;
2400 m_ownsImageListState = false;
2401 m_dirty = true;
2402
2403 if (m_anchor)
2404 m_anchor->RecursiveResetSize();
2405
2406 // Don't do any drawing if we're setting the list to NULL,
2407 // since we may be in the process of deleting the tree control.
2408 if (imageList)
2409 CalculateLineHeight();
2410 }
2411
2412 void wxGenericTreeCtrl::SetButtonsImageList(wxImageList *imageList)
2413 {
2414 if (m_ownsImageListButtons) delete m_imageListButtons;
2415 m_imageListButtons = imageList;
2416 m_ownsImageListButtons = false;
2417 m_dirty = true;
2418
2419 if (m_anchor)
2420 m_anchor->RecursiveResetSize();
2421
2422 CalculateLineHeight();
2423 }
2424
2425 void wxGenericTreeCtrl::AssignButtonsImageList(wxImageList *imageList)
2426 {
2427 SetButtonsImageList(imageList);
2428 m_ownsImageListButtons = true;
2429 }
2430
2431 // -----------------------------------------------------------------------------
2432 // helpers
2433 // -----------------------------------------------------------------------------
2434
2435 void wxGenericTreeCtrl::AdjustMyScrollbars()
2436 {
2437 if (m_anchor)
2438 {
2439 int x = 0, y = 0;
2440 m_anchor->GetSize( x, y, this );
2441 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2442 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2443 int x_pos = GetScrollPos( wxHORIZONTAL );
2444 int y_pos = GetScrollPos( wxVERTICAL );
2445 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT,
2446 x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT,
2447 x_pos, y_pos );
2448 }
2449 else
2450 {
2451 SetScrollbars( 0, 0, 0, 0 );
2452 }
2453 }
2454
2455 int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
2456 {
2457 if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
2458 return item->GetHeight();
2459 else
2460 return m_lineHeight;
2461 }
2462
2463 void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
2464 {
2465 item->SetFont(this, dc);
2466 item->CalculateSize(this, dc);
2467
2468 wxCoord text_h = item->GetTextHeight();
2469
2470 int image_h = 0, image_w = 0;
2471 int image = item->GetCurrentImage();
2472 if ( image != NO_IMAGE )
2473 {
2474 if ( m_imageListNormal )
2475 {
2476 m_imageListNormal->GetSize(image, image_w, image_h);
2477 image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
2478 }
2479 else
2480 {
2481 image = NO_IMAGE;
2482 }
2483 }
2484
2485 int state_h = 0, state_w = 0;
2486 int state = item->GetState();
2487 if ( state != wxTREE_ITEMSTATE_NONE )
2488 {
2489 if ( m_imageListState )
2490 {
2491 m_imageListState->GetSize(state, state_w, state_h);
2492 if ( image_w != 0 )
2493 state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
2494 else
2495 state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
2496 }
2497 else
2498 {
2499 state = wxTREE_ITEMSTATE_NONE;
2500 }
2501 }
2502
2503 int total_h = GetLineHeight(item);
2504 bool drawItemBackground = false,
2505 hasBgColour = false;
2506
2507 if ( item->IsSelected() )
2508 {
2509 dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
2510 drawItemBackground = true;
2511 }
2512 else
2513 {
2514 wxColour colBg;
2515 wxTreeItemAttr * const attr = item->GetAttributes();
2516 if ( attr && attr->HasBackgroundColour() )
2517 {
2518 drawItemBackground =
2519 hasBgColour = true;
2520 colBg = attr->GetBackgroundColour();
2521 }
2522 else
2523 {
2524 colBg = GetBackgroundColour();
2525 }
2526 dc.SetBrush(wxBrush(colBg, wxBRUSHSTYLE_SOLID));
2527 }
2528
2529 int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0;
2530
2531 if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) )
2532 {
2533 int x, w, h;
2534 x=0;
2535 GetVirtualSize(&w, &h);
2536 wxRect rect( x, item->GetY()+offset, w, total_h-offset);
2537 if (!item->IsSelected())
2538 {
2539 dc.DrawRectangle(rect);
2540 }
2541 else
2542 {
2543 int flags = wxCONTROL_SELECTED;
2544 if (m_hasFocus
2545 #if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS
2546 && IsControlActive( (ControlRef)GetHandle() )
2547 #endif
2548 )
2549 flags |= wxCONTROL_FOCUSED;
2550 if ((item == m_current) && (m_hasFocus))
2551 flags |= wxCONTROL_CURRENT;
2552
2553 wxRendererNative::Get().
2554 DrawItemSelectionRect(this, dc, rect, flags);
2555 }
2556 }
2557 else // no full row highlight
2558 {
2559 if ( item->IsSelected() &&
2560 (state != wxTREE_ITEMSTATE_NONE || image != NO_IMAGE) )
2561 {
2562 // If it's selected, and there's an state image or normal image,
2563 // then we should take care to leave the area under the image
2564 // painted in the background colour.
2565 wxRect rect( item->GetX() + state_w + image_w - 2,
2566 item->GetY() + offset,
2567 item->GetWidth() - state_w - image_w + 2,
2568 total_h - offset );
2569 #if !defined(__WXGTK20__) && !defined(__WXMAC__)
2570 dc.DrawRectangle( rect );
2571 #else
2572 rect.x -= 1;
2573 rect.width += 2;
2574
2575 int flags = wxCONTROL_SELECTED;
2576 if (m_hasFocus)
2577 flags |= wxCONTROL_FOCUSED;
2578 if ((item == m_current) && (m_hasFocus))
2579 flags |= wxCONTROL_CURRENT;
2580 wxRendererNative::Get().
2581 DrawItemSelectionRect(this, dc, rect, flags);
2582 #endif
2583 }
2584 // On GTK+ 2, drawing a 'normal' background is wrong for themes that
2585 // don't allow backgrounds to be customized. Not drawing the background,
2586 // except for custom item backgrounds, works for both kinds of theme.
2587 else if (drawItemBackground)
2588 {
2589 wxRect rect( item->GetX() + state_w + image_w - 2,
2590 item->GetY() + offset,
2591 item->GetWidth() - state_w - image_w + 2,
2592 total_h - offset );
2593 if ( hasBgColour )
2594 {
2595 dc.DrawRectangle( rect );
2596 }
2597 else // no specific background colour
2598 {
2599 rect.x -= 1;
2600 rect.width += 2;
2601
2602 int flags = wxCONTROL_SELECTED;
2603 if (m_hasFocus)
2604 flags |= wxCONTROL_FOCUSED;
2605 if ((item == m_current) && (m_hasFocus))
2606 flags |= wxCONTROL_CURRENT;
2607 wxRendererNative::Get().
2608 DrawItemSelectionRect(this, dc, rect, flags);
2609 }
2610 }
2611 }
2612
2613 if ( state != wxTREE_ITEMSTATE_NONE )
2614 {
2615 dc.SetClippingRegion( item->GetX(), item->GetY(), state_w, total_h );
2616 m_imageListState->Draw( state, dc,
2617 item->GetX(),
2618 item->GetY() +
2619 (total_h > state_h ? (total_h-state_h)/2
2620 : 0),
2621 wxIMAGELIST_DRAW_TRANSPARENT );
2622 dc.DestroyClippingRegion();
2623 }
2624
2625 if ( image != NO_IMAGE )
2626 {
2627 dc.SetClippingRegion(item->GetX() + state_w, item->GetY(),
2628 image_w, total_h);
2629 m_imageListNormal->Draw( image, dc,
2630 item->GetX() + state_w,
2631 item->GetY() +
2632 (total_h > image_h ? (total_h-image_h)/2
2633 : 0),
2634 wxIMAGELIST_DRAW_TRANSPARENT );
2635 dc.DestroyClippingRegion();
2636 }
2637
2638 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
2639 int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0;
2640 dc.DrawText( item->GetText(),
2641 (wxCoord)(state_w + image_w + item->GetX()),
2642 (wxCoord)(item->GetY() + extraH));
2643
2644 // restore normal font
2645 dc.SetFont( m_normalFont );
2646
2647 if (item == m_dndEffectItem)
2648 {
2649 dc.SetPen( *wxBLACK_PEN );
2650 // DnD visual effects
2651 switch (m_dndEffect)
2652 {
2653 case BorderEffect:
2654 {
2655 dc.SetBrush(*wxTRANSPARENT_BRUSH);
2656 int w = item->GetWidth() + 2;
2657 int h = total_h + 2;
2658 dc.DrawRectangle( item->GetX() - 1, item->GetY() - 1, w, h);
2659 break;
2660 }
2661 case AboveEffect:
2662 {
2663 int x = item->GetX(),
2664 y = item->GetY();
2665 dc.DrawLine( x, y, x + item->GetWidth(), y);
2666 break;
2667 }
2668 case BelowEffect:
2669 {
2670 int x = item->GetX(),
2671 y = item->GetY();
2672 y += total_h - 1;
2673 dc.DrawLine( x, y, x + item->GetWidth(), y);
2674 break;
2675 }
2676 case NoEffect:
2677 break;
2678 }
2679 }
2680 }
2681
2682 void
2683 wxGenericTreeCtrl::PaintLevel(wxGenericTreeItem *item,
2684 wxDC &dc,
2685 int level,
2686 int &y)
2687 {
2688 int x = level*m_indent;
2689 if (!HasFlag(wxTR_HIDE_ROOT))
2690 {
2691 x += m_indent;
2692 }
2693 else if (level == 0)
2694 {
2695 // always expand hidden root
2696 int origY = y;
2697 wxArrayGenericTreeItems& children = item->GetChildren();
2698 int count = children.GetCount();
2699 if (count > 0)
2700 {
2701 int n = 0, oldY;
2702 do {
2703 oldY = y;
2704 PaintLevel(children[n], dc, 1, y);
2705 } while (++n < count);
2706
2707 if ( !HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT)
2708 && count > 0 )
2709 {
2710 // draw line down to last child
2711 origY += GetLineHeight(children[0])>>1;
2712 oldY += GetLineHeight(children[n-1])>>1;
2713 dc.DrawLine(3, origY, 3, oldY);
2714 }
2715 }
2716 return;
2717 }
2718
2719 item->SetX(x+m_spacing);
2720 item->SetY(y);
2721
2722 int h = GetLineHeight(item);
2723 int y_top = y;
2724 int y_mid = y_top + (h>>1);
2725 y += h;
2726
2727 int exposed_x = dc.LogicalToDeviceX(0);
2728 int exposed_y = dc.LogicalToDeviceY(y_top);
2729
2730 if (IsExposed(exposed_x, exposed_y, 10000, h)) // 10000 = very much
2731 {
2732 const wxPen *pen =
2733 #ifndef __WXMAC__
2734 // don't draw rect outline if we already have the
2735 // background color under Mac
2736 (item->IsSelected() && m_hasFocus) ? wxBLACK_PEN :
2737 #endif // !__WXMAC__
2738 wxTRANSPARENT_PEN;
2739
2740 wxColour colText;
2741 if ( item->IsSelected()
2742 #if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS
2743 // On wxMac, if the tree doesn't have the focus we draw an empty
2744 // rectangle, so we want to make sure that the text is visible
2745 // against the normal background, not the highlightbackground, so
2746 // don't use the highlight text colour unless we have the focus.
2747 && m_hasFocus && IsControlActive( (ControlRef)GetHandle() )
2748 #endif
2749 )
2750 {
2751 #ifdef __WXMAC__
2752 colText = *wxWHITE;
2753 #else
2754 colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
2755 #endif
2756 }
2757 else
2758 {
2759 wxTreeItemAttr *attr = item->GetAttributes();
2760 if (attr && attr->HasTextColour())
2761 colText = attr->GetTextColour();
2762 else
2763 colText = GetForegroundColour();
2764 }
2765
2766 // prepare to draw
2767 dc.SetTextForeground(colText);
2768 dc.SetPen(*pen);
2769
2770 // draw
2771 PaintItem(item, dc);
2772
2773 if (HasFlag(wxTR_ROW_LINES))
2774 {
2775 // if the background colour is white, choose a
2776 // contrasting color for the lines
2777 dc.SetPen(*((GetBackgroundColour() == *wxWHITE)
2778 ? wxMEDIUM_GREY_PEN : wxWHITE_PEN));
2779 dc.DrawLine(0, y_top, 10000, y_top);
2780 dc.DrawLine(0, y, 10000, y);
2781 }
2782
2783 // restore DC objects
2784 dc.SetBrush(*wxWHITE_BRUSH);
2785 dc.SetPen(m_dottedPen);
2786 dc.SetTextForeground(*wxBLACK);
2787
2788 if ( !HasFlag(wxTR_NO_LINES) )
2789 {
2790 // draw the horizontal line here
2791 int x_start = x;
2792 if (x > (signed)m_indent)
2793 x_start -= m_indent;
2794 else if (HasFlag(wxTR_LINES_AT_ROOT))
2795 x_start = 3;
2796 dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
2797 }
2798
2799 // should the item show a button?
2800 if ( item->HasPlus() && HasButtons() )
2801 {
2802 if ( m_imageListButtons )
2803 {
2804 // draw the image button here
2805 int image_h = 0,
2806 image_w = 0;
2807 int image = item->IsExpanded() ? wxTreeItemIcon_Expanded
2808 : wxTreeItemIcon_Normal;
2809 if ( item->IsSelected() )
2810 image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal;
2811
2812 m_imageListButtons->GetSize(image, image_w, image_h);
2813 int xx = x - image_w/2;
2814 int yy = y_mid - image_h/2;
2815
2816 wxDCClipper clip(dc, xx, yy, image_w, image_h);
2817 m_imageListButtons->Draw(image, dc, xx, yy,
2818 wxIMAGELIST_DRAW_TRANSPARENT);
2819 }
2820 else // no custom buttons
2821 {
2822 static const int wImage = 9;
2823 static const int hImage = 9;
2824
2825 int flag = 0;
2826 if (item->IsExpanded())
2827 flag |= wxCONTROL_EXPANDED;
2828 if (item == m_underMouse)
2829 flag |= wxCONTROL_CURRENT;
2830
2831 wxRendererNative::Get().DrawTreeItemButton
2832 (
2833 this,
2834 dc,
2835 wxRect(x - wImage/2,
2836 y_mid - hImage/2,
2837 wImage, hImage),
2838 flag
2839 );
2840 }
2841 }
2842 }
2843
2844 if (item->IsExpanded())
2845 {
2846 wxArrayGenericTreeItems& children = item->GetChildren();
2847 int count = children.GetCount();
2848 if (count > 0)
2849 {
2850 int n = 0, oldY;
2851 ++level;
2852 do {
2853 oldY = y;
2854 PaintLevel(children[n], dc, level, y);
2855 } while (++n < count);
2856
2857 if (!HasFlag(wxTR_NO_LINES) && count > 0)
2858 {
2859 // draw line down to last child
2860 oldY += GetLineHeight(children[n-1])>>1;
2861 if (HasButtons()) y_mid += 5;
2862
2863 // Only draw the portion of the line that is visible, in case
2864 // it is huge
2865 wxCoord xOrigin=0, yOrigin=0, width, height;
2866 dc.GetDeviceOrigin(&xOrigin, &yOrigin);
2867 yOrigin = abs(yOrigin);
2868 GetClientSize(&width, &height);
2869
2870 // Move end points to the begining/end of the view?
2871 if (y_mid < yOrigin)
2872 y_mid = yOrigin;
2873 if (oldY > yOrigin + height)
2874 oldY = yOrigin + height;
2875
2876 // after the adjustments if y_mid is larger than oldY then the
2877 // line isn't visible at all so don't draw anything
2878 if (y_mid < oldY)
2879 dc.DrawLine(x, y_mid, x, oldY);
2880 }
2881 }
2882 }
2883 }
2884
2885 void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item)
2886 {
2887 if ( item )
2888 {
2889 if ( item->HasPlus() )
2890 {
2891 // it's a folder, indicate it by a border
2892 DrawBorder(item);
2893 }
2894 else
2895 {
2896 // draw a line under the drop target because the item will be
2897 // dropped there
2898 DrawLine(item, !m_dropEffectAboveItem );
2899 }
2900
2901 SetCursor(*wxSTANDARD_CURSOR);
2902 }
2903 else
2904 {
2905 // can't drop here
2906 SetCursor(wxCURSOR_NO_ENTRY);
2907 }
2908 }
2909
2910 void wxGenericTreeCtrl::DrawBorder(const wxTreeItemId &item)
2911 {
2912 wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" );
2913
2914 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
2915
2916 if (m_dndEffect == NoEffect)
2917 {
2918 m_dndEffect = BorderEffect;
2919 m_dndEffectItem = i;
2920 }
2921 else
2922 {
2923 m_dndEffect = NoEffect;
2924 m_dndEffectItem = NULL;
2925 }
2926
2927 wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 );
2928 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2929 RefreshRect( rect );
2930 }
2931
2932 void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below)
2933 {
2934 wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" );
2935
2936 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
2937
2938 if (m_dndEffect == NoEffect)
2939 {
2940 if (below)
2941 m_dndEffect = BelowEffect;
2942 else
2943 m_dndEffect = AboveEffect;
2944 m_dndEffectItem = i;
2945 }
2946 else
2947 {
2948 m_dndEffect = NoEffect;
2949 m_dndEffectItem = NULL;
2950 }
2951
2952 wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 );
2953 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
2954 RefreshRect( rect );
2955 }
2956
2957 // -----------------------------------------------------------------------------
2958 // wxWidgets callbacks
2959 // -----------------------------------------------------------------------------
2960
2961 void wxGenericTreeCtrl::OnSize( wxSizeEvent &event )
2962 {
2963 #ifdef __WXGTK__
2964 if (HasFlag( wxTR_FULL_ROW_HIGHLIGHT) && m_current)
2965 RefreshLine( m_current );
2966 #endif
2967
2968 event.Skip(true);
2969 }
2970
2971 void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
2972 {
2973 wxPaintDC dc(this);
2974 PrepareDC( dc );
2975
2976 if ( !m_anchor)
2977 return;
2978
2979 dc.SetFont( m_normalFont );
2980 dc.SetPen( m_dottedPen );
2981
2982 // this is now done dynamically
2983 //if(GetImageList() == NULL)
2984 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
2985
2986 int y = 2;
2987 PaintLevel( m_anchor, dc, 0, y );
2988 }
2989
2990 void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event )
2991 {
2992 m_hasFocus = true;
2993
2994 RefreshSelected();
2995
2996 event.Skip();
2997 }
2998
2999 void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event )
3000 {
3001 m_hasFocus = false;
3002
3003 RefreshSelected();
3004
3005 event.Skip();
3006 }
3007
3008 void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
3009 {
3010 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, this);
3011 te.m_evtKey = event;
3012 if ( GetEventHandler()->ProcessEvent( te ) )
3013 {
3014 // intercepted by the user code
3015 return;
3016 }
3017
3018 if ( (m_current == 0) || (m_key_current == 0) )
3019 {
3020 event.Skip();
3021 return;
3022 }
3023
3024 // how should the selection work for this event?
3025 bool is_multiple, extended_select, unselect_others;
3026 EventFlagsToSelType(GetWindowStyleFlag(),
3027 event.ShiftDown(),
3028 event.CmdDown(),
3029 is_multiple, extended_select, unselect_others);
3030
3031 if (GetLayoutDirection() == wxLayout_RightToLeft)
3032 {
3033 if (event.GetKeyCode() == WXK_RIGHT)
3034 event.m_keyCode = WXK_LEFT;
3035 else if (event.GetKeyCode() == WXK_LEFT)
3036 event.m_keyCode = WXK_RIGHT;
3037 }
3038
3039 // + : Expand
3040 // - : Collaspe
3041 // * : Expand all/Collapse all
3042 // ' ' | return : activate
3043 // up : go up (not last children!)
3044 // down : go down
3045 // left : go to parent
3046 // right : open if parent and go next
3047 // home : go to root
3048 // end : go to last item without opening parents
3049 // alnum : start or continue searching for the item with this prefix
3050 int keyCode = event.GetKeyCode();
3051 switch ( keyCode )
3052 {
3053 case '+':
3054 case WXK_ADD:
3055 if (m_current->HasPlus() && !IsExpanded(m_current))
3056 {
3057 Expand(m_current);
3058 }
3059 break;
3060
3061 case '*':
3062 case WXK_MULTIPLY:
3063 if ( !IsExpanded(m_current) )
3064 {
3065 // expand all
3066 ExpandAllChildren(m_current);
3067 break;
3068 }
3069 //else: fall through to Collapse() it
3070
3071 case '-':
3072 case WXK_SUBTRACT:
3073 if (IsExpanded(m_current))
3074 {
3075 Collapse(m_current);
3076 }
3077 break;
3078
3079 case WXK_MENU:
3080 {
3081 // Use the item's bounding rectangle to determine position for
3082 // the event
3083 wxRect ItemRect;
3084 GetBoundingRect(m_current, ItemRect, true);
3085
3086 wxTreeEvent
3087 eventMenu(wxEVT_COMMAND_TREE_ITEM_MENU, this, m_current);
3088 // Use the left edge, vertical middle
3089 eventMenu.m_pointDrag = wxPoint(ItemRect.GetX(),
3090 ItemRect.GetY() +
3091 ItemRect.GetHeight() / 2);
3092 GetEventHandler()->ProcessEvent( eventMenu );
3093 }
3094 break;
3095
3096 case ' ':
3097 case WXK_RETURN:
3098 if ( !event.HasModifiers() )
3099 {
3100 wxTreeEvent
3101 eventAct(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, m_current);
3102 GetEventHandler()->ProcessEvent( eventAct );
3103 }
3104
3105 // in any case, also generate the normal key event for this key,
3106 // even if we generated the ACTIVATED event above: this is what
3107 // wxMSW does and it makes sense because you might not want to
3108 // process ACTIVATED event at all and handle Space and Return
3109 // directly (and differently) which would be impossible otherwise
3110 event.Skip();
3111 break;
3112
3113 // up goes to the previous sibling or to the last
3114 // of its children if it's expanded
3115 case WXK_UP:
3116 {
3117 wxTreeItemId prev = GetPrevSibling( m_key_current );
3118 if (!prev)
3119 {
3120 prev = GetItemParent( m_key_current );
3121 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
3122 {
3123 break; // don't go to root if it is hidden
3124 }
3125 if (prev)
3126 {
3127 wxTreeItemIdValue cookie;
3128 wxTreeItemId current = m_key_current;
3129 // TODO: Huh? If we get here, we'd better be the first
3130 // child of our parent. How else could it be?
3131 if (current == GetFirstChild( prev, cookie ))
3132 {
3133 // otherwise we return to where we came from
3134 DoSelectItem(prev,
3135 unselect_others,
3136 extended_select);
3137 m_key_current = (wxGenericTreeItem*) prev.m_pItem;
3138 break;
3139 }
3140 }
3141 }
3142 if (prev)
3143 {
3144 while ( IsExpanded(prev) && HasChildren(prev) )
3145 {
3146 wxTreeItemId child = GetLastChild(prev);
3147 if ( child )
3148 {
3149 prev = child;
3150 }
3151 }
3152
3153 DoSelectItem( prev, unselect_others, extended_select );
3154 m_key_current=(wxGenericTreeItem*) prev.m_pItem;
3155 }
3156 }
3157 break;
3158
3159 // left arrow goes to the parent
3160 case WXK_LEFT:
3161 {
3162 wxTreeItemId prev = GetItemParent( m_current );
3163 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
3164 {
3165 // don't go to root if it is hidden
3166 prev = GetPrevSibling( m_current );
3167 }
3168 if (prev)
3169 {
3170 DoSelectItem( prev, unselect_others, extended_select );
3171 }
3172 }
3173 break;
3174
3175 case WXK_RIGHT:
3176 // this works the same as the down arrow except that we
3177 // also expand the item if it wasn't expanded yet
3178 if (m_current != GetRootItem().m_pItem || !HasFlag(wxTR_HIDE_ROOT))
3179 Expand(m_current);
3180 //else: don't try to expand hidden root item (which can be the
3181 // current one when the tree is empty)
3182
3183 // fall through
3184
3185 case WXK_DOWN:
3186 {
3187 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
3188 {
3189 wxTreeItemIdValue cookie;
3190 wxTreeItemId child = GetFirstChild( m_key_current, cookie );
3191 if ( !child )
3192 break;
3193
3194 DoSelectItem( child, unselect_others, extended_select );
3195 m_key_current=(wxGenericTreeItem*) child.m_pItem;
3196 }
3197 else
3198 {
3199 wxTreeItemId next = GetNextSibling( m_key_current );
3200 if (!next)
3201 {
3202 wxTreeItemId current = m_key_current;
3203 while (current.IsOk() && !next)
3204 {
3205 current = GetItemParent( current );
3206 if (current) next = GetNextSibling( current );
3207 }
3208 }
3209 if (next)
3210 {
3211 DoSelectItem( next, unselect_others, extended_select );
3212 m_key_current=(wxGenericTreeItem*) next.m_pItem;
3213 }
3214 }
3215 }
3216 break;
3217
3218 // <End> selects the last visible tree item
3219 case WXK_END:
3220 {
3221 wxTreeItemId last = GetRootItem();
3222
3223 while ( last.IsOk() && IsExpanded(last) )
3224 {
3225 wxTreeItemId lastChild = GetLastChild(last);
3226
3227 // it may happen if the item was expanded but then all of
3228 // its children have been deleted - so IsExpanded() returned
3229 // true, but GetLastChild() returned invalid item
3230 if ( !lastChild )
3231 break;
3232
3233 last = lastChild;
3234 }
3235
3236 if ( last.IsOk() )
3237 {
3238 DoSelectItem( last, unselect_others, extended_select );
3239 }
3240 }
3241 break;
3242
3243 // <Home> selects the root item
3244 case WXK_HOME:
3245 {
3246 wxTreeItemId prev = GetRootItem();
3247 if (!prev)
3248 break;
3249
3250 if ( HasFlag(wxTR_HIDE_ROOT) )
3251 {
3252 wxTreeItemIdValue cookie;
3253 prev = GetFirstChild(prev, cookie);
3254 if (!prev)
3255 break;
3256 }
3257
3258 DoSelectItem( prev, unselect_others, extended_select );
3259 }
3260 break;
3261
3262 default:
3263 // do not use wxIsalnum() here
3264 if ( !event.HasModifiers() &&
3265 ((keyCode >= '0' && keyCode <= '9') ||
3266 (keyCode >= 'a' && keyCode <= 'z') ||
3267 (keyCode >= 'A' && keyCode <= 'Z' )))
3268 {
3269 // find the next item starting with the given prefix
3270 wxChar ch = (wxChar)keyCode;
3271
3272 wxTreeItemId id = FindItem(m_current, m_findPrefix + ch);
3273 if ( !id.IsOk() )
3274 {
3275 // no such item
3276 break;
3277 }
3278
3279 SelectItem(id);
3280
3281 m_findPrefix += ch;
3282
3283 // also start the timer to reset the current prefix if the user
3284 // doesn't press any more alnum keys soon -- we wouldn't want
3285 // to use this prefix for a new item search
3286 if ( !m_findTimer )
3287 {
3288 m_findTimer = new wxTreeFindTimer(this);
3289 }
3290
3291 m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT);
3292 }
3293 else
3294 {
3295 event.Skip();
3296 }
3297 }
3298 }
3299
3300 wxTreeItemId
3301 wxGenericTreeCtrl::DoTreeHitTest(const wxPoint& point, int& flags) const
3302 {
3303 int w, h;
3304 GetSize(&w, &h);
3305 flags=0;
3306 if (point.x<0) flags |= wxTREE_HITTEST_TOLEFT;
3307 if (point.x>w) flags |= wxTREE_HITTEST_TORIGHT;
3308 if (point.y<0) flags |= wxTREE_HITTEST_ABOVE;
3309 if (point.y>h) flags |= wxTREE_HITTEST_BELOW;
3310 if (flags) return wxTreeItemId();
3311
3312 if (m_anchor == NULL)
3313 {
3314 flags = wxTREE_HITTEST_NOWHERE;
3315 return wxTreeItemId();
3316 }
3317
3318 wxGenericTreeItem *hit = m_anchor->HitTest(CalcUnscrolledPosition(point),
3319 this, flags, 0);
3320 if (hit == NULL)
3321 {
3322 flags = wxTREE_HITTEST_NOWHERE;
3323 return wxTreeItemId();
3324 }
3325 return hit;
3326 }
3327
3328 // get the bounding rectangle of the item (or of its label only)
3329 bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
3330 wxRect& rect,
3331 bool textOnly) const
3332 {
3333 wxCHECK_MSG( item.IsOk(), false,
3334 "invalid item in wxGenericTreeCtrl::GetBoundingRect" );
3335
3336 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
3337
3338 if ( textOnly )
3339 {
3340 int image_h = 0, image_w = 0;
3341 int image = ((wxGenericTreeItem*) item.m_pItem)->GetCurrentImage();
3342 if ( image != NO_IMAGE && m_imageListNormal )
3343 {
3344 m_imageListNormal->GetSize( image, image_w, image_h );
3345 image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
3346 }
3347
3348 int state_h = 0, state_w = 0;
3349 int state = ((wxGenericTreeItem*) item.m_pItem)->GetState();
3350 if ( state != wxTREE_ITEMSTATE_NONE && m_imageListState )
3351 {
3352 m_imageListState->GetSize( state, state_w, state_h );
3353 if ( image_w != 0 )
3354 state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
3355 else
3356 state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
3357 }
3358
3359 rect.x = i->GetX() + state_w + image_w;
3360 rect.width = i->GetWidth() - state_w - image_w;
3361
3362 }
3363 else // the entire line
3364 {
3365 rect.x = 0;
3366 rect.width = GetClientSize().x;
3367 }
3368
3369 rect.y = i->GetY();
3370 rect.height = GetLineHeight(i);
3371
3372 // we have to return the logical coordinates, not physical ones
3373 rect.SetTopLeft(CalcScrolledPosition(rect.GetTopLeft()));
3374
3375 return true;
3376 }
3377
3378 wxTextCtrl *wxGenericTreeCtrl::EditLabel(const wxTreeItemId& item,
3379 wxClassInfo * WXUNUSED(textCtrlClass))
3380 {
3381 wxCHECK_MSG( item.IsOk(), NULL, _T("can't edit an invalid item") );
3382
3383 wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem;
3384
3385 wxTreeEvent te(wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, this, itemEdit);
3386 if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() )
3387 {
3388 // vetoed by user
3389 return NULL;
3390 }
3391
3392 // We have to call this here because the label in
3393 // question might just have been added and no screen
3394 // update taken place.
3395 if ( m_dirty )
3396 #if defined( __WXMSW__ ) || defined(__WXMAC__)
3397 Update();
3398 #else
3399 DoDirtyProcessing();
3400 #endif
3401
3402 // TODO: use textCtrlClass here to create the control of correct class
3403 m_textCtrl = new wxTreeTextCtrl(this, itemEdit);
3404
3405 m_textCtrl->SetFocus();
3406
3407 return m_textCtrl;
3408 }
3409
3410 // returns a pointer to the text edit control if the item is being
3411 // edited, NULL otherwise (it's assumed that no more than one item may
3412 // be edited simultaneously)
3413 wxTextCtrl* wxGenericTreeCtrl::GetEditControl() const
3414 {
3415 return m_textCtrl;
3416 }
3417
3418 void wxGenericTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item),
3419 bool discardChanges)
3420 {
3421 wxCHECK_RET( m_textCtrl, _T("not editing label") );
3422
3423 m_textCtrl->EndEdit(discardChanges);
3424 }
3425
3426 bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item,
3427 const wxString& value)
3428 {
3429 wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item);
3430 le.m_label = value;
3431 le.m_editCancelled = false;
3432
3433 return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
3434 }
3435
3436 void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item)
3437 {
3438 // let owner know that the edit was cancelled
3439 wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item);
3440 le.m_label = wxEmptyString;
3441 le.m_editCancelled = true;
3442
3443 GetEventHandler()->ProcessEvent( le );
3444 }
3445
3446 void wxGenericTreeCtrl::OnRenameTimer()
3447 {
3448 EditLabel( m_current );
3449 }
3450
3451 void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
3452 {
3453 if ( !m_anchor )return;
3454
3455 wxPoint pt = CalcUnscrolledPosition(event.GetPosition());
3456
3457 // Is the mouse over a tree item button?
3458 int flags = 0;
3459 wxGenericTreeItem *thisItem = m_anchor->HitTest(pt, this, flags, 0);
3460 wxGenericTreeItem *underMouse = thisItem;
3461 #if wxUSE_TOOLTIPS
3462 bool underMouseChanged = (underMouse != m_underMouse) ;
3463 #endif // wxUSE_TOOLTIPS
3464
3465 if ((underMouse) &&
3466 (flags & wxTREE_HITTEST_ONITEMBUTTON) &&
3467 (!event.LeftIsDown()) &&
3468 (!m_isDragging) &&
3469 (!m_renameTimer || !m_renameTimer->IsRunning()))
3470 {
3471 }
3472 else
3473 {
3474 underMouse = NULL;
3475 }
3476
3477 if (underMouse != m_underMouse)
3478 {
3479 if (m_underMouse)
3480 {
3481 // unhighlight old item
3482 wxGenericTreeItem *tmp = m_underMouse;
3483 m_underMouse = NULL;
3484 RefreshLine( tmp );
3485 }
3486
3487 m_underMouse = underMouse;
3488 if (m_underMouse)
3489 RefreshLine( m_underMouse );
3490 }
3491
3492 #if wxUSE_TOOLTIPS
3493 // Determines what item we are hovering over and need a tooltip for
3494 wxTreeItemId hoverItem = thisItem;
3495
3496 // We do not want a tooltip if we are dragging, or if the rename timer is
3497 // running
3498 if ( underMouseChanged &&
3499 hoverItem.IsOk() &&
3500 !m_isDragging &&
3501 (!m_renameTimer || !m_renameTimer->IsRunning()) )
3502 {
3503 // Ask the tree control what tooltip (if any) should be shown
3504 wxTreeEvent
3505 hevent(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, this, hoverItem);
3506
3507 if ( GetEventHandler()->ProcessEvent(hevent) && hevent.IsAllowed() )
3508 {
3509 SetToolTip(hevent.m_label);
3510 }
3511 }
3512 #endif
3513
3514 // we process left mouse up event (enables in-place edit), middle/right down
3515 // (pass to the user code), left dbl click (activate item) and
3516 // dragging/moving events for items drag-and-drop
3517 if ( !(event.LeftDown() ||
3518 event.LeftUp() ||
3519 event.MiddleDown() ||
3520 event.RightDown() ||
3521 event.LeftDClick() ||
3522 event.Dragging() ||
3523 ((event.Moving() || event.RightUp()) && m_isDragging)) )
3524 {
3525 event.Skip();
3526
3527 return;
3528 }
3529
3530
3531 flags = 0;
3532 wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0);
3533
3534 if ( event.Dragging() && !m_isDragging )
3535 {
3536 if (m_dragCount == 0)
3537 m_dragStart = pt;
3538
3539 m_dragCount++;
3540
3541 if (m_dragCount != 3)
3542 {
3543 // wait until user drags a bit further...
3544 return;
3545 }
3546
3547 wxEventType command = event.RightIsDown()
3548 ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
3549 : wxEVT_COMMAND_TREE_BEGIN_DRAG;
3550
3551 wxTreeEvent nevent(command, this, m_current);
3552 nevent.SetPoint(CalcScrolledPosition(pt));
3553
3554 // by default the dragging is not supported, the user code must
3555 // explicitly allow the event for it to take place
3556 nevent.Veto();
3557
3558 if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() )
3559 {
3560 // we're going to drag this item
3561 m_isDragging = true;
3562
3563 // remember the old cursor because we will change it while
3564 // dragging
3565 m_oldCursor = m_cursor;
3566
3567 // in a single selection control, hide the selection temporarily
3568 if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
3569 {
3570 m_oldSelection = (wxGenericTreeItem*) GetSelection().m_pItem;
3571
3572 if ( m_oldSelection )
3573 {
3574 m_oldSelection->SetHilight(false);
3575 RefreshLine(m_oldSelection);
3576 }
3577 }
3578
3579 CaptureMouse();
3580 }
3581 }
3582 else if ( event.Dragging() )
3583 {
3584 if ( item != m_dropTarget )
3585 {
3586 // unhighlight the previous drop target
3587 DrawDropEffect(m_dropTarget);
3588
3589 m_dropTarget = item;
3590
3591 // highlight the current drop target if any
3592 DrawDropEffect(m_dropTarget);
3593
3594 #if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXGTK20__)
3595 Update();
3596 #else
3597 // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI)
3598 // instead (needs to be tested!)
3599 wxYieldIfNeeded();
3600 #endif
3601 }
3602 }
3603 else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
3604 {
3605 ReleaseMouse();
3606
3607 // erase the highlighting
3608 DrawDropEffect(m_dropTarget);
3609
3610 if ( m_oldSelection )
3611 {
3612 m_oldSelection->SetHilight(true);
3613 RefreshLine(m_oldSelection);
3614 m_oldSelection = NULL;
3615 }
3616
3617 // generate the drag end event
3618 wxTreeEvent eventEndDrag(wxEVT_COMMAND_TREE_END_DRAG, this, item);
3619
3620 eventEndDrag.m_pointDrag = CalcScrolledPosition(pt);
3621
3622 (void)GetEventHandler()->ProcessEvent(eventEndDrag);
3623
3624 m_isDragging = false;
3625 m_dropTarget = NULL;
3626
3627 SetCursor(m_oldCursor);
3628
3629 #if defined( __WXMSW__ ) || defined(__WXMAC__) || defined(__WXGTK20__)
3630 Update();
3631 #else
3632 // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI)
3633 // instead (needs to be tested!)
3634 wxYieldIfNeeded();
3635 #endif
3636 }
3637 else
3638 {
3639 // If we got to this point, we are not dragging or moving the mouse.
3640 // Because the code in carbon/toplevel.cpp will only set focus to the
3641 // tree if we skip for EVT_LEFT_DOWN, we MUST skip this event here for
3642 // focus to work.
3643 // We skip even if we didn't hit an item because we still should
3644 // restore focus to the tree control even if we didn't exactly hit an
3645 // item.
3646 if ( event.LeftDown() )
3647 {
3648 event.Skip();
3649 }
3650
3651 // here we process only the messages which happen on tree items
3652
3653 m_dragCount = 0;
3654
3655 if (item == NULL) return; /* we hit the blank area */
3656
3657 if ( event.RightDown() )
3658 {
3659 // If the item is already selected, do not update the selection.
3660 // Multi-selections should not be cleared if a selected item is
3661 // clicked.
3662 if (!IsSelected(item))
3663 {
3664 DoSelectItem(item, true, false);
3665 }
3666
3667 wxTreeEvent
3668 nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, this, item);
3669 nevent.m_pointDrag = CalcScrolledPosition(pt);
3670 event.Skip(!GetEventHandler()->ProcessEvent(nevent));
3671
3672 // Consistent with MSW (for now), send the ITEM_MENU *after*
3673 // the RIGHT_CLICK event. TODO: This behavior may change.
3674 wxTreeEvent nevent2(wxEVT_COMMAND_TREE_ITEM_MENU, this, item);
3675 nevent2.m_pointDrag = CalcScrolledPosition(pt);
3676 GetEventHandler()->ProcessEvent(nevent2);
3677 }
3678 else if ( event.MiddleDown() )
3679 {
3680 wxTreeEvent
3681 nevent(wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, this, item);
3682 nevent.m_pointDrag = CalcScrolledPosition(pt);
3683 event.Skip(!GetEventHandler()->ProcessEvent(nevent));
3684 }
3685 else if ( event.LeftUp() )
3686 {
3687 if (flags & wxTREE_HITTEST_ONITEMSTATEICON)
3688 {
3689 wxTreeEvent
3690 nevent(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, this, item);
3691 GetEventHandler()->ProcessEvent(nevent);
3692 }
3693
3694 // this facilitates multiple-item drag-and-drop
3695
3696 if ( /* item && */ HasFlag(wxTR_MULTIPLE))
3697 {
3698 wxArrayTreeItemIds selections;
3699 size_t count = GetSelections(selections);
3700
3701 if (count > 1 &&
3702 !event.CmdDown() &&
3703 !event.ShiftDown())
3704 {
3705 DoSelectItem(item, true, false);
3706 }
3707 }
3708
3709 if ( m_lastOnSame )
3710 {
3711 if ( (item == m_current) &&
3712 (flags & wxTREE_HITTEST_ONITEMLABEL) &&
3713 HasFlag(wxTR_EDIT_LABELS) )
3714 {
3715 if ( m_renameTimer )
3716 {
3717 if ( m_renameTimer->IsRunning() )
3718 m_renameTimer->Stop();
3719 }
3720 else
3721 {
3722 m_renameTimer = new wxTreeRenameTimer( this );
3723 }
3724
3725 m_renameTimer->Start( wxTreeRenameTimer::DELAY, true );
3726 }
3727
3728 m_lastOnSame = false;
3729 }
3730 }
3731 else // !RightDown() && !MiddleDown() && !LeftUp()
3732 {
3733 // ==> LeftDown() || LeftDClick()
3734 if ( event.LeftDown() )
3735 {
3736 m_lastOnSame = item == m_current;
3737 }
3738
3739 if ( flags & wxTREE_HITTEST_ONITEMBUTTON )
3740 {
3741 // only toggle the item for a single click, double click on
3742 // the button doesn't do anything (it toggles the item twice)
3743 if ( event.LeftDown() )
3744 {
3745 Toggle( item );
3746 }
3747
3748 // don't select the item if the button was clicked
3749 return;
3750 }
3751
3752
3753 // clear the previously selected items, if the
3754 // user clicked outside of the present selection.
3755 // otherwise, perform the deselection on mouse-up.
3756 // this allows multiple drag and drop to work.
3757 // but if Cmd is down, toggle selection of the clicked item
3758 if (!IsSelected(item) || event.CmdDown())
3759 {
3760 // how should the selection work for this event?
3761 bool is_multiple, extended_select, unselect_others;
3762 EventFlagsToSelType(GetWindowStyleFlag(),
3763 event.ShiftDown(),
3764 event.CmdDown(),
3765 is_multiple,
3766 extended_select,
3767 unselect_others);
3768
3769 DoSelectItem(item, unselect_others, extended_select);
3770 }
3771
3772
3773 // For some reason, Windows isn't recognizing a left double-click,
3774 // so we need to simulate it here. Allow 200 milliseconds for now.
3775 if ( event.LeftDClick() )
3776 {
3777 // double clicking should not start editing the item label
3778 if ( m_renameTimer )
3779 m_renameTimer->Stop();
3780
3781 m_lastOnSame = false;
3782
3783 // send activate event first
3784 wxTreeEvent
3785 nevent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, item);
3786 nevent.m_pointDrag = CalcScrolledPosition(pt);
3787 if ( !GetEventHandler()->ProcessEvent( nevent ) )
3788 {
3789 // if the user code didn't process the activate event,
3790 // handle it ourselves by toggling the item when it is
3791 // double clicked
3792 if ( item->HasPlus() )
3793 {
3794 Toggle(item);
3795 }
3796 }
3797 }
3798 }
3799 }
3800 }
3801
3802 void wxGenericTreeCtrl::OnInternalIdle()
3803 {
3804 wxWindow::OnInternalIdle();
3805
3806 // Check if we need to select the root item
3807 // because nothing else has been selected.
3808 // Delaying it means that we can invoke event handlers
3809 // as required, when a first item is selected.
3810 if (!HasFlag(wxTR_MULTIPLE) && !GetSelection().IsOk())
3811 {
3812 if (m_select_me)
3813 SelectItem(m_select_me);
3814 else if (GetRootItem().IsOk())
3815 SelectItem(GetRootItem());
3816 }
3817
3818 // after all changes have been done to the tree control,
3819 // actually redraw the tree when everything is over
3820 if (m_dirty)
3821 DoDirtyProcessing();
3822 }
3823
3824 void
3825 wxGenericTreeCtrl::CalculateLevel(wxGenericTreeItem *item,
3826 wxDC &dc,
3827 int level,
3828 int &y )
3829 {
3830 int x = level*m_indent;
3831 if (!HasFlag(wxTR_HIDE_ROOT))
3832 {
3833 x += m_indent;
3834 }
3835 else if (level == 0)
3836 {
3837 // a hidden root is not evaluated, but its
3838 // children are always calculated
3839 goto Recurse;
3840 }
3841
3842 item->CalculateSize(this, dc);
3843
3844 // set its position
3845 item->SetX( x+m_spacing );
3846 item->SetY( y );
3847 y += GetLineHeight(item);
3848
3849 if ( !item->IsExpanded() )
3850 {
3851 // we don't need to calculate collapsed branches
3852 return;
3853 }
3854
3855 Recurse:
3856 wxArrayGenericTreeItems& children = item->GetChildren();
3857 size_t n, count = children.GetCount();
3858 ++level;
3859 for (n = 0; n < count; ++n )
3860 CalculateLevel( children[n], dc, level, y ); // recurse
3861 }
3862
3863 void wxGenericTreeCtrl::CalculatePositions()
3864 {
3865 if ( !m_anchor ) return;
3866
3867 wxClientDC dc(this);
3868 PrepareDC( dc );
3869
3870 dc.SetFont( m_normalFont );
3871
3872 dc.SetPen( m_dottedPen );
3873 //if(GetImageList() == NULL)
3874 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
3875
3876 int y = 2;
3877 CalculateLevel( m_anchor, dc, 0, y ); // start recursion
3878 }
3879
3880 void wxGenericTreeCtrl::Refresh(bool eraseBackground, const wxRect *rect)
3881 {
3882 if ( !IsFrozen() )
3883 wxTreeCtrlBase::Refresh(eraseBackground, rect);
3884 }
3885
3886 void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
3887 {
3888 if (m_dirty || IsFrozen() )
3889 return;
3890
3891 wxSize client = GetClientSize();
3892
3893 wxRect rect;
3894 CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
3895 rect.width = client.x;
3896 rect.height = client.y;
3897
3898 Refresh(true, &rect);
3899
3900 AdjustMyScrollbars();
3901 }
3902
3903 void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
3904 {
3905 if (m_dirty || IsFrozen() )
3906 return;
3907
3908 wxRect rect;
3909 CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
3910 rect.width = GetClientSize().x;
3911 rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
3912
3913 Refresh(true, &rect);
3914 }
3915
3916 void wxGenericTreeCtrl::RefreshSelected()
3917 {
3918 if (IsFrozen())
3919 return;
3920
3921 // TODO: this is awfully inefficient, we should keep the list of all
3922 // selected items internally, should be much faster
3923 if ( m_anchor )
3924 RefreshSelectedUnder(m_anchor);
3925 }
3926
3927 void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item)
3928 {
3929 if (IsFrozen())
3930 return;
3931
3932 if ( item->IsSelected() )
3933 RefreshLine(item);
3934
3935 const wxArrayGenericTreeItems& children = item->GetChildren();
3936 size_t count = children.GetCount();
3937 for ( size_t n = 0; n < count; n++ )
3938 {
3939 RefreshSelectedUnder(children[n]);
3940 }
3941 }
3942
3943 void wxGenericTreeCtrl::DoThaw()
3944 {
3945 wxTreeCtrlBase::DoThaw();
3946
3947 if ( m_dirty )
3948 DoDirtyProcessing();
3949 else
3950 Refresh();
3951 }
3952
3953 // ----------------------------------------------------------------------------
3954 // changing colours: we need to refresh the tree control
3955 // ----------------------------------------------------------------------------
3956
3957 bool wxGenericTreeCtrl::SetBackgroundColour(const wxColour& colour)
3958 {
3959 if ( !wxWindow::SetBackgroundColour(colour) )
3960 return false;
3961
3962 Refresh();
3963
3964 return true;
3965 }
3966
3967 bool wxGenericTreeCtrl::SetForegroundColour(const wxColour& colour)
3968 {
3969 if ( !wxWindow::SetForegroundColour(colour) )
3970 return false;
3971
3972 Refresh();
3973
3974 return true;
3975 }
3976
3977 // Process the tooltip event, to speed up event processing.
3978 // Doesn't actually get a tooltip.
3979 void wxGenericTreeCtrl::OnGetToolTip( wxTreeEvent &event )
3980 {
3981 event.Veto();
3982 }
3983
3984
3985 // NOTE: If using the wxListBox visual attributes works everywhere then this can
3986 // be removed, as well as the #else case below.
3987 #define _USE_VISATTR 0
3988
3989 //static
3990 wxVisualAttributes
3991 #if _USE_VISATTR
3992 wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
3993 #else
3994 wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
3995 #endif
3996 {
3997 #if _USE_VISATTR
3998 // Use the same color scheme as wxListBox
3999 return wxListBox::GetClassDefaultAttributes(variant);
4000 #else
4001 wxVisualAttributes attr;
4002 attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT);
4003 attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX);
4004 attr.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
4005 return attr;
4006 #endif
4007 }
4008
4009 void wxGenericTreeCtrl::DoDirtyProcessing()
4010 {
4011 if (IsFrozen())
4012 return;
4013
4014 m_dirty = false;
4015
4016 CalculatePositions();
4017 Refresh();
4018 AdjustMyScrollbars();
4019 }
4020
4021 wxSize wxGenericTreeCtrl::DoGetBestSize() const
4022 {
4023 // make sure all positions are calculated as normally this only done during
4024 // idle time but we need them for base class DoGetBestSize() to return the
4025 // correct result
4026 wxConstCast(this, wxGenericTreeCtrl)->CalculatePositions();
4027
4028 wxSize size = wxTreeCtrlBase::DoGetBestSize();
4029
4030 // there seems to be an implicit extra border around the items, although
4031 // I'm not really sure where does it come from -- but without this, the
4032 // scrollbars appear in a tree with default/best size
4033 size.IncBy(4, 4);
4034
4035 // and the border has to be rounded up to a multiple of PIXELS_PER_UNIT or
4036 // scrollbars still appear
4037 const wxSize& borderSize = GetWindowBorderSize();
4038
4039 int dx = (size.x - borderSize.x) % PIXELS_PER_UNIT;
4040 if ( dx )
4041 size.x += PIXELS_PER_UNIT - dx;
4042 int dy = (size.y - borderSize.y) % PIXELS_PER_UNIT;
4043 if ( dy )
4044 size.y += PIXELS_PER_UNIT - dy;
4045
4046 // we need to update the cache too as the base class cached its own value
4047 CacheBestSize(size);
4048
4049 return size;
4050 }
4051
4052 #endif // wxUSE_TREECTRL