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