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