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