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