]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectlg.cpp
Did the unmap/map trick for wxFrame::IsIconized().
[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 = wxSIMPLE_BORDER,
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 #if !defined(__WXMSW__) || defined(__WIN16__)
584 /*
585 * wxTreeCtrl has to be a real class or we have problems with
586 * the run-time information.
587 */
588
589 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl)
590 #endif
591
592 // -----------------------------------------------------------------------------
593 // construction/destruction
594 // -----------------------------------------------------------------------------
595
596 void wxGenericTreeCtrl::Init()
597 {
598 m_current =
599 m_key_current =
600 m_anchor = (wxGenericTreeItem *) NULL;
601 m_hasFocus = FALSE;
602 m_dirty = FALSE;
603
604 m_xScroll = 0;
605 m_yScroll = 0;
606 m_lineHeight = 10;
607 m_indent = 15;
608 m_spacing = 18;
609
610 m_hilightBrush = new wxBrush
611 (
612 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT),
613 wxSOLID
614 );
615
616 m_imageListNormal =
617 m_imageListState = (wxImageList *) NULL;
618
619 m_dragCount = 0;
620 m_isDragging = FALSE;
621 m_dropTarget =
622 m_oldSelection = (wxGenericTreeItem *)NULL;
623
624 m_renameTimer = new wxTreeRenameTimer( this );
625 m_lastOnSame = FALSE;
626
627 m_normalFont = wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT );
628 m_boldFont = wxFont( m_normalFont.GetPointSize(),
629 m_normalFont.GetFamily(),
630 m_normalFont.GetStyle(),
631 wxBOLD,
632 m_normalFont.GetUnderlined());
633 }
634
635 bool wxGenericTreeCtrl::Create(wxWindow *parent, wxWindowID id,
636 const wxPoint& pos, const wxSize& size,
637 long style,
638 const wxValidator &validator,
639 const wxString& name )
640 {
641 wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name );
642
643 #if wxUSE_VALIDATORS
644 SetValidator( validator );
645 #endif
646
647 SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
648 // m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86
649 m_dottedPen = wxPen( "grey", 0, 0 );
650
651 return TRUE;
652 }
653
654 wxGenericTreeCtrl::~wxGenericTreeCtrl()
655 {
656 wxDELETE( m_hilightBrush );
657
658 DeleteAllItems();
659
660 delete m_renameTimer;
661 }
662
663 // -----------------------------------------------------------------------------
664 // accessors
665 // -----------------------------------------------------------------------------
666
667 size_t wxGenericTreeCtrl::GetCount() const
668 {
669 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
670 }
671
672 void wxGenericTreeCtrl::SetIndent(unsigned int indent)
673 {
674 m_indent = indent;
675 m_dirty = TRUE;
676 }
677
678 void wxGenericTreeCtrl::SetSpacing(unsigned int spacing)
679 {
680 m_spacing = spacing;
681 m_dirty = TRUE;
682 }
683
684 size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
685 {
686 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
687
688 return ((wxGenericTreeItem*) item.m_pItem)->GetChildrenCount(recursively);
689 }
690
691 // -----------------------------------------------------------------------------
692 // functions to work with tree items
693 // -----------------------------------------------------------------------------
694
695 wxString wxGenericTreeCtrl::GetItemText(const wxTreeItemId& item) const
696 {
697 wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") );
698
699 return ((wxGenericTreeItem*) item.m_pItem)->GetText();
700 }
701
702 int wxGenericTreeCtrl::GetItemImage(const wxTreeItemId& item,
703 wxTreeItemIcon which) const
704 {
705 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
706
707 return ((wxGenericTreeItem*) item.m_pItem)->GetImage(which);
708 }
709
710 wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const
711 {
712 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
713
714 return ((wxGenericTreeItem*) item.m_pItem)->GetData();
715 }
716
717 void wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
718 {
719 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
720
721 wxClientDC dc(this);
722 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
723 pItem->SetText(text);
724 CalculateSize(pItem, dc);
725 RefreshLine(pItem);
726 }
727
728 void wxGenericTreeCtrl::SetItemImage(const wxTreeItemId& item,
729 int image,
730 wxTreeItemIcon which)
731 {
732 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
733
734 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
735 pItem->SetImage(image, which);
736
737 wxClientDC dc(this);
738 CalculateSize(pItem, dc);
739 RefreshLine(pItem);
740 }
741
742 void wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
743 {
744 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
745
746 ((wxGenericTreeItem*) item.m_pItem)->SetData(data);
747 }
748
749 void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
750 {
751 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
752
753 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
754 pItem->SetHasPlus(has);
755 RefreshLine(pItem);
756 }
757
758 void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
759 {
760 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
761
762 // avoid redrawing the tree if no real change
763 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
764 if ( pItem->IsBold() != bold )
765 {
766 pItem->SetBold(bold);
767 RefreshLine(pItem);
768 }
769 }
770
771 void wxGenericTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
772 const wxColour& col)
773 {
774 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
775
776 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
777 pItem->Attr().SetTextColour(col);
778 RefreshLine(pItem);
779 }
780
781 void wxGenericTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
782 const wxColour& col)
783 {
784 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
785
786 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
787 pItem->Attr().SetBackgroundColour(col);
788 RefreshLine(pItem);
789 }
790
791 void wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
792 {
793 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
794
795 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
796 pItem->Attr().SetFont(font);
797 RefreshLine(pItem);
798 }
799
800 // -----------------------------------------------------------------------------
801 // item status inquiries
802 // -----------------------------------------------------------------------------
803
804 bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const
805 {
806 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
807
808 // An item is only visible if it's not a descendant of a collapsed item
809 wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
810 wxGenericTreeItem* parent = pItem->GetParent();
811 while (parent)
812 {
813 if (!parent->IsExpanded())
814 return FALSE;
815 parent = parent->GetParent();
816 }
817
818 int startX, startY;
819 GetViewStart(& startX, & startY);
820
821 wxSize clientSize = GetClientSize();
822
823 wxRect rect;
824 if (!GetBoundingRect(item, rect))
825 return FALSE;
826 if (rect.GetWidth() == 0 || rect.GetHeight() == 0)
827 return FALSE;
828 if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y)
829 return FALSE;
830 if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x)
831 return FALSE;
832
833 return TRUE;
834 }
835
836 bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
837 {
838 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
839
840 return !((wxGenericTreeItem*) item.m_pItem)->GetChildren().IsEmpty();
841 }
842
843 bool wxGenericTreeCtrl::IsExpanded(const wxTreeItemId& item) const
844 {
845 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
846
847 return ((wxGenericTreeItem*) item.m_pItem)->IsExpanded();
848 }
849
850 bool wxGenericTreeCtrl::IsSelected(const wxTreeItemId& item) const
851 {
852 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
853
854 return ((wxGenericTreeItem*) item.m_pItem)->IsSelected();
855 }
856
857 bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const
858 {
859 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
860
861 return ((wxGenericTreeItem*) item.m_pItem)->IsBold();
862 }
863
864 // -----------------------------------------------------------------------------
865 // navigation
866 // -----------------------------------------------------------------------------
867
868 wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const
869 {
870 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
871
872 return ((wxGenericTreeItem*) item.m_pItem)->GetParent();
873 }
874
875 wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
876 {
877 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
878
879 cookie = 0;
880 return GetNextChild(item, cookie);
881 }
882
883 wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
884 {
885 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
886
887 wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
888 if ( (size_t)cookie < children.Count() )
889 {
890 return children.Item((size_t)cookie++);
891 }
892 else
893 {
894 // there are no more of them
895 return wxTreeItemId();
896 }
897 }
898
899 wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const
900 {
901 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
902
903 wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
904 return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
905 }
906
907 wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
908 {
909 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
910
911 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
912 wxGenericTreeItem *parent = i->GetParent();
913 if ( parent == NULL )
914 {
915 // root item doesn't have any siblings
916 return wxTreeItemId();
917 }
918
919 wxArrayGenericTreeItems& siblings = parent->GetChildren();
920 int index = siblings.Index(i);
921 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
922
923 size_t n = (size_t)(index + 1);
924 return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
925 }
926
927 wxTreeItemId wxGenericTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
928 {
929 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
930
931 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
932 wxGenericTreeItem *parent = i->GetParent();
933 if ( parent == NULL )
934 {
935 // root item doesn't have any siblings
936 return wxTreeItemId();
937 }
938
939 wxArrayGenericTreeItems& siblings = parent->GetChildren();
940 int index = siblings.Index(i);
941 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
942
943 return index == 0 ? wxTreeItemId()
944 : wxTreeItemId(siblings[(size_t)(index - 1)]);
945 }
946
947 // Only for internal use right now, but should probably be public
948 wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const
949 {
950 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
951
952 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
953
954 // First see if there are any children.
955 wxArrayGenericTreeItems& children = i->GetChildren();
956 if (children.GetCount() > 0)
957 {
958 return children.Item(0);
959 }
960 else
961 {
962 // Try a sibling of this or ancestor instead
963 wxTreeItemId p = item;
964 wxTreeItemId toFind;
965 do
966 {
967 toFind = GetNextSibling(p);
968 p = GetParent(p);
969 } while (p.IsOk() && !toFind.IsOk());
970 return toFind;
971 }
972 }
973
974 wxTreeItemId wxGenericTreeCtrl::GetPrev(const wxTreeItemId& item) const
975 {
976 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
977
978 wxFAIL_MSG(wxT("not implemented"));
979
980 return wxTreeItemId();
981 }
982
983 wxTreeItemId wxGenericTreeCtrl::GetFirstVisibleItem() const
984 {
985 wxTreeItemId id = GetRootItem();
986 if (!id.IsOk())
987 return id;
988
989 do
990 {
991 if (IsVisible(id))
992 return id;
993 id = GetNext(id);
994 } while (id.IsOk());
995
996 return wxTreeItemId();
997 }
998
999 wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
1000 {
1001 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1002
1003 wxTreeItemId id = item;
1004 while (id.IsOk())
1005 {
1006 id = GetNext(id);
1007
1008 if (id.IsOk() && IsVisible(id))
1009 return id;
1010 }
1011 return wxTreeItemId();
1012 }
1013
1014 wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
1015 {
1016 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1017
1018 wxFAIL_MSG(wxT("not implemented"));
1019
1020 return wxTreeItemId();
1021 }
1022
1023 // -----------------------------------------------------------------------------
1024 // operations
1025 // -----------------------------------------------------------------------------
1026
1027 wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
1028 size_t previous,
1029 const wxString& text,
1030 int image, int selImage,
1031 wxTreeItemData *data)
1032 {
1033 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1034 if ( !parent )
1035 {
1036 // should we give a warning here?
1037 return AddRoot(text, image, selImage, data);
1038 }
1039
1040 wxClientDC dc(this);
1041 wxGenericTreeItem *item =
1042 new wxGenericTreeItem( parent, text, dc, image, selImage, data );
1043
1044 if ( data != NULL )
1045 {
1046 data->m_pItem = (long) item;
1047 }
1048
1049 parent->Insert( item, previous );
1050
1051 m_dirty = TRUE;
1052
1053 return item;
1054 }
1055
1056 wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text,
1057 int image, int selImage,
1058 wxTreeItemData *data)
1059 {
1060 wxCHECK_MSG( !m_anchor, wxTreeItemId(), wxT("tree can have only one root") );
1061
1062 wxClientDC dc(this);
1063 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
1064 image, selImage, data);
1065 if ( data != NULL )
1066 {
1067 data->m_pItem = (long) m_anchor;
1068 }
1069
1070 if (!HasFlag(wxTR_MULTIPLE))
1071 {
1072 m_current = m_key_current = m_anchor;
1073 m_current->SetHilight( TRUE );
1074 }
1075
1076 m_dirty = TRUE;
1077
1078 return m_anchor;
1079 }
1080
1081 wxTreeItemId wxGenericTreeCtrl::PrependItem(const wxTreeItemId& parent,
1082 const wxString& text,
1083 int image, int selImage,
1084 wxTreeItemData *data)
1085 {
1086 return DoInsertItem(parent, 0u, text, image, selImage, data);
1087 }
1088
1089 wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId,
1090 const wxTreeItemId& idPrevious,
1091 const wxString& text,
1092 int image, int selImage,
1093 wxTreeItemData *data)
1094 {
1095 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1096 if ( !parent )
1097 {
1098 // should we give a warning here?
1099 return AddRoot(text, image, selImage, data);
1100 }
1101
1102 int index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem);
1103 wxASSERT_MSG( index != wxNOT_FOUND,
1104 wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") );
1105
1106 return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
1107 }
1108
1109 wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId,
1110 size_t before,
1111 const wxString& text,
1112 int image, int selImage,
1113 wxTreeItemData *data)
1114 {
1115 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1116 if ( !parent )
1117 {
1118 // should we give a warning here?
1119 return AddRoot(text, image, selImage, data);
1120 }
1121
1122 return DoInsertItem(parentId, before, text, image, selImage, data);
1123 }
1124
1125 wxTreeItemId wxGenericTreeCtrl::AppendItem(const wxTreeItemId& parentId,
1126 const wxString& text,
1127 int image, int selImage,
1128 wxTreeItemData *data)
1129 {
1130 wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
1131 if ( !parent )
1132 {
1133 // should we give a warning here?
1134 return AddRoot(text, image, selImage, data);
1135 }
1136
1137 return DoInsertItem( parent, parent->GetChildren().Count(), text,
1138 image, selImage, data);
1139 }
1140
1141 void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
1142 {
1143 wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
1144 event.m_item = (long) item;
1145 event.SetEventObject( this );
1146 ProcessEvent( event );
1147 }
1148
1149 void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
1150 {
1151 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1152 item->DeleteChildren(this);
1153
1154 m_dirty = TRUE;
1155 }
1156
1157 void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId)
1158 {
1159 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1160
1161 // don't stay with invalid m_key_current or we will crash in the next call
1162 // to OnChar()
1163 bool changeKeyCurrent = FALSE;
1164 wxGenericTreeItem *itemKey = m_key_current;
1165 while ( itemKey && !changeKeyCurrent )
1166 {
1167 if ( itemKey == item )
1168 {
1169 // m_key_current is a descendant of the item being deleted
1170 changeKeyCurrent = TRUE;
1171 }
1172 else
1173 {
1174 itemKey = itemKey->GetParent();
1175 }
1176 }
1177
1178 wxGenericTreeItem *parent = item->GetParent();
1179 if ( parent )
1180 {
1181 parent->GetChildren().Remove( item ); // remove by value
1182 }
1183
1184 if ( changeKeyCurrent )
1185 {
1186 // may be NULL or not
1187 m_key_current = parent;
1188 }
1189
1190 item->DeleteChildren(this);
1191 SendDeleteEvent(item);
1192 delete item;
1193
1194 m_dirty = TRUE;
1195 }
1196
1197 void wxGenericTreeCtrl::DeleteAllItems()
1198 {
1199 if ( m_anchor )
1200 {
1201 m_anchor->DeleteChildren(this);
1202 delete m_anchor;
1203
1204 m_anchor = NULL;
1205
1206 m_dirty = TRUE;
1207 }
1208 }
1209
1210 void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
1211 {
1212 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1213
1214 wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") );
1215
1216 if ( !item->HasPlus() )
1217 return;
1218
1219 if ( item->IsExpanded() )
1220 return;
1221
1222 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
1223 event.m_item = (long) item;
1224 event.SetEventObject( this );
1225
1226 if ( ProcessEvent( event ) && !event.IsAllowed() )
1227 {
1228 // cancelled by program
1229 return;
1230 }
1231
1232 item->Expand();
1233 CalculatePositions();
1234
1235 RefreshSubtree(item);
1236
1237 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
1238 ProcessEvent( event );
1239 }
1240
1241 void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item)
1242 {
1243 Expand(item);
1244 if ( IsExpanded(item) )
1245 {
1246 long cookie;
1247 wxTreeItemId child = GetFirstChild(item, cookie);
1248 while ( child.IsOk() )
1249 {
1250 ExpandAll(child);
1251
1252 child = GetNextChild(item, cookie);
1253 }
1254 }
1255 }
1256
1257 void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
1258 {
1259 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1260
1261 if ( !item->IsExpanded() )
1262 return;
1263
1264 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
1265 event.m_item = (long) item;
1266 event.SetEventObject( this );
1267 if ( ProcessEvent( event ) && !event.IsAllowed() )
1268 {
1269 // cancelled by program
1270 return;
1271 }
1272
1273 item->Collapse();
1274
1275 wxArrayGenericTreeItems& children = item->GetChildren();
1276 size_t count = children.Count();
1277 for ( size_t n = 0; n < count; n++ )
1278 {
1279 Collapse(children[n]);
1280 }
1281
1282 CalculatePositions();
1283
1284 RefreshSubtree(item);
1285
1286 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
1287 ProcessEvent( event );
1288 }
1289
1290 void wxGenericTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
1291 {
1292 Collapse(item);
1293 DeleteChildren(item);
1294 }
1295
1296 void wxGenericTreeCtrl::Toggle(const wxTreeItemId& itemId)
1297 {
1298 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1299
1300 if (item->IsExpanded())
1301 Collapse(itemId);
1302 else
1303 Expand(itemId);
1304 }
1305
1306 void wxGenericTreeCtrl::Unselect()
1307 {
1308 if (m_current)
1309 {
1310 m_current->SetHilight( FALSE );
1311 RefreshLine( m_current );
1312 }
1313 }
1314
1315 void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
1316 {
1317 if (item->IsSelected())
1318 {
1319 item->SetHilight(FALSE);
1320 RefreshLine(item);
1321 }
1322
1323 if (item->HasChildren())
1324 {
1325 wxArrayGenericTreeItems& children = item->GetChildren();
1326 size_t count = children.Count();
1327 for ( size_t n = 0; n < count; ++n )
1328 {
1329 UnselectAllChildren(children[n]);
1330 }
1331 }
1332 }
1333
1334 void wxGenericTreeCtrl::UnselectAll()
1335 {
1336 UnselectAllChildren((wxGenericTreeItem*) GetRootItem().m_pItem);
1337 }
1338
1339 // Recursive function !
1340 // To stop we must have crt_item<last_item
1341 // Algorithm :
1342 // Tag all next children, when no more children,
1343 // Move to parent (not to tag)
1344 // Keep going... if we found last_item, we stop.
1345 bool wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1346 {
1347 wxGenericTreeItem *parent = crt_item->GetParent();
1348
1349 if (parent == NULL) // This is root item
1350 return TagAllChildrenUntilLast(crt_item, last_item, select);
1351
1352 wxArrayGenericTreeItems& children = parent->GetChildren();
1353 int index = children.Index(crt_item);
1354 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
1355
1356 size_t count = children.Count();
1357 for (size_t n=(size_t)(index+1); n<count; ++n)
1358 {
1359 if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
1360 }
1361
1362 return TagNextChildren(parent, last_item, select);
1363 }
1364
1365 bool wxGenericTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
1366 {
1367 crt_item->SetHilight(select);
1368 RefreshLine(crt_item);
1369
1370 if (crt_item==last_item)
1371 return TRUE;
1372
1373 if (crt_item->HasChildren())
1374 {
1375 wxArrayGenericTreeItems& children = crt_item->GetChildren();
1376 size_t count = children.Count();
1377 for ( size_t n = 0; n < count; ++n )
1378 {
1379 if (TagAllChildrenUntilLast(children[n], last_item, select))
1380 return TRUE;
1381 }
1382 }
1383
1384 return FALSE;
1385 }
1386
1387 void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *item2)
1388 {
1389 // item2 is not necessary after item1
1390 wxGenericTreeItem *first=NULL, *last=NULL;
1391
1392 // choice first' and 'last' between item1 and item2
1393 if (item1->GetY()<item2->GetY())
1394 {
1395 first=item1;
1396 last=item2;
1397 }
1398 else
1399 {
1400 first=item2;
1401 last=item1;
1402 }
1403
1404 bool select = m_current->IsSelected();
1405
1406 if ( TagAllChildrenUntilLast(first,last,select) )
1407 return;
1408
1409 TagNextChildren(first,last,select);
1410 }
1411
1412 void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId,
1413 bool unselect_others,
1414 bool extended_select)
1415 {
1416 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
1417
1418 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
1419 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1420
1421 //wxCHECK_RET( ( (!unselect_others) && is_single),
1422 // wxT("this is a single selection tree") );
1423
1424 // to keep going anyhow !!!
1425 if (is_single)
1426 {
1427 if (item->IsSelected())
1428 return; // nothing to do
1429 unselect_others = TRUE;
1430 extended_select = FALSE;
1431 }
1432 else if ( unselect_others && item->IsSelected() )
1433 {
1434 // selection change if there is more than one item currently selected
1435 wxArrayTreeItemIds selected_items;
1436 if ( GetSelections(selected_items) == 1 )
1437 return;
1438 }
1439
1440 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
1441 event.m_item = (long) item;
1442 event.m_itemOld = (long) m_current;
1443 event.SetEventObject( this );
1444 // TODO : Here we don't send any selection mode yet !
1445
1446 if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
1447 return;
1448
1449 // ctrl press
1450 if (unselect_others)
1451 {
1452 if (is_single) Unselect(); // to speed up thing
1453 else UnselectAll();
1454 }
1455
1456 // shift press
1457 if (extended_select)
1458 {
1459 if ( !m_current )
1460 {
1461 m_current =
1462 m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem;
1463 }
1464
1465 // don't change the mark (m_current)
1466 SelectItemRange(m_current, item);
1467 }
1468 else
1469 {
1470 bool select=TRUE; // the default
1471
1472 // Check if we need to toggle hilight (ctrl mode)
1473 if (!unselect_others)
1474 select=!item->IsSelected();
1475
1476 m_current = m_key_current = item;
1477 m_current->SetHilight(select);
1478 RefreshLine( m_current );
1479 }
1480
1481 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
1482 GetEventHandler()->ProcessEvent( event );
1483 }
1484
1485 void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
1486 wxArrayTreeItemIds &array) const
1487 {
1488 if ( item->IsSelected() )
1489 array.Add(wxTreeItemId(item));
1490
1491 if ( item->HasChildren() )
1492 {
1493 wxArrayGenericTreeItems& children = item->GetChildren();
1494 size_t count = children.GetCount();
1495 for ( size_t n = 0; n < count; ++n )
1496 FillArray(children[n], array);
1497 }
1498 }
1499
1500 size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
1501 {
1502 array.Empty();
1503 wxTreeItemId idRoot = GetRootItem();
1504 if ( idRoot.IsOk() )
1505 {
1506 FillArray((wxGenericTreeItem*) idRoot.m_pItem, array);
1507 }
1508 //else: the tree is empty, so no selections
1509
1510 return array.Count();
1511 }
1512
1513 void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item)
1514 {
1515 if (!item.IsOk()) return;
1516
1517 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
1518
1519 // first expand all parent branches
1520 wxGenericTreeItem *parent = gitem->GetParent();
1521 while ( parent )
1522 {
1523 Expand(parent);
1524 parent = parent->GetParent();
1525 }
1526
1527 //if (parent) CalculatePositions();
1528
1529 ScrollTo(item);
1530 }
1531
1532 void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item)
1533 {
1534 if (!item.IsOk()) return;
1535
1536 // We have to call this here because the label in
1537 // question might just have been added and no screen
1538 // update taken place.
1539 if (m_dirty) wxYield();
1540
1541 wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
1542
1543 // now scroll to the item
1544 int item_y = gitem->GetY();
1545
1546 int start_x = 0;
1547 int start_y = 0;
1548 ViewStart( &start_x, &start_y );
1549 start_y *= PIXELS_PER_UNIT;
1550
1551 int client_h = 0;
1552 int client_w = 0;
1553 GetClientSize( &client_w, &client_h );
1554
1555 if (item_y < start_y+3)
1556 {
1557 // going down
1558 int x = 0;
1559 int y = 0;
1560 m_anchor->GetSize( x, y, this );
1561 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1562 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1563 int x_pos = GetScrollPos( wxHORIZONTAL );
1564 // Item should appear at top
1565 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, item_y/PIXELS_PER_UNIT );
1566 }
1567 else if (item_y+GetLineHeight(gitem) > start_y+client_h)
1568 {
1569 // going up
1570 int x = 0;
1571 int y = 0;
1572 m_anchor->GetSize( x, y, this );
1573 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1574 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1575 item_y += PIXELS_PER_UNIT+2;
1576 int x_pos = GetScrollPos( wxHORIZONTAL );
1577 // Item should appear at bottom
1578 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 );
1579 }
1580 }
1581
1582 // FIXME: tree sorting functions are not reentrant and not MT-safe!
1583 static wxGenericTreeCtrl *s_treeBeingSorted = NULL;
1584
1585 static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
1586 wxGenericTreeItem **item2)
1587 {
1588 wxCHECK_MSG( s_treeBeingSorted, 0, wxT("bug in wxGenericTreeCtrl::SortChildren()") );
1589
1590 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
1591 }
1592
1593 int wxGenericTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
1594 const wxTreeItemId& item2)
1595 {
1596 return wxStrcmp(GetItemText(item1), GetItemText(item2));
1597 }
1598
1599 void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId)
1600 {
1601 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
1602
1603 wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
1604
1605 wxCHECK_RET( !s_treeBeingSorted,
1606 wxT("wxGenericTreeCtrl::SortChildren is not reentrant") );
1607
1608 wxArrayGenericTreeItems& children = item->GetChildren();
1609 if ( children.Count() > 1 )
1610 {
1611 s_treeBeingSorted = this;
1612 children.Sort(tree_ctrl_compare_func);
1613 s_treeBeingSorted = NULL;
1614
1615 m_dirty = TRUE;
1616 }
1617 //else: don't make the tree dirty as nothing changed
1618 }
1619
1620 wxImageList *wxGenericTreeCtrl::GetImageList() const
1621 {
1622 return m_imageListNormal;
1623 }
1624
1625 wxImageList *wxGenericTreeCtrl::GetStateImageList() const
1626 {
1627 return m_imageListState;
1628 }
1629
1630 void wxGenericTreeCtrl::SetImageList(wxImageList *imageList)
1631 {
1632 m_imageListNormal = imageList;
1633
1634 if ( !m_imageListNormal )
1635 return;
1636
1637 // Calculate a m_lineHeight value from the image sizes.
1638 // May be toggle off. Then wxGenericTreeCtrl will spread when
1639 // necessary (which might look ugly).
1640 wxClientDC dc(this);
1641 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1642 int width = 0, height = 0,
1643 n = m_imageListNormal->GetImageCount();
1644
1645 for (int i = 0; i < n ; i++)
1646 {
1647 m_imageListNormal->GetSize(i, width, height);
1648 if (height > m_lineHeight) m_lineHeight = height;
1649 }
1650
1651 if (m_lineHeight < 40)
1652 m_lineHeight += 2; // at least 2 pixels
1653 else
1654 m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
1655 }
1656
1657 void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList)
1658 {
1659 m_imageListState = imageList;
1660 }
1661
1662 // -----------------------------------------------------------------------------
1663 // helpers
1664 // -----------------------------------------------------------------------------
1665
1666 void wxGenericTreeCtrl::AdjustMyScrollbars()
1667 {
1668 if (m_anchor)
1669 {
1670 int x = 0;
1671 int y = 0;
1672 m_anchor->GetSize( x, y, this );
1673 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1674 x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
1675 int x_pos = GetScrollPos( wxHORIZONTAL );
1676 int y_pos = GetScrollPos( wxVERTICAL );
1677 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, y_pos );
1678 }
1679 else
1680 {
1681 SetScrollbars( 0, 0, 0, 0 );
1682 }
1683 }
1684
1685 int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
1686 {
1687 if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
1688 return item->GetHeight();
1689 else
1690 return m_lineHeight;
1691 }
1692
1693 void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1694 {
1695 wxTreeItemAttr *attr = item->GetAttributes();
1696 if ( attr && attr->HasFont() )
1697 dc.SetFont(attr->GetFont());
1698 else if (item->IsBold())
1699 dc.SetFont(m_boldFont);
1700
1701 long text_w = 0;
1702 long text_h = 0;
1703 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
1704
1705 int image_h = 0;
1706 int image_w = 0;
1707 int image = item->GetCurrentImage();
1708 if ( image != NO_IMAGE )
1709 {
1710 if ( m_imageListNormal )
1711 {
1712 m_imageListNormal->GetSize( image, image_w, image_h );
1713 image_w += 4;
1714 }
1715 else
1716 {
1717 image = NO_IMAGE;
1718 }
1719 }
1720
1721 int total_h = GetLineHeight(item);
1722
1723 if (item->IsSelected())
1724 dc.SetBrush(*m_hilightBrush);
1725 else
1726 {
1727 wxColour colBg;
1728 if ( attr && attr->HasBackgroundColour() )
1729 colBg = attr->GetBackgroundColour();
1730 else
1731 colBg = m_backgroundColour;
1732 dc.SetBrush(wxBrush(colBg, wxSOLID));
1733 }
1734
1735 dc.DrawRectangle( item->GetX()-2, item->GetY(), item->GetWidth()+2, total_h );
1736
1737 if ( image != NO_IMAGE )
1738 {
1739 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h );
1740 m_imageListNormal->Draw( image, dc,
1741 item->GetX(),
1742 item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0),
1743 wxIMAGELIST_DRAW_TRANSPARENT );
1744 dc.DestroyClippingRegion();
1745 }
1746
1747 dc.SetBackgroundMode(wxTRANSPARENT);
1748 int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0;
1749 dc.DrawText( item->GetText(),
1750 (wxCoord)(image_w + item->GetX()),
1751 (wxCoord)(item->GetY() + extraH));
1752
1753 // restore normal font
1754 dc.SetFont( m_normalFont );
1755 }
1756
1757 // Now y stands for the top of the item, whereas it used to stand for middle !
1758 void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
1759 {
1760 int horizX = level*m_indent;
1761
1762 item->SetX( horizX+m_indent+m_spacing );
1763 item->SetY( y );
1764
1765 int oldY = y;
1766 y+=GetLineHeight(item)/2;
1767
1768 item->SetCross( horizX+m_indent, y );
1769
1770 int exposed_x = dc.LogicalToDeviceX( 0 );
1771 int exposed_y = dc.LogicalToDeviceY( item->GetY() );
1772
1773 bool drawLines = ((GetWindowStyle() & wxTR_NO_LINES) == 0);
1774
1775 if (IsExposed( exposed_x, exposed_y, 10000, GetLineHeight(item) )) // 10000 = very much
1776 {
1777 int startX = horizX;
1778 int endX = horizX + (m_indent-5);
1779
1780 // if (!item->HasChildren()) endX += (m_indent+5);
1781 if (!item->HasChildren()) endX += 20;
1782
1783 if (drawLines)
1784 dc.DrawLine( startX, y, endX, y );
1785
1786 if (item->HasPlus())
1787 {
1788 if (drawLines)
1789 dc.DrawLine( horizX+(m_indent+5), y, horizX+(m_indent+15), y );
1790 dc.SetPen( *wxGREY_PEN );
1791 dc.SetBrush( *wxWHITE_BRUSH );
1792 dc.DrawRectangle( horizX+(m_indent-5), y-4, 11, 9 );
1793
1794 dc.SetPen( *wxBLACK_PEN );
1795 dc.DrawLine( horizX+(m_indent-2), y, horizX+(m_indent+3), y );
1796 if (!item->IsExpanded())
1797 dc.DrawLine( horizX+m_indent, y-2, horizX+m_indent, y+3 );
1798
1799 dc.SetPen( m_dottedPen );
1800 }
1801
1802 wxPen *pen = wxTRANSPARENT_PEN;
1803 wxColour colText;
1804
1805 if ( item->IsSelected() )
1806 {
1807 colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
1808
1809 if ( m_hasFocus )
1810 pen = wxBLACK_PEN;
1811
1812 }
1813 else
1814 {
1815 wxTreeItemAttr *attr = item->GetAttributes();
1816 if ( attr && attr->HasTextColour() )
1817 colText = attr->GetTextColour();
1818 else
1819 colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOWTEXT );
1820 }
1821
1822 // prepare to draw
1823 dc.SetTextForeground(colText);
1824 dc.SetPen(*pen);
1825
1826 // draw
1827 PaintItem(item, dc);
1828
1829 // restore DC objects
1830 dc.SetBrush( *wxWHITE_BRUSH );
1831 dc.SetPen( m_dottedPen );
1832 dc.SetTextForeground( *wxBLACK );
1833 }
1834
1835 y = oldY+GetLineHeight(item);
1836
1837 if (item->IsExpanded())
1838 {
1839 oldY+=GetLineHeight(item)/2;
1840 int semiOldY=0;
1841
1842 wxArrayGenericTreeItems& children = item->GetChildren();
1843 size_t n, count = children.Count();
1844 for ( n = 0; n < count; ++n )
1845 {
1846 semiOldY=y;
1847 PaintLevel( children[n], dc, level+1, y );
1848 }
1849
1850 // it may happen that the item is expanded but has no items (when you
1851 // delete all its children for example) - don't draw the vertical line
1852 // in this case
1853 if (count > 0)
1854 {
1855 semiOldY+=GetLineHeight(children[--n])/2;
1856 if (drawLines)
1857 dc.DrawLine( horizX+m_indent, oldY+5, horizX+m_indent, semiOldY );
1858 }
1859 }
1860 }
1861
1862 void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item)
1863 {
1864 if ( item )
1865 {
1866 if ( item->HasPlus() )
1867 {
1868 // it's a folder, indicate it by a border
1869 DrawBorder(item);
1870 }
1871 else
1872 {
1873 // draw a line under the drop target because the item will be
1874 // dropped there
1875 DrawLine(item, TRUE /* below */);
1876 }
1877
1878 SetCursor(wxCURSOR_BULLSEYE);
1879 }
1880 else
1881 {
1882 // can't drop here
1883 SetCursor(wxCURSOR_NO_ENTRY);
1884 }
1885 }
1886
1887 void wxGenericTreeCtrl::DrawBorder(const wxTreeItemId &item)
1888 {
1889 wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") );
1890
1891 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1892
1893 wxClientDC dc(this);
1894 PrepareDC( dc );
1895 dc.SetLogicalFunction(wxINVERT);
1896 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1897
1898 int w = i->GetWidth() + 2;
1899 int h = GetLineHeight(i) + 2;
1900
1901 dc.DrawRectangle( i->GetX() - 1, i->GetY() - 1, w, h);
1902 }
1903
1904 void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below)
1905 {
1906 wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") );
1907
1908 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
1909
1910 wxClientDC dc(this);
1911 PrepareDC( dc );
1912 dc.SetLogicalFunction(wxINVERT);
1913
1914 int x = i->GetX(),
1915 y = i->GetY();
1916 if ( below )
1917 {
1918 y += GetLineHeight(i) - 1;
1919 }
1920
1921 dc.DrawLine( x, y, x + i->GetWidth(), y);
1922 }
1923
1924 // -----------------------------------------------------------------------------
1925 // wxWindows callbacks
1926 // -----------------------------------------------------------------------------
1927
1928 void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
1929 {
1930 wxPaintDC dc(this);
1931 PrepareDC( dc );
1932
1933 if ( !m_anchor)
1934 return;
1935
1936 dc.SetFont( m_normalFont );
1937 dc.SetPen( m_dottedPen );
1938
1939 // this is now done dynamically
1940 //if(GetImageList() == NULL)
1941 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
1942
1943 int y = 2;
1944 PaintLevel( m_anchor, dc, 0, y );
1945 }
1946
1947 void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1948 {
1949 m_hasFocus = TRUE;
1950
1951 if (m_current) RefreshLine( m_current );
1952 }
1953
1954 void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
1955 {
1956 m_hasFocus = FALSE;
1957
1958 if (m_current) RefreshLine( m_current );
1959 }
1960
1961 void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
1962 {
1963 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1964 te.m_code = (int)event.KeyCode();
1965 te.SetEventObject( this );
1966 GetEventHandler()->ProcessEvent( te );
1967
1968 if ( (m_current == 0) || (m_key_current == 0) )
1969 {
1970 event.Skip();
1971 return;
1972 }
1973
1974 // how should the selection work for this event?
1975 bool is_multiple, extended_select, unselect_others;
1976 EventFlagsToSelType(GetWindowStyleFlag(),
1977 event.ShiftDown(),
1978 event.ControlDown(),
1979 is_multiple, extended_select, unselect_others);
1980
1981 // + : Expand
1982 // - : Collaspe
1983 // * : Expand all/Collapse all
1984 // ' ' | return : activate
1985 // up : go up (not last children!)
1986 // down : go down
1987 // left : go to parent
1988 // right : open if parent and go next
1989 // home : go to root
1990 // end : go to last item without opening parents
1991 switch (event.KeyCode())
1992 {
1993 case '+':
1994 case WXK_ADD:
1995 if (m_current->HasPlus() && !IsExpanded(m_current))
1996 {
1997 Expand(m_current);
1998 }
1999 break;
2000
2001 case '*':
2002 case WXK_MULTIPLY:
2003 if ( !IsExpanded(m_current) )
2004 {
2005 // expand all
2006 ExpandAll(m_current);
2007 break;
2008 }
2009 //else: fall through to Collapse() it
2010
2011 case '-':
2012 case WXK_SUBTRACT:
2013 if (IsExpanded(m_current))
2014 {
2015 Collapse(m_current);
2016 }
2017 break;
2018
2019 case ' ':
2020 case WXK_RETURN:
2021 {
2022 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
2023 event.m_item = (long) m_current;
2024 event.m_code = 0;
2025 event.SetEventObject( this );
2026 GetEventHandler()->ProcessEvent( event );
2027 }
2028 break;
2029
2030 // up goes to the previous sibling or to the last of its children if
2031 // it's expanded
2032 case WXK_UP:
2033 {
2034 wxTreeItemId prev = GetPrevSibling( m_key_current );
2035 if (!prev)
2036 {
2037 prev = GetParent( m_key_current );
2038 if (prev)
2039 {
2040 long cockie = 0;
2041 wxTreeItemId current = m_key_current;
2042 if (current == GetFirstChild( prev, cockie ))
2043 {
2044 // otherwise we return to where we came from
2045 SelectItem( prev, unselect_others, extended_select );
2046 m_key_current= (wxGenericTreeItem*) prev.m_pItem;
2047 EnsureVisible( prev );
2048 break;
2049 }
2050 }
2051 }
2052 if (prev)
2053 {
2054 while ( IsExpanded(prev) && HasChildren(prev) )
2055 {
2056 wxTreeItemId child = GetLastChild(prev);
2057 if ( child )
2058 {
2059 prev = child;
2060 }
2061 }
2062
2063 SelectItem( prev, unselect_others, extended_select );
2064 m_key_current=(wxGenericTreeItem*) prev.m_pItem;
2065 EnsureVisible( prev );
2066 }
2067 }
2068 break;
2069
2070 // left arrow goes to the parent
2071 case WXK_LEFT:
2072 {
2073 wxTreeItemId prev = GetParent( m_current );
2074 if (prev)
2075 {
2076 EnsureVisible( prev );
2077 SelectItem( prev, unselect_others, extended_select );
2078 }
2079 }
2080 break;
2081
2082 case WXK_RIGHT:
2083 // this works the same as the down arrow except that we also expand the
2084 // item if it wasn't expanded yet
2085 Expand(m_current);
2086 // fall through
2087
2088 case WXK_DOWN:
2089 {
2090 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
2091 {
2092 long cookie = 0;
2093 wxTreeItemId child = GetFirstChild( m_key_current, cookie );
2094 SelectItem( child, unselect_others, extended_select );
2095 m_key_current=(wxGenericTreeItem*) child.m_pItem;
2096 EnsureVisible( child );
2097 }
2098 else
2099 {
2100 wxTreeItemId next = GetNextSibling( m_key_current );
2101 if (!next)
2102 {
2103 wxTreeItemId current = m_key_current;
2104 while (current && !next)
2105 {
2106 current = GetParent( current );
2107 if (current) next = GetNextSibling( current );
2108 }
2109 }
2110 if (next)
2111 {
2112 SelectItem( next, unselect_others, extended_select );
2113 m_key_current=(wxGenericTreeItem*) next.m_pItem;
2114 EnsureVisible( next );
2115 }
2116 }
2117 }
2118 break;
2119
2120 // <End> selects the last visible tree item
2121 case WXK_END:
2122 {
2123 wxTreeItemId last = GetRootItem();
2124
2125 while ( last.IsOk() && IsExpanded(last) )
2126 {
2127 wxTreeItemId lastChild = GetLastChild(last);
2128
2129 // it may happen if the item was expanded but then all of
2130 // its children have been deleted - so IsExpanded() returned
2131 // TRUE, but GetLastChild() returned invalid item
2132 if ( !lastChild )
2133 break;
2134
2135 last = lastChild;
2136 }
2137
2138 if ( last.IsOk() )
2139 {
2140 EnsureVisible( last );
2141 SelectItem( last, unselect_others, extended_select );
2142 }
2143 }
2144 break;
2145
2146 // <Home> selects the root item
2147 case WXK_HOME:
2148 {
2149 wxTreeItemId prev = GetRootItem();
2150 if (prev)
2151 {
2152 EnsureVisible( prev );
2153 SelectItem( prev, unselect_others, extended_select );
2154 }
2155 }
2156 break;
2157
2158 default:
2159 event.Skip();
2160 }
2161 }
2162
2163 wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags)
2164 {
2165 // We have to call this here because the label in
2166 // question might just have been added and no screen
2167 // update taken place.
2168 if (m_dirty) wxYield();
2169
2170 wxClientDC dc(this);
2171 PrepareDC(dc);
2172 wxCoord x = dc.DeviceToLogicalX( point.x );
2173 wxCoord y = dc.DeviceToLogicalY( point.y );
2174 int w, h;
2175 GetSize(&w, &h);
2176
2177 flags=0;
2178 if (point.x<0) flags|=wxTREE_HITTEST_TOLEFT;
2179 if (point.x>w) flags|=wxTREE_HITTEST_TORIGHT;
2180 if (point.y<0) flags|=wxTREE_HITTEST_ABOVE;
2181 if (point.y>h) flags|=wxTREE_HITTEST_BELOW;
2182
2183 return m_anchor->HitTest( wxPoint(x, y), this, flags);
2184 }
2185
2186 // get the bounding rectangle of the item (or of its label only)
2187 bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
2188 wxRect& rect,
2189 bool textOnly) const
2190 {
2191 wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") );
2192
2193 wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
2194
2195 int startX, startY;
2196 GetViewStart(& startX, & startY);
2197
2198 rect.x = i->GetX() - startX*PIXELS_PER_UNIT;
2199 rect.y = i->GetY() - startY*PIXELS_PER_UNIT;
2200 rect.width = i->GetWidth();
2201 //rect.height = i->GetHeight();
2202 rect.height = GetLineHeight(i);
2203
2204 return TRUE;
2205 }
2206
2207 /* **** */
2208
2209 void wxGenericTreeCtrl::Edit( const wxTreeItemId& item )
2210 {
2211 if (!item.IsOk()) return;
2212
2213 m_currentEdit = (wxGenericTreeItem*) item.m_pItem;
2214
2215 wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() );
2216 te.m_item = (long) m_currentEdit;
2217 te.SetEventObject( this );
2218 GetEventHandler()->ProcessEvent( te );
2219
2220 if (!te.IsAllowed()) return;
2221
2222 // We have to call this here because the label in
2223 // question might just have been added and no screen
2224 // update taken place.
2225 if (m_dirty) wxYield();
2226
2227 wxString s = m_currentEdit->GetText();
2228 int x = m_currentEdit->GetX();
2229 int y = m_currentEdit->GetY();
2230 int w = m_currentEdit->GetWidth();
2231 int h = m_currentEdit->GetHeight();
2232
2233 int image_h = 0;
2234 int image_w = 0;
2235
2236 int image = m_currentEdit->GetCurrentImage();
2237 if ( image != NO_IMAGE )
2238 {
2239 if ( m_imageListNormal )
2240 {
2241 m_imageListNormal->GetSize( image, image_w, image_h );
2242 image_w += 4;
2243 }
2244 else
2245 {
2246 wxFAIL_MSG(_T("you must create an image list to use images!"));
2247 }
2248 }
2249 x += image_w;
2250 w -= image_w + 4; // I don't know why +4 is needed
2251
2252 wxClientDC dc(this);
2253 PrepareDC( dc );
2254 x = dc.LogicalToDeviceX( x );
2255 y = dc.LogicalToDeviceY( y );
2256
2257 wxTreeTextCtrl *text = new wxTreeTextCtrl(
2258 this, -1, &m_renameAccept, &m_renameRes, this, s, wxPoint(x-4,y-4), wxSize(w+11,h+8) );
2259 text->SetFocus();
2260 }
2261
2262 void wxGenericTreeCtrl::OnRenameTimer()
2263 {
2264 Edit( m_current );
2265 }
2266
2267 void wxGenericTreeCtrl::OnRenameAccept()
2268 {
2269 wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
2270 le.m_item = (long) m_currentEdit;
2271 le.SetEventObject( this );
2272 le.m_label = m_renameRes;
2273 GetEventHandler()->ProcessEvent( le );
2274
2275 if (!le.IsAllowed()) return;
2276
2277 SetItemText( m_currentEdit, m_renameRes );
2278 }
2279
2280 void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
2281 {
2282 if ( !m_anchor ) return;
2283
2284 // we process left mouse up event (enables in-place edit), right down
2285 // (pass to the user code), left dbl click (activate item) and
2286 // dragging/moving events for items drag-and-drop
2287 if ( !(event.LeftDown() ||
2288 event.LeftUp() ||
2289 event.RightDown() ||
2290 event.LeftDClick() ||
2291 event.Dragging() ||
2292 ((event.Moving() || event.RightUp()) && m_isDragging)) )
2293 {
2294 event.Skip();
2295
2296 return;
2297 }
2298
2299 wxClientDC dc(this);
2300 PrepareDC(dc);
2301 wxCoord x = dc.DeviceToLogicalX( event.GetX() );
2302 wxCoord y = dc.DeviceToLogicalY( event.GetY() );
2303
2304 int flags = 0;
2305 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), this, flags);
2306
2307 if ( event.Dragging() && !m_isDragging )
2308 {
2309 if (m_dragCount == 0)
2310 m_dragStart = wxPoint(x,y);
2311
2312 m_dragCount++;
2313
2314 if (m_dragCount != 3)
2315 {
2316 // wait until user drags a bit further...
2317 return;
2318 }
2319
2320 wxEventType command = event.RightIsDown()
2321 ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
2322 : wxEVT_COMMAND_TREE_BEGIN_DRAG;
2323
2324 wxTreeEvent nevent( command, GetId() );
2325 nevent.m_item = (long) m_current;
2326 nevent.SetEventObject(this);
2327
2328 // by default the dragging is not supported, the user code must
2329 // explicitly allow the event for it to take place
2330 nevent.Veto();
2331
2332 if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() )
2333 {
2334 // we're going to drag this item
2335 m_isDragging = TRUE;
2336
2337 // remember the old cursor because we will change it while
2338 // dragging
2339 m_oldCursor = m_cursor;
2340
2341 // in a single selection control, hide the selection temporarily
2342 if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
2343 {
2344 m_oldSelection = (wxGenericTreeItem*) GetSelection().m_pItem;
2345
2346 if ( m_oldSelection )
2347 {
2348 m_oldSelection->SetHilight(FALSE);
2349 RefreshLine(m_oldSelection);
2350 }
2351 }
2352
2353 CaptureMouse();
2354 }
2355 }
2356 else if ( event.Moving() )
2357 {
2358 if ( item != m_dropTarget )
2359 {
2360 // unhighlight the previous drop target
2361 DrawDropEffect(m_dropTarget);
2362
2363 m_dropTarget = item;
2364
2365 // highlight the current drop target if any
2366 DrawDropEffect(m_dropTarget);
2367
2368 wxYield();
2369 }
2370 }
2371 else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
2372 {
2373 // erase the highlighting
2374 DrawDropEffect(m_dropTarget);
2375
2376 // generate the drag end event
2377 wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId());
2378
2379 event.m_item = (long) item;
2380 event.m_pointDrag = wxPoint(x, y);
2381 event.SetEventObject(this);
2382
2383 (void)GetEventHandler()->ProcessEvent(event);
2384
2385 m_isDragging = FALSE;
2386 m_dropTarget = (wxGenericTreeItem *)NULL;
2387
2388 if ( m_oldSelection )
2389 {
2390 m_oldSelection->SetHilight(TRUE);
2391 RefreshLine(m_oldSelection);
2392 m_oldSelection = (wxGenericTreeItem *)NULL;
2393 }
2394
2395 ReleaseMouse();
2396
2397 SetCursor(m_oldCursor);
2398
2399 wxYield();
2400 }
2401 else
2402 {
2403 // here we process only the messages which happen on tree items
2404
2405 m_dragCount = 0;
2406
2407 if (item == NULL) return; /* we hit the blank area */
2408
2409 if ( event.RightDown() )
2410 {
2411 wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId());
2412 nevent.m_item = (long) item;
2413 nevent.m_code = 0;
2414 nevent.SetEventObject(this);
2415 GetEventHandler()->ProcessEvent(nevent);
2416 }
2417 else if ( event.LeftUp() )
2418 {
2419 if ( m_lastOnSame )
2420 {
2421 if ( (item == m_current) &&
2422 (flags & wxTREE_HITTEST_ONITEMLABEL) &&
2423 HasFlag(wxTR_EDIT_LABELS) )
2424 {
2425 if ( m_renameTimer->IsRunning() )
2426 m_renameTimer->Stop();
2427
2428 m_renameTimer->Start( 100, TRUE );
2429 }
2430
2431 m_lastOnSame = FALSE;
2432 }
2433 }
2434 else
2435 {
2436 if ( event.LeftDown() )
2437 {
2438 m_lastOnSame = item == m_current;
2439 }
2440
2441 // how should the selection work for this event?
2442 bool is_multiple, extended_select, unselect_others;
2443 EventFlagsToSelType(GetWindowStyleFlag(),
2444 event.ShiftDown(),
2445 event.ControlDown(),
2446 is_multiple, extended_select, unselect_others);
2447
2448 if ( (flags & wxTREE_HITTEST_ONITEMBUTTON) && event.LeftDown() )
2449 {
2450 Toggle( item );
2451 if ( is_multiple )
2452 return;
2453 }
2454
2455 SelectItem(item, unselect_others, extended_select);
2456
2457 if ( event.LeftDClick() )
2458 {
2459 // double clicking should not start editing the item label
2460 m_renameTimer->Stop();
2461 m_lastOnSame = FALSE;
2462
2463 wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
2464 nevent.m_item = (long) item;
2465 nevent.m_code = 0;
2466 nevent.SetEventObject( this );
2467 GetEventHandler()->ProcessEvent( nevent );
2468 }
2469 }
2470 }
2471 }
2472
2473 void wxGenericTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
2474 {
2475 /* after all changes have been done to the tree control,
2476 * we actually redraw the tree when everything is over */
2477
2478 if (!m_dirty)
2479 return;
2480
2481 m_dirty = FALSE;
2482
2483 CalculatePositions();
2484 Refresh();
2485 AdjustMyScrollbars();
2486 }
2487
2488 void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc )
2489 {
2490 wxCoord text_w = 0;
2491 wxCoord text_h = 0;
2492
2493 if (item->IsBold())
2494 dc.SetFont(m_boldFont);
2495
2496 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
2497 text_h+=2;
2498
2499 // restore normal font
2500 dc.SetFont( m_normalFont );
2501
2502 int image_h = 0;
2503 int image_w = 0;
2504 int image = item->GetCurrentImage();
2505 if ( image != NO_IMAGE )
2506 {
2507 if ( m_imageListNormal )
2508 {
2509 m_imageListNormal->GetSize( image, image_w, image_h );
2510 image_w += 4;
2511 }
2512 }
2513
2514 int total_h = (image_h > text_h) ? image_h : text_h;
2515
2516 if (total_h < 40)
2517 total_h += 2; // at least 2 pixels
2518 else
2519 total_h += total_h/10; // otherwise 10% extra spacing
2520
2521 item->SetHeight(total_h);
2522 if (total_h>m_lineHeight)
2523 m_lineHeight=total_h;
2524
2525 item->SetWidth(image_w+text_w+2);
2526 }
2527
2528 // -----------------------------------------------------------------------------
2529 // for developper : y is now the top of the level
2530 // not the middle of it !
2531 void wxGenericTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
2532 {
2533 int horizX = level*m_indent;
2534
2535 CalculateSize( item, dc );
2536
2537 // set its position
2538 item->SetX( horizX+m_indent+m_spacing );
2539 item->SetY( y );
2540 y+=GetLineHeight(item);
2541
2542 if ( !item->IsExpanded() )
2543 {
2544 // we dont need to calculate collapsed branches
2545 return;
2546 }
2547
2548 wxArrayGenericTreeItems& children = item->GetChildren();
2549 size_t n, count = children.Count();
2550 for (n = 0; n < count; ++n )
2551 CalculateLevel( children[n], dc, level+1, y ); // recurse
2552 }
2553
2554 void wxGenericTreeCtrl::CalculatePositions()
2555 {
2556 if ( !m_anchor ) return;
2557
2558 wxClientDC dc(this);
2559 PrepareDC( dc );
2560
2561 dc.SetFont( m_normalFont );
2562
2563 dc.SetPen( m_dottedPen );
2564 //if(GetImageList() == NULL)
2565 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
2566
2567 int y = 2;
2568 CalculateLevel( m_anchor, dc, 0, y ); // start recursion
2569 }
2570
2571 void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
2572 {
2573 if (m_dirty) return;
2574
2575 wxClientDC dc(this);
2576 PrepareDC(dc);
2577
2578 int cw = 0;
2579 int ch = 0;
2580 GetClientSize( &cw, &ch );
2581
2582 wxRect rect;
2583 rect.x = dc.LogicalToDeviceX( 0 );
2584 rect.width = cw;
2585 rect.y = dc.LogicalToDeviceY( item->GetY() );
2586 rect.height = ch;
2587
2588 Refresh( TRUE, &rect );
2589
2590 AdjustMyScrollbars();
2591 }
2592
2593 void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
2594 {
2595 if (m_dirty) return;
2596
2597 wxClientDC dc(this);
2598 PrepareDC( dc );
2599
2600 int cw = 0;
2601 int ch = 0;
2602 GetClientSize( &cw, &ch );
2603
2604 wxRect rect;
2605 rect.x = dc.LogicalToDeviceX( 0 );
2606 rect.y = dc.LogicalToDeviceY( item->GetY() );
2607 rect.width = cw;
2608 rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
2609
2610 Refresh( TRUE, &rect );
2611 }
2612