]>
Commit | Line | Data |
---|---|---|
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 | #include "wx/treectrl.h" | |
25 | #include "wx/settings.h" | |
26 | #include "wx/log.h" | |
27 | #include "wx/intl.h" | |
28 | #include "wx/dynarray.h" | |
29 | #include "wx/dcclient.h" | |
30 | ||
31 | // ----------------------------------------------------------------------------- | |
32 | // array types | |
33 | // ----------------------------------------------------------------------------- | |
34 | ||
35 | WX_DEFINE_ARRAY(wxGenericTreeItem *, wxArrayTreeItems); | |
36 | ||
37 | // ----------------------------------------------------------------------------- | |
38 | // private classes | |
39 | // ----------------------------------------------------------------------------- | |
40 | ||
41 | // a tree item | |
42 | class WXDLLEXPORT wxGenericTreeItem | |
43 | { | |
44 | public: | |
45 | // ctors & dtor | |
46 | wxGenericTreeItem() { m_data = NULL; } | |
47 | wxGenericTreeItem( wxGenericTreeItem *parent, | |
48 | const wxString& text, | |
49 | wxDC& dc, | |
50 | int image, int selImage, | |
51 | wxTreeItemData *data ); | |
52 | ||
53 | inline ~wxGenericTreeItem(); | |
54 | ||
55 | // trivial accessors | |
56 | wxArrayTreeItems& GetChildren() { return m_children; } | |
57 | ||
58 | const wxString& GetText() const { return m_text; } | |
59 | int GetImage() const { return m_image; } | |
60 | int GetSelectedImage() const { return m_selImage; } | |
61 | wxTreeItemData *GetData() const { return m_data; } | |
62 | ||
63 | void SetText( const wxString &text, wxDC& dc ); | |
64 | void SetImage(int image) { m_image = image; } | |
65 | void SetSelectedImage(int image) { m_selImage = image; } | |
66 | void SetData(wxTreeItemData *data) { m_data = data; } | |
67 | ||
68 | void SetHasPlus(bool has = TRUE) { m_hasPlus = has; } | |
69 | ||
70 | int GetX() const { return m_x; } | |
71 | int GetY() const { return m_y; } | |
72 | ||
73 | void SetHeight(int h) { m_height = h; } | |
74 | ||
75 | void SetX(int x) { m_x = x; } | |
76 | void SetY(int y) { m_y = y; } | |
77 | ||
78 | wxGenericTreeItem *GetParent() const { return m_parent; } | |
79 | ||
80 | // operations | |
81 | void Reset(); | |
82 | ||
83 | // get count of all children (and grand children and ...) of this item | |
84 | size_t GetTotalNumberOfChildren() const; | |
85 | ||
86 | void Insert(wxGenericTreeItem *child, size_t index) | |
87 | { m_children.Insert(child, index); } | |
88 | ||
89 | void SetCross( int x, int y ); | |
90 | void GetSize( int &x, int &y ); | |
91 | ||
92 | // return the item at given position (or NULL if no item), onButton is TRUE | |
93 | // if the point belongs to the item's button, otherwise it lies on the | |
94 | // button's label | |
95 | wxGenericTreeItem *HitTest( const wxPoint& point, bool &onButton ); | |
96 | ||
97 | void Expand() { m_isCollapsed = FALSE; } | |
98 | void Collapse() { m_isCollapsed = TRUE; } | |
99 | ||
100 | void SetHilight( bool set = TRUE ) { m_hasHilight = set; } | |
101 | ||
102 | // status inquiries | |
103 | bool HasChildren() const { return !m_children.IsEmpty(); } | |
104 | bool HasHilight() const { return m_hasHilight; } | |
105 | bool IsExpanded() const { return !m_isCollapsed; } | |
106 | bool HasPlus() const { return m_hasPlus || HasChildren(); } | |
107 | ||
108 | private: | |
109 | wxString m_text; | |
110 | ||
111 | int m_image, | |
112 | m_selImage; | |
113 | ||
114 | wxTreeItemData *m_data; | |
115 | ||
116 | // @@ probably should use bitfields to save size | |
117 | bool m_isCollapsed, | |
118 | m_hasHilight, // same as focused | |
119 | m_hasPlus; // used for item which doesn't have | |
120 | // children but still has a [+] button | |
121 | ||
122 | int m_x, m_y; | |
123 | long m_height, m_width; | |
124 | int m_xCross, m_yCross; | |
125 | int m_level; | |
126 | wxArrayTreeItems m_children; | |
127 | wxGenericTreeItem *m_parent; | |
128 | }; | |
129 | ||
130 | // ============================================================================= | |
131 | // implementation | |
132 | // ============================================================================= | |
133 | ||
134 | // ----------------------------------------------------------------------------- | |
135 | // wxTreeEvent | |
136 | // ----------------------------------------------------------------------------- | |
137 | ||
138 | IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxCommandEvent) | |
139 | ||
140 | wxTreeEvent::wxTreeEvent( wxEventType commandType, int id ) | |
141 | : wxCommandEvent( commandType, id ) | |
142 | { | |
143 | m_code = 0; | |
144 | m_itemOld = (wxGenericTreeItem *)NULL; | |
145 | } | |
146 | ||
147 | // ----------------------------------------------------------------------------- | |
148 | // wxGenericTreeItem | |
149 | // ----------------------------------------------------------------------------- | |
150 | ||
151 | wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent, | |
152 | const wxString& text, | |
153 | wxDC& dc, | |
154 | int image, int selImage, | |
155 | wxTreeItemData *data) | |
156 | : m_text(text) | |
157 | { | |
158 | m_image = image; | |
159 | m_selImage = selImage; | |
160 | m_data = data; | |
161 | m_x = m_y = 0; | |
162 | m_xCross = m_yCross = 0; | |
163 | ||
164 | m_level = 0; | |
165 | ||
166 | m_isCollapsed = TRUE; | |
167 | m_hasHilight = FALSE; | |
168 | ||
169 | m_parent = parent; | |
170 | ||
171 | dc.GetTextExtent( m_text, &m_width, &m_height ); | |
172 | } | |
173 | ||
174 | wxGenericTreeItem::~wxGenericTreeItem() | |
175 | { | |
176 | delete m_data; | |
177 | ||
178 | size_t count = m_children.Count(); | |
179 | for ( size_t n = 0; n < count; n++ ) | |
180 | delete m_children[n]; | |
181 | } | |
182 | ||
183 | void wxGenericTreeItem::SetText( const wxString &text, wxDC& dc ) | |
184 | { | |
185 | m_text = text; | |
186 | ||
187 | dc.GetTextExtent( m_text, &m_width, &m_height ); | |
188 | } | |
189 | ||
190 | void wxGenericTreeItem::Reset() | |
191 | { | |
192 | m_text.Empty(); | |
193 | m_image = | |
194 | m_selImage = -1; | |
195 | m_data = NULL; | |
196 | m_x = m_y = | |
197 | m_height = m_width = 0; | |
198 | m_xCross = | |
199 | m_yCross = 0; | |
200 | ||
201 | m_level = 0; | |
202 | ||
203 | m_children.Empty(); | |
204 | m_isCollapsed = TRUE; | |
205 | ||
206 | m_parent = (wxGenericTreeItem *)NULL; | |
207 | } | |
208 | ||
209 | size_t wxGenericTreeItem::GetTotalNumberOfChildren() const | |
210 | { | |
211 | size_t count = m_children.Count(); | |
212 | size_t total = count; | |
213 | for ( size_t n = 0; n < count; n++ ) | |
214 | { | |
215 | total += m_children[n]->GetTotalNumberOfChildren(); | |
216 | } | |
217 | ||
218 | return total; | |
219 | } | |
220 | ||
221 | void wxGenericTreeItem::SetCross( int x, int y ) | |
222 | { | |
223 | m_xCross = x; | |
224 | m_yCross = y; | |
225 | } | |
226 | ||
227 | void wxGenericTreeItem::GetSize( int &x, int &y ) | |
228 | { | |
229 | // FIXME what does this all mean?? | |
230 | if ( y < m_y + 10 ) y = m_y +10; | |
231 | int width = m_x + m_width; | |
232 | if (width > x) x = width; | |
233 | ||
234 | size_t count = m_children.Count(); | |
235 | for ( size_t n = 0; n < count; n++ ) | |
236 | { | |
237 | m_children[n]->GetSize( x, y ); | |
238 | } | |
239 | } | |
240 | ||
241 | wxGenericTreeItem *wxGenericTreeItem::HitTest( const wxPoint& point, | |
242 | bool &onButton ) | |
243 | { | |
244 | if ((point.y > m_y) && (point.y < m_y + m_height)) | |
245 | { | |
246 | // FIXME why +5? | |
247 | if ((point.x > m_xCross-5) && (point.x < m_xCross+5) && | |
248 | (point.y > m_yCross-5) && (point.y < m_yCross+5) && | |
249 | (IsExpanded() || HasPlus())) | |
250 | { | |
251 | onButton = TRUE; | |
252 | return this; | |
253 | } | |
254 | ||
255 | if ((point.x > m_x) && (point.x < m_x+m_width)) | |
256 | { | |
257 | onButton = FALSE; | |
258 | return this; | |
259 | } | |
260 | } | |
261 | else | |
262 | { | |
263 | if (!m_isCollapsed) | |
264 | { | |
265 | size_t count = m_children.Count(); | |
266 | for ( size_t n = 0; n < count; n++ ) | |
267 | { | |
268 | wxGenericTreeItem *res = m_children[n]->HitTest( point, onButton ); | |
269 | if ( res != NULL ) | |
270 | return res; | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | return NULL; | |
276 | } | |
277 | ||
278 | // ----------------------------------------------------------------------------- | |
279 | // wxTreeCtrl implementation | |
280 | // ----------------------------------------------------------------------------- | |
281 | ||
282 | IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxScrolledWindow) | |
283 | ||
284 | BEGIN_EVENT_TABLE(wxTreeCtrl,wxScrolledWindow) | |
285 | EVT_PAINT (wxTreeCtrl::OnPaint) | |
286 | EVT_MOUSE_EVENTS (wxTreeCtrl::OnMouse) | |
287 | EVT_CHAR (wxTreeCtrl::OnChar) | |
288 | EVT_SET_FOCUS (wxTreeCtrl::OnSetFocus) | |
289 | EVT_KILL_FOCUS (wxTreeCtrl::OnKillFocus) | |
290 | END_EVENT_TABLE() | |
291 | ||
292 | // ----------------------------------------------------------------------------- | |
293 | // construction/destruction | |
294 | // ----------------------------------------------------------------------------- | |
295 | void wxTreeCtrl::Init() | |
296 | { | |
297 | m_current = | |
298 | m_anchor = (wxGenericTreeItem *) NULL; | |
299 | m_hasFocus = FALSE; | |
300 | ||
301 | m_xScroll = 0; | |
302 | m_yScroll = 0; | |
303 | m_lineHeight = 10; | |
304 | m_indent = 15; | |
305 | ||
306 | m_hilightBrush = new wxBrush | |
307 | ( | |
308 | wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT), | |
309 | wxSOLID | |
310 | ); | |
311 | ||
312 | m_imageListNormal = | |
313 | m_imageListState = (wxImageList *) NULL; | |
314 | } | |
315 | ||
316 | bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id, | |
317 | const wxPoint& pos, const wxSize& size, | |
318 | long style, const wxString& name ) | |
319 | { | |
320 | Init(); | |
321 | ||
322 | wxScrolledWindow::Create( parent, id, pos, size, style, name ); | |
323 | ||
324 | SetBackgroundColour( *wxWHITE ); | |
325 | m_dottedPen = wxPen( *wxBLACK, 0, 0 ); | |
326 | ||
327 | return TRUE; | |
328 | } | |
329 | ||
330 | wxTreeCtrl::~wxTreeCtrl() | |
331 | { | |
332 | wxDELETE( m_hilightBrush ); | |
333 | wxDELETE( m_anchor ); | |
334 | } | |
335 | ||
336 | // ----------------------------------------------------------------------------- | |
337 | // accessors | |
338 | // ----------------------------------------------------------------------------- | |
339 | ||
340 | size_t wxTreeCtrl::GetCount() const | |
341 | { | |
342 | return m_anchor == NULL ? 0u : m_anchor->GetTotalNumberOfChildren(); | |
343 | } | |
344 | ||
345 | void wxTreeCtrl::SetIndent(unsigned int indent) | |
346 | { | |
347 | m_indent = indent; | |
348 | Refresh(); | |
349 | } | |
350 | ||
351 | // ----------------------------------------------------------------------------- | |
352 | // functions to work with tree items | |
353 | // ----------------------------------------------------------------------------- | |
354 | ||
355 | wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const | |
356 | { | |
357 | return item.m_pItem->GetText(); | |
358 | } | |
359 | ||
360 | int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const | |
361 | { | |
362 | return item.m_pItem->GetImage(); | |
363 | } | |
364 | ||
365 | int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const | |
366 | { | |
367 | return item.m_pItem->GetSelectedImage(); | |
368 | } | |
369 | ||
370 | wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const | |
371 | { | |
372 | return item.m_pItem->GetData(); | |
373 | } | |
374 | ||
375 | void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) | |
376 | { | |
377 | wxClientDC dc(this); | |
378 | item.m_pItem->SetText(text, dc); | |
379 | } | |
380 | ||
381 | void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image) | |
382 | { | |
383 | item.m_pItem->SetImage(image); | |
384 | } | |
385 | ||
386 | void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image) | |
387 | { | |
388 | item.m_pItem->SetSelectedImage(image); | |
389 | } | |
390 | ||
391 | void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) | |
392 | { | |
393 | item.m_pItem->SetData(data); | |
394 | } | |
395 | ||
396 | void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) | |
397 | { | |
398 | item.m_pItem->SetHasPlus(has); | |
399 | } | |
400 | ||
401 | // ----------------------------------------------------------------------------- | |
402 | // item status inquiries | |
403 | // ----------------------------------------------------------------------------- | |
404 | ||
405 | bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const | |
406 | { | |
407 | wxFAIL_MSG("not implemented"); | |
408 | ||
409 | return TRUE; | |
410 | } | |
411 | ||
412 | bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const | |
413 | { | |
414 | return !item.m_pItem->GetChildren().IsEmpty(); | |
415 | } | |
416 | ||
417 | bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const | |
418 | { | |
419 | return item.m_pItem->IsExpanded(); | |
420 | } | |
421 | ||
422 | bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const | |
423 | { | |
424 | return item.m_pItem->HasHilight(); | |
425 | } | |
426 | ||
427 | // ----------------------------------------------------------------------------- | |
428 | // navigation | |
429 | // ----------------------------------------------------------------------------- | |
430 | ||
431 | wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const | |
432 | { | |
433 | wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" ); | |
434 | ||
435 | return item.m_pItem->GetParent(); | |
436 | } | |
437 | ||
438 | wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const | |
439 | { | |
440 | wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" ); | |
441 | ||
442 | cookie = 0; | |
443 | return GetNextChild(item, cookie); | |
444 | } | |
445 | ||
446 | wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const | |
447 | { | |
448 | wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" ); | |
449 | ||
450 | return item.m_pItem->GetChildren().Item(cookie++); | |
451 | } | |
452 | ||
453 | wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const | |
454 | { | |
455 | wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" ); | |
456 | ||
457 | wxGenericTreeItem *i = item.m_pItem; | |
458 | wxGenericTreeItem *parent = i->GetParent(); | |
459 | if ( parent == NULL ) | |
460 | { | |
461 | // root item doesn't have any siblings | |
462 | return NULL; | |
463 | } | |
464 | ||
465 | wxArrayTreeItems& siblings = parent->GetChildren(); | |
466 | int index = siblings.Index(i); | |
467 | wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent? | |
468 | ||
469 | size_t n = (size_t)(index + 1); | |
470 | return n == siblings.Count() ? (wxGenericTreeItem*)NULL : siblings[n]; | |
471 | } | |
472 | ||
473 | wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const | |
474 | { | |
475 | wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" ); | |
476 | ||
477 | wxGenericTreeItem *i = item.m_pItem; | |
478 | wxGenericTreeItem *parent = i->GetParent(); | |
479 | if ( parent == NULL ) | |
480 | { | |
481 | // root item doesn't have any siblings | |
482 | return NULL; | |
483 | } | |
484 | ||
485 | wxArrayTreeItems& siblings = parent->GetChildren(); | |
486 | int index = siblings.Index(i); | |
487 | wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent? | |
488 | ||
489 | return index == 0 ? (wxGenericTreeItem*)NULL : siblings[(size_t)(index - 1)]; | |
490 | } | |
491 | ||
492 | wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const | |
493 | { | |
494 | wxFAIL_MSG("not implemented"); | |
495 | ||
496 | return NULL; | |
497 | } | |
498 | ||
499 | wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const | |
500 | { | |
501 | wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" ); | |
502 | ||
503 | wxFAIL_MSG("not implemented"); | |
504 | ||
505 | return NULL; | |
506 | } | |
507 | ||
508 | wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const | |
509 | { | |
510 | wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" ); | |
511 | ||
512 | wxFAIL_MSG("not implemented"); | |
513 | ||
514 | return NULL; | |
515 | } | |
516 | ||
517 | // ----------------------------------------------------------------------------- | |
518 | // operations | |
519 | // ----------------------------------------------------------------------------- | |
520 | ||
521 | wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId, | |
522 | size_t previous, | |
523 | const wxString& text, | |
524 | int image, int selImage, | |
525 | wxTreeItemData *data) | |
526 | { | |
527 | wxGenericTreeItem *parent = parentId.m_pItem; | |
528 | if ( !parent ) | |
529 | { | |
530 | // should we give a warning here? | |
531 | return AddRoot(text, image, selImage, data); | |
532 | } | |
533 | ||
534 | wxClientDC dc(this); | |
535 | wxGenericTreeItem *item = new wxGenericTreeItem(parent, | |
536 | text, dc, | |
537 | image, selImage, | |
538 | data); | |
539 | ||
540 | if ( data != NULL ) | |
541 | { | |
542 | data->m_pItem = item; | |
543 | } | |
544 | ||
545 | parent->Insert( item, previous ); | |
546 | ||
547 | CalculatePositions(); | |
548 | ||
549 | int cw, ch; | |
550 | GetClientSize( &cw, &ch ); | |
551 | ||
552 | PrepareDC( dc ); | |
553 | ||
554 | wxRectangle rect; | |
555 | rect.x = dc.LogicalToDeviceX( 0 ); | |
556 | rect.y = 0; | |
557 | rect.width = 10000; // @@@ not very elegant... | |
558 | rect.height = ch; | |
559 | ||
560 | if ( previous != 0 ) | |
561 | { | |
562 | rect.y = dc.LogicalToDeviceY( parent->GetChildren().Item(previous)->GetY() ); | |
563 | } | |
564 | else // it's the 1st child | |
565 | { | |
566 | rect.y = dc.LogicalToDeviceY( parent->GetY() ); | |
567 | } | |
568 | ||
569 | AdjustMyScrollbars(); | |
570 | ||
571 | if ( rect.height > 0 ) | |
572 | Refresh( FALSE, &rect ); | |
573 | ||
574 | return item; | |
575 | } | |
576 | ||
577 | wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, | |
578 | int image, int selImage, | |
579 | wxTreeItemData *data) | |
580 | { | |
581 | wxCHECK_MSG( !m_anchor, NULL, "tree can have only one root" ); | |
582 | ||
583 | wxClientDC dc(this); | |
584 | m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc, | |
585 | image, selImage, data); | |
586 | if ( data != NULL ) | |
587 | { | |
588 | data->m_pItem = m_anchor; | |
589 | } | |
590 | ||
591 | AdjustMyScrollbars(); | |
592 | Refresh(); | |
593 | ||
594 | return m_anchor; | |
595 | } | |
596 | ||
597 | wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent, | |
598 | const wxString& text, | |
599 | int image, int selImage, | |
600 | wxTreeItemData *data) | |
601 | { | |
602 | return DoInsertItem(parent, 0u, text, image, selImage, data); | |
603 | } | |
604 | ||
605 | wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId, | |
606 | const wxTreeItemId& idPrevious, | |
607 | const wxString& text, | |
608 | int image, int selImage, | |
609 | wxTreeItemData *data) | |
610 | { | |
611 | wxGenericTreeItem *parent = parentId.m_pItem; | |
612 | if ( !parent ) | |
613 | { | |
614 | // should we give a warning here? | |
615 | return AddRoot(text, image, selImage, data); | |
616 | } | |
617 | ||
618 | int index = parent->GetChildren().Index(idPrevious.m_pItem); | |
619 | wxASSERT_MSG( index != NOT_FOUND, | |
620 | "previous item in wxTreeCtrl::InsertItem() is not a sibling" ); | |
621 | return DoInsertItem(parentId, (size_t)index, text, image, selImage, data); | |
622 | } | |
623 | ||
624 | wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId, | |
625 | const wxString& text, | |
626 | int image, int selImage, | |
627 | wxTreeItemData *data) | |
628 | { | |
629 | wxGenericTreeItem *parent = parentId.m_pItem; | |
630 | if ( !parent ) | |
631 | { | |
632 | // should we give a warning here? | |
633 | return AddRoot(text, image, selImage, data); | |
634 | } | |
635 | ||
636 | return DoInsertItem(parent, parent->GetChildren().Count(), text, | |
637 | image, selImage, data); | |
638 | } | |
639 | ||
640 | void wxTreeCtrl::Delete(const wxTreeItemId& itemId) | |
641 | { | |
642 | wxGenericTreeItem *item = itemId.m_pItem; | |
643 | ||
644 | delete item; | |
645 | ||
646 | Refresh(); | |
647 | } | |
648 | ||
649 | void wxTreeCtrl::DeleteAllItems() | |
650 | { | |
651 | if ( m_anchor ) | |
652 | { | |
653 | delete m_anchor; | |
654 | m_anchor = NULL; | |
655 | ||
656 | Refresh(); | |
657 | } | |
658 | } | |
659 | ||
660 | void wxTreeCtrl::Expand(const wxTreeItemId& itemId) | |
661 | { | |
662 | wxGenericTreeItem *item = itemId.m_pItem; | |
663 | ||
664 | if ( item->IsExpanded() ) | |
665 | return; | |
666 | ||
667 | wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() ); | |
668 | event.m_item = item; | |
669 | event.SetEventObject( this ); | |
670 | if ( ProcessEvent( event ) && event.m_code ) | |
671 | { | |
672 | // cancelled by program | |
673 | return; | |
674 | } | |
675 | ||
676 | item->Expand(); | |
677 | ||
678 | RefreshSubtree(item); | |
679 | ||
680 | event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED); | |
681 | ProcessEvent( event ); | |
682 | } | |
683 | ||
684 | void wxTreeCtrl::Collapse(const wxTreeItemId& itemId) | |
685 | { | |
686 | wxGenericTreeItem *item = itemId.m_pItem; | |
687 | ||
688 | if ( !item->IsExpanded() ) | |
689 | return; | |
690 | ||
691 | wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() ); | |
692 | event.m_item = item; | |
693 | event.SetEventObject( this ); | |
694 | if ( ProcessEvent( event ) && event.m_code ) | |
695 | { | |
696 | // cancelled by program | |
697 | return; | |
698 | } | |
699 | ||
700 | item->Collapse(); | |
701 | ||
702 | wxArrayTreeItems& children = item->GetChildren(); | |
703 | size_t count = children.Count(); | |
704 | for ( size_t n = 0; n < count; n++ ) | |
705 | { | |
706 | Collapse(children[n]); | |
707 | } | |
708 | ||
709 | CalculatePositions(); | |
710 | ||
711 | RefreshSubtree(item); | |
712 | ||
713 | event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED); | |
714 | ProcessEvent( event ); | |
715 | } | |
716 | ||
717 | void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item) | |
718 | { | |
719 | Collapse(item); | |
720 | Delete(item); | |
721 | } | |
722 | ||
723 | void wxTreeCtrl::Toggle(const wxTreeItemId& itemId) | |
724 | { | |
725 | wxGenericTreeItem *item = itemId.m_pItem; | |
726 | ||
727 | if ( item->IsExpanded() ) | |
728 | Collapse(itemId); | |
729 | else | |
730 | Expand(itemId); | |
731 | } | |
732 | ||
733 | void wxTreeCtrl::Unselect() | |
734 | { | |
735 | if ( m_current ) | |
736 | { | |
737 | m_current->SetHilight( FALSE ); | |
738 | RefreshLine( m_current ); | |
739 | } | |
740 | } | |
741 | ||
742 | void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId) | |
743 | { | |
744 | wxGenericTreeItem *item = itemId.m_pItem; | |
745 | ||
746 | if ( m_current != item ) | |
747 | { | |
748 | wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() ); | |
749 | event.m_item = item; | |
750 | event.m_itemOld = m_current; | |
751 | event.SetEventObject( this ); | |
752 | if ( ProcessEvent( event ) && event.WasVetoed() ) | |
753 | return; | |
754 | ||
755 | if ( m_current ) | |
756 | { | |
757 | m_current->SetHilight( FALSE ); | |
758 | RefreshLine( m_current ); | |
759 | } | |
760 | ||
761 | m_current = item; | |
762 | m_current->SetHilight( TRUE ); | |
763 | RefreshLine( m_current ); | |
764 | ||
765 | event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); | |
766 | ProcessEvent( event ); | |
767 | } | |
768 | } | |
769 | ||
770 | void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item) | |
771 | { | |
772 | wxFAIL_MSG("not implemented"); | |
773 | } | |
774 | ||
775 | void wxTreeCtrl::ScrollTo(const wxTreeItemId& item) | |
776 | { | |
777 | wxFAIL_MSG("not implemented"); | |
778 | } | |
779 | ||
780 | wxTextCtrl *wxTreeCtrl::EditLabel(const wxTreeItemId& item, | |
781 | wxClassInfo* textCtrlClass) | |
782 | { | |
783 | wxFAIL_MSG("not implemented"); | |
784 | ||
785 | return NULL; | |
786 | } | |
787 | ||
788 | wxTextCtrl *wxTreeCtrl::GetEditControl() const | |
789 | { | |
790 | wxFAIL_MSG("not implemented"); | |
791 | ||
792 | return NULL; | |
793 | } | |
794 | ||
795 | void wxTreeCtrl::EndEditLabel(const wxTreeItemId& item, bool discardChanges) | |
796 | { | |
797 | wxFAIL_MSG("not implemented"); | |
798 | } | |
799 | ||
800 | void wxTreeCtrl::SortChildren(const wxTreeItemId& item, | |
801 | wxTreeItemCmpFunc *cmpFunction) | |
802 | { | |
803 | wxFAIL_MSG("not implemented"); | |
804 | } | |
805 | ||
806 | // ----------------------------------------------------------------------------- | |
807 | // images are not currently supported, but we still provide stubs for these | |
808 | // functions | |
809 | // ----------------------------------------------------------------------------- | |
810 | wxImageList *wxTreeCtrl::GetImageList() const | |
811 | { | |
812 | return m_imageListNormal; | |
813 | } | |
814 | ||
815 | wxImageList *wxTreeCtrl::GetStateImageList() const | |
816 | { | |
817 | return m_imageListState; | |
818 | } | |
819 | ||
820 | void wxTreeCtrl::SetImageList(wxImageList *imageList) | |
821 | { | |
822 | m_imageListNormal = imageList; | |
823 | } | |
824 | ||
825 | void wxTreeCtrl::SetStateImageList(wxImageList *imageList) | |
826 | { | |
827 | m_imageListState = imageList; | |
828 | } | |
829 | ||
830 | // ----------------------------------------------------------------------------- | |
831 | // helpers | |
832 | // ----------------------------------------------------------------------------- | |
833 | void wxTreeCtrl::AdjustMyScrollbars() | |
834 | { | |
835 | if (m_anchor) | |
836 | { | |
837 | int x = 0; | |
838 | int y = 0; | |
839 | m_anchor->GetSize( x, y ); | |
840 | y += 2*m_lineHeight; | |
841 | int x_pos = GetScrollPos( wxHORIZONTAL ); | |
842 | int y_pos = GetScrollPos( wxVERTICAL ); | |
843 | SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos ); | |
844 | } | |
845 | else | |
846 | { | |
847 | SetScrollbars( 0, 0, 0, 0 ); | |
848 | } | |
849 | } | |
850 | ||
851 | void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y ) | |
852 | { | |
853 | int horizX = level*m_indent; | |
854 | ||
855 | item->SetX( horizX+33 ); | |
856 | item->SetY( y-m_lineHeight/3 ); | |
857 | item->SetHeight( m_lineHeight ); | |
858 | ||
859 | item->SetCross( horizX+15, y ); | |
860 | ||
861 | int oldY = y; | |
862 | ||
863 | int exposed_x = dc.LogicalToDeviceX( 0 ); | |
864 | int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 ); | |
865 | ||
866 | if (IsExposed( exposed_x, exposed_y, 1000, m_lineHeight+4 )) | |
867 | { | |
868 | int startX = horizX; | |
869 | int endX = horizX + 10; | |
870 | ||
871 | if (!item->HasChildren()) endX += 20; | |
872 | ||
873 | dc.DrawLine( startX, y, endX, y ); | |
874 | ||
875 | if (item->HasPlus()) | |
876 | { | |
877 | dc.DrawLine( horizX+20, y, horizX+30, y ); | |
878 | dc.SetPen( *wxGREY_PEN ); | |
879 | dc.DrawRectangle( horizX+10, y-4, 11, 9 ); | |
880 | dc.SetPen( *wxBLACK_PEN ); | |
881 | dc.DrawLine( horizX+13, y, horizX+18, y ); | |
882 | ||
883 | if (!item->IsExpanded()) | |
884 | dc.DrawLine( horizX+15, y-2, horizX+15, y+3 ); | |
885 | } | |
886 | ||
887 | if (item->HasHilight()) | |
888 | { | |
889 | dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) ); | |
890 | dc.SetBrush( *m_hilightBrush ); | |
891 | long tw, th; | |
892 | dc.GetTextExtent( item->GetText(), &tw, &th ); | |
893 | if (m_hasFocus) | |
894 | { | |
895 | dc.SetPen( *wxBLACK_PEN ); | |
896 | dc.DrawRectangle( item->GetX()-2, item->GetY()-2, tw+4, th+4 ); | |
897 | } | |
898 | else | |
899 | { | |
900 | dc.SetPen( *wxTRANSPARENT_PEN ); | |
901 | dc.DrawRectangle( item->GetX()-2, item->GetY()-2, tw+4, th+4 ); | |
902 | } | |
903 | dc.DrawText( item->GetText(), item->GetX(), item->GetY() ); | |
904 | ||
905 | dc.SetPen( *wxBLACK_PEN ); | |
906 | dc.SetTextForeground( *wxBLACK ); | |
907 | dc.SetBrush( *wxWHITE_BRUSH ); | |
908 | } | |
909 | else | |
910 | { | |
911 | dc.SetBrush( *wxWHITE_BRUSH ); | |
912 | dc.SetPen( *wxTRANSPARENT_PEN ); | |
913 | long tw, th; | |
914 | dc.GetTextExtent( item->GetText(), &tw, &th ); | |
915 | dc.DrawRectangle( item->GetX()-2, item->GetY()-2, tw+4, th+4 ); | |
916 | dc.DrawText( item->GetText(), item->GetX(), item->GetY() ); | |
917 | dc.SetPen( *wxBLACK_PEN ); | |
918 | } | |
919 | } | |
920 | ||
921 | if ( !item->IsExpanded() ) | |
922 | return; | |
923 | ||
924 | int semiOldY = y; | |
925 | ||
926 | wxArrayTreeItems& children = item->GetChildren(); | |
927 | size_t count = children.Count(); | |
928 | for ( size_t n = 0; n < count; n++ ) | |
929 | { | |
930 | y += m_lineHeight; | |
931 | semiOldY = y; | |
932 | ||
933 | PaintLevel( children[n], dc, level+1, y ); | |
934 | } | |
935 | ||
936 | dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY ); | |
937 | } | |
938 | ||
939 | // ----------------------------------------------------------------------------- | |
940 | // wxWindows callbacks | |
941 | // ----------------------------------------------------------------------------- | |
942 | ||
943 | void wxTreeCtrl::OnPaint( const wxPaintEvent &WXUNUSED(event) ) | |
944 | { | |
945 | if ( !m_anchor ) | |
946 | return; | |
947 | ||
948 | wxPaintDC dc(this); | |
949 | PrepareDC( dc ); | |
950 | ||
951 | dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) ); | |
952 | ||
953 | dc.SetPen( m_dottedPen ); | |
954 | m_lineHeight = (int)(dc.GetCharHeight() + 4); | |
955 | ||
956 | int y = m_lineHeight / 2 + 2; | |
957 | PaintLevel( m_anchor, dc, 0, y ); | |
958 | } | |
959 | ||
960 | void wxTreeCtrl::OnSetFocus( const wxFocusEvent &WXUNUSED(event) ) | |
961 | { | |
962 | m_hasFocus = TRUE; | |
963 | if ( m_current ) | |
964 | RefreshLine( m_current ); | |
965 | } | |
966 | ||
967 | void wxTreeCtrl::OnKillFocus( const wxFocusEvent &WXUNUSED(event) ) | |
968 | { | |
969 | m_hasFocus = FALSE; | |
970 | if ( m_current ) | |
971 | RefreshLine( m_current ); | |
972 | } | |
973 | ||
974 | void wxTreeCtrl::OnChar( wxKeyEvent &event ) | |
975 | { | |
976 | // TODO process '+', '-' (expand/collapse branch) and cursor keys | |
977 | event.Skip(); | |
978 | } | |
979 | ||
980 | void wxTreeCtrl::OnMouse( const wxMouseEvent &event ) | |
981 | { | |
982 | if ( !(event.LeftDown() || event.LeftDClick()) ) | |
983 | return; | |
984 | ||
985 | if ( !m_anchor ) | |
986 | return; | |
987 | ||
988 | wxClientDC dc(this); | |
989 | PrepareDC(dc); | |
990 | long x = dc.DeviceToLogicalX( (long)event.GetX() ); | |
991 | long y = dc.DeviceToLogicalY( (long)event.GetY() ); | |
992 | ||
993 | bool onButton = FALSE; | |
994 | wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton ); | |
995 | if ( item == NULL ) | |
996 | return; | |
997 | ||
998 | SelectItem(item); | |
999 | ||
1000 | if ( event.LeftDClick() ) | |
1001 | { | |
1002 | wxTreeEvent event( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() ); | |
1003 | event.m_item = item; | |
1004 | event.m_code = 0; | |
1005 | event.SetEventObject( this ); | |
1006 | ProcessEvent( event ); | |
1007 | } | |
1008 | ||
1009 | if ( onButton ) | |
1010 | { | |
1011 | Toggle( item ); | |
1012 | } | |
1013 | } | |
1014 | ||
1015 | // ----------------------------------------------------------------------------- | |
1016 | // ----------------------------------------------------------------------------- | |
1017 | void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item, | |
1018 | wxDC &dc, | |
1019 | int level, | |
1020 | int &y ) | |
1021 | { | |
1022 | int horizX = level*m_indent; | |
1023 | ||
1024 | item->SetX( horizX+33 ); | |
1025 | item->SetY( y-m_lineHeight/3-2 ); | |
1026 | item->SetHeight( m_lineHeight ); | |
1027 | ||
1028 | if ( item->IsExpanded() ) | |
1029 | return; | |
1030 | ||
1031 | wxArrayTreeItems& children = item->GetChildren(); | |
1032 | size_t count = children.Count(); | |
1033 | for ( size_t n = 0; n < count; n++ ) | |
1034 | { | |
1035 | y += m_lineHeight; | |
1036 | CalculateLevel( children[n], dc, level+1, y ); | |
1037 | } | |
1038 | } | |
1039 | ||
1040 | void wxTreeCtrl::CalculatePositions() | |
1041 | { | |
1042 | if ( !m_anchor ) | |
1043 | return; | |
1044 | ||
1045 | wxClientDC dc(this); | |
1046 | PrepareDC( dc ); | |
1047 | ||
1048 | dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) ); | |
1049 | ||
1050 | dc.SetPen( m_dottedPen ); | |
1051 | m_lineHeight = (int)(dc.GetCharHeight() + 4); | |
1052 | ||
1053 | int y = m_lineHeight / 2 + 2; | |
1054 | CalculateLevel( m_anchor, dc, 0, y ); | |
1055 | } | |
1056 | ||
1057 | void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) | |
1058 | { | |
1059 | wxClientDC dc(this); | |
1060 | PrepareDC(dc); | |
1061 | ||
1062 | int cw = 0; | |
1063 | int ch = 0; | |
1064 | GetClientSize( &cw, &ch ); | |
1065 | ||
1066 | wxRect rect; | |
1067 | rect.x = dc.LogicalToDeviceX( 0 ); | |
1068 | rect.width = cw; | |
1069 | rect.y = dc.LogicalToDeviceY( item->GetY() ); | |
1070 | rect.height = ch; | |
1071 | ||
1072 | Refresh( TRUE, &rect ); | |
1073 | ||
1074 | AdjustMyScrollbars(); | |
1075 | } | |
1076 | ||
1077 | void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item ) | |
1078 | { | |
1079 | wxClientDC dc(this); | |
1080 | PrepareDC( dc ); | |
1081 | ||
1082 | wxRect rect; | |
1083 | rect.x = dc.LogicalToDeviceX( item->GetX() - 2 ); | |
1084 | rect.y = dc.LogicalToDeviceY( item->GetY() - 2 ); | |
1085 | rect.width = 1000; | |
1086 | rect.height = dc.GetCharHeight() + 6; | |
1087 | ||
1088 | Refresh( TRUE, &rect ); | |
1089 | } | |
1090 |