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