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