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