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