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