]> git.saurik.com Git - wxWidgets.git/blame - wxPython/contrib/gizmos/treelistctrl.cpp
User the version number in the dir name of the private wxWindows install.
[wxWidgets.git] / wxPython / contrib / gizmos / treelistctrl.cpp
CommitLineData
1fded56b
RD
1/////////////////////////////////////////////////////////////////////////////
2// Name: treelistctrl.cpp (derived by treectlg.h)
3// Purpose: multi column tree control implementation
4// Author: Robert Roebling
5// Created: 01/02/97
6// Modified: Alberto Griggio, 2002
7// 22/10/98 - almost total rewrite, simpler interface (VZ)
8// Id: $Id$
9// Copyright: (c) 1998 Robert Roebling, Julian Smart and Markus Holzem
10// Licence: wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// ===========================================================================
14// declarations
15// ===========================================================================
16
17// ---------------------------------------------------------------------------
18// headers
19// ---------------------------------------------------------------------------
20
21#if defined(__GNUG__) && !defined(__APPLE__)
22 #pragma implementation "treelistctrl.h"
23#endif
24
25// For compilers that support precompilation, includes "wx.h".
26#include "wx/wxprec.h"
27
28#ifdef __BORLANDC__
29 #pragma hdrstop
30#endif
31
32
33#include <wx/treebase.h>
34#include <wx/timer.h>
35#include <wx/textctrl.h>
36#include <wx/imaglist.h>
37#include <wx/settings.h>
38#include <wx/dcclient.h>
39#include <wx/dcscreen.h>
40#include <wx/scrolwin.h>
41
42//#include "wx/gizmos/treelistctrl.h"
43#include "treelistctrl.h"
44
45
46#ifdef __WXGTK__
47 #include <gtk/gtk.h>
48 #include <wx/gtk/win_gtk.h>
49#endif
50
51// ---------------------------------------------------------------------------
52// array types
53// ---------------------------------------------------------------------------
54
55class wxTreeListItem;
56
57WX_DEFINE_ARRAY(wxTreeListItem *, wxArrayTreeListItems);
58
59#include <wx/dynarray.h>
60WX_DECLARE_OBJARRAY(wxTreeListColumnInfo, wxArrayTreeListColumnInfo);
61#include <wx/arrimpl.cpp>
62WX_DEFINE_OBJARRAY(wxArrayTreeListColumnInfo);
63
64#if !wxCHECK_VERSION(2, 3, 3)
65WX_DEFINE_ARRAY(short, wxArrayShort);
66#endif
67
68
69// --------------------------------------------------------------------------
70// constants
71// --------------------------------------------------------------------------
72
73static const int NO_IMAGE = -1;
74
75#define PIXELS_PER_UNIT 10
76
77const wxChar* wxTreeListCtrlNameStr = wxT("treelistctrl");
78
79static wxTreeListColumnInfo wxInvalidTreeListColumnInfo;
80
81
82// ---------------------------------------------------------------------------
83// private classes
84// ---------------------------------------------------------------------------
85//-----------------------------------------------------------------------------
86// wxTreeListHeaderWindow (internal)
87//-----------------------------------------------------------------------------
88
89class wxTreeListHeaderWindow : public wxWindow
90{
91protected:
92 wxTreeListMainWindow *m_owner;
93 wxCursor *m_currentCursor;
94 wxCursor *m_resizeCursor;
95 bool m_isDragging;
96
97 // column being resized
98 int m_column;
99
100 // divider line position in logical (unscrolled) coords
101 int m_currentX;
102
103 // minimal position beyond which the divider line can't be dragged in
104 // logical coords
105 int m_minX;
106
107 wxArrayTreeListColumnInfo m_columns;
108
109 // total width of the columns
110 int m_total_col_width;
111
112
113public:
114 wxTreeListHeaderWindow();
115
116 wxTreeListHeaderWindow( wxWindow *win,
117 wxWindowID id,
118 wxTreeListMainWindow *owner,
119 const wxPoint &pos = wxDefaultPosition,
120 const wxSize &size = wxDefaultSize,
121 long style = 0,
122 const wxString &name = wxT("wxtreelistctrlcolumntitles") );
123
124 virtual ~wxTreeListHeaderWindow();
125
126 void DoDrawRect( wxDC *dc, int x, int y, int w, int h );
127 void DrawCurrent();
128 void AdjustDC(wxDC& dc);
129
130 void OnPaint( wxPaintEvent &event );
131 void OnMouse( wxMouseEvent &event );
132 void OnSetFocus( wxFocusEvent &event );
133
134
135 // columns manipulation
136
137 size_t GetColumnCount() const { return m_columns.GetCount(); }
138
139 void AddColumn(const wxTreeListColumnInfo& col);
140
141 void InsertColumn(size_t before, const wxTreeListColumnInfo& col);
142
143 void RemoveColumn(size_t column);
144
145 void SetColumn(size_t column, const wxTreeListColumnInfo& info);
146 const wxTreeListColumnInfo& GetColumn(size_t column) const
147 {
148 wxCHECK_MSG(column < GetColumnCount(), wxInvalidTreeListColumnInfo, wxT("Invalid column"));
149 return m_columns[column];
150 }
151 wxTreeListColumnInfo& GetColumn(size_t column)
152 {
153 wxCHECK_MSG(column < GetColumnCount(), wxInvalidTreeListColumnInfo, wxT("Invalid column"));
154 return m_columns[column];
155 }
156
157 void SetColumnWidth(size_t column, size_t width);
158
159 void SetColumnText(size_t column, const wxString& text)
160 {
161 wxCHECK_RET(column < GetColumnCount(), wxT("Invalid column"));
162 m_columns[column].SetText(text);
163 }
164
165 wxString GetColumnText(size_t column) const
166 {
167 wxCHECK_MSG(column < GetColumnCount(), wxEmptyString, wxT("Invalid column"));
168 return m_columns[column].GetText();
169 }
170
171 int GetColumnWidth(size_t column) const
172 {
173 wxCHECK_MSG(column < GetColumnCount(), -1, wxT("Invalid column"));
174 return m_columns[column].GetWidth();
175 }
176
177 int GetWidth() const { return m_total_col_width; }
178
179 // needs refresh
180 bool m_dirty;
181
182private:
183 // common part of all ctors
184 void Init();
185
186 void SendListEvent(wxEventType type, wxPoint pos);
187
188 DECLARE_DYNAMIC_CLASS(wxTreeListHeaderWindow)
189 DECLARE_EVENT_TABLE()
190};
191
192
193// this is the "true" control
194class wxTreeListMainWindow: public wxScrolledWindow
195{
196public:
197 // creation
198 // --------
199 wxTreeListMainWindow() { Init(); }
200
201 wxTreeListMainWindow(wxTreeListCtrl *parent, wxWindowID id = -1,
202 const wxPoint& pos = wxDefaultPosition,
203 const wxSize& size = wxDefaultSize,
204 long style = wxTR_DEFAULT_STYLE,
205 const wxValidator &validator = wxDefaultValidator,
206 const wxString& name = wxT("wxtreelistmainwindow"))
207 {
208 Init();
209 Create(parent, id, pos, size, style, validator, name);
210 }
211
212 virtual ~wxTreeListMainWindow();
213
214 bool Create(wxTreeListCtrl *parent, wxWindowID id = -1,
215 const wxPoint& pos = wxDefaultPosition,
216 const wxSize& size = wxDefaultSize,
217 long style = wxTR_DEFAULT_STYLE,
218 const wxValidator &validator = wxDefaultValidator,
219 const wxString& name = wxT("wxtreelistctrl"));
220
221 // accessors
222 // ---------
223
224 // get the total number of items in the control
225 size_t GetCount() const;
226
227 // indent is the number of pixels the children are indented relative to
228 // the parents position. SetIndent() also redraws the control
229 // immediately.
230 unsigned int GetIndent() const { return m_indent; }
231 void SetIndent(unsigned int indent);
232
233 // spacing is the number of pixels between the start and the Text
234 unsigned int GetSpacing() const { return m_spacing; }
235 void SetSpacing(unsigned int spacing);
236
237 // see wxTreeListCtrl for the meaning
238 unsigned int GetLineSpacing() const { return m_linespacing; }
239 void SetLineSpacing(unsigned int spacing);
240
241 // image list: these functions allow to associate an image list with
242 // the control and retrieve it. Note that when assigned with
243 // SetImageList, the control does _not_ delete
244 // the associated image list when it's deleted in order to allow image
245 // lists to be shared between different controls. If you use
246 // AssignImageList, the control _does_ delete the image list.
247 //
248 // The normal image list is for the icons which correspond to the
249 // normal tree item state (whether it is selected or not).
250 // Additionally, the application might choose to show a state icon
251 // which corresponds to an app-defined item state (for example,
252 // checked/unchecked) which are taken from the state image list.
253 wxImageList *GetImageList() const;
254 wxImageList *GetStateImageList() const;
255 wxImageList *GetButtonsImageList() const;
256
257 void SetImageList(wxImageList *imageList);
258 void SetStateImageList(wxImageList *imageList);
259 void SetButtonsImageList(wxImageList *imageList);
260 void AssignImageList(wxImageList *imageList);
261 void AssignStateImageList(wxImageList *imageList);
262 void AssignButtonsImageList(wxImageList *imageList);
263
264 // Functions to work with tree ctrl items.
265
266 // accessors
267 // ---------
268
269 // retrieve item's label
270 wxString GetItemText(const wxTreeItemId& item) const
271 { return GetItemText(item, GetMainColumn()); }
272 // get one of the images associated with the item (normal by default)
273 int GetItemImage(const wxTreeItemId& item,
274 wxTreeItemIcon which = wxTreeItemIcon_Normal) const
275 { return GetItemImage(item, GetMainColumn(), which); }
276
277 // get the data associated with the item
278 wxTreeItemData *GetItemData(const wxTreeItemId& item) const;
279
280 bool GetItemBold(const wxTreeItemId& item) const;
281 wxColour GetItemTextColour(const wxTreeItemId& item) const;
282 wxColour GetItemBackgroundColour(const wxTreeItemId& item) const;
283 wxFont GetItemFont(const wxTreeItemId& item) const;
284
285 // modifiers
286 // ---------
287
288 // set item's label
289 void SetItemText(const wxTreeItemId& item, const wxString& text)
290 { SetItemText(item, GetMainColumn(), text); }
291
292 // get one of the images associated with the item (normal by default)
293 void SetItemImage(const wxTreeItemId& item, int image,
294 wxTreeItemIcon which = wxTreeItemIcon_Normal)
295 { SetItemImage(item, GetMainColumn(), image, which); }
296
297 // associate some data with the item
298 void SetItemData(const wxTreeItemId& item, wxTreeItemData *data);
299
300 // force appearance of [+] button near the item. This is useful to
301 // allow the user to expand the items which don't have any children now
302 // - but instead add them only when needed, thus minimizing memory
303 // usage and loading time.
304 void SetItemHasChildren(const wxTreeItemId& item, bool has = TRUE);
305
306 // the item will be shown in bold
307 void SetItemBold(const wxTreeItemId& item, bool bold = TRUE);
308
309 // set the item's text colour
310 void SetItemTextColour(const wxTreeItemId& item, const wxColour& col);
311
312 // set the item's background colour
313 void SetItemBackgroundColour(const wxTreeItemId& item,
314 const wxColour& col);
315
316 // set the item's font (should be of the same height for all items)
317 void SetItemFont(const wxTreeItemId& item, const wxFont& font);
318
319 // set the window font
320 virtual bool SetFont( const wxFont &font );
321
322 // set the styles. No need to specify a GetWindowStyle here since
323 // the base wxWindow member function will do it for us
324 void SetWindowStyle(const long styles);
325
326 // item status inquiries
327 // ---------------------
328
329 // is the item visible (it might be outside the view or not expanded)?
330 bool IsVisible(const wxTreeItemId& item) const;
331 // does the item has any children?
332 bool HasChildren(const wxTreeItemId& item) const
333 { return ItemHasChildren(item); }
334 bool ItemHasChildren(const wxTreeItemId& item) const;
335 // is the item expanded (only makes sense if HasChildren())?
336 bool IsExpanded(const wxTreeItemId& item) const;
337 // is this item currently selected (the same as has focus)?
338 bool IsSelected(const wxTreeItemId& item) const;
339 // is item text in bold font?
340 bool IsBold(const wxTreeItemId& item) const;
341 // does the layout include space for a button?
342
343 // number of children
344 // ------------------
345
346 // if 'recursively' is FALSE, only immediate children count, otherwise
347 // the returned number is the number of all items in this branch
348 size_t GetChildrenCount(const wxTreeItemId& item, bool recursively = TRUE);
349
350 // navigation
351 // ----------
352
353 // wxTreeItemId.IsOk() will return FALSE if there is no such item
354
355 // get the root tree item
356 wxTreeItemId GetRootItem() const { return m_anchor; }
357
358 // get the item currently selected (may return NULL if no selection)
359 wxTreeItemId GetSelection() const { return m_current; }
360
361 // get the items currently selected, return the number of such item
362 size_t GetSelections(wxArrayTreeItemIds&) const;
363
364 // get the parent of this item (may return NULL if root)
365 wxTreeItemId GetParent(const wxTreeItemId& item) const;
366
367 // for this enumeration function you must pass in a "cookie" parameter
368 // which is opaque for the application but is necessary for the library
369 // to make these functions reentrant (i.e. allow more than one
370 // enumeration on one and the same object simultaneously). Of course,
371 // the "cookie" passed to GetFirstChild() and GetNextChild() should be
372 // the same!
373
374 // get the first child of this item
375 wxTreeItemId GetFirstChild(const wxTreeItemId& item, long& cookie) const;
376 // get the next child
377 wxTreeItemId GetNextChild(const wxTreeItemId& item, long& cookie) const;
378 // get the last child of this item - this method doesn't use cookies
379 wxTreeItemId GetLastChild(const wxTreeItemId& item) const;
380
381 // get the next sibling of this item
382 wxTreeItemId GetNextSibling(const wxTreeItemId& item) const;
383 // get the previous sibling
384 wxTreeItemId GetPrevSibling(const wxTreeItemId& item) const;
385
386 // get first visible item
387 wxTreeItemId GetFirstVisibleItem() const;
388 // get the next visible item: item must be visible itself!
389 // see IsVisible() and wxTreeCtrl::GetFirstVisibleItem()
390 wxTreeItemId GetNextVisible(const wxTreeItemId& item) const;
391 // get the previous visible item: item must be visible itself!
392 wxTreeItemId GetPrevVisible(const wxTreeItemId& item) const;
393
394 // Only for internal use right now, but should probably be public
395 wxTreeItemId GetNext(const wxTreeItemId& item) const;
396
397 // operations
398 // ----------
399
400 // add the root node to the tree
401 wxTreeItemId AddRoot(const wxString& text,
402 int image = -1, int selectedImage = -1,
403 wxTreeItemData *data = NULL);
404
405 // insert a new item in as the first child of the parent
406 wxTreeItemId PrependItem(const wxTreeItemId& parent,
407 const wxString& text,
408 int image = -1, int selectedImage = -1,
409 wxTreeItemData *data = NULL);
410
411 // insert a new item after a given one
412 wxTreeItemId InsertItem(const wxTreeItemId& parent,
413 const wxTreeItemId& idPrevious,
414 const wxString& text,
415 int image = -1, int selectedImage = -1,
416 wxTreeItemData *data = NULL);
417
418 // insert a new item before the one with the given index
419 wxTreeItemId InsertItem(const wxTreeItemId& parent,
420 size_t index,
421 const wxString& text,
422 int image = -1, int selectedImage = -1,
423 wxTreeItemData *data = NULL);
424
425 // insert a new item in as the last child of the parent
426 wxTreeItemId AppendItem(const wxTreeItemId& parent,
427 const wxString& text,
428 int image = -1, int selectedImage = -1,
429 wxTreeItemData *data = NULL);
430
431 // delete this item and associated data if any
432 void Delete(const wxTreeItemId& item);
433 // delete all children (but don't delete the item itself)
434 // NB: this won't send wxEVT_COMMAND_TREE_ITEM_DELETED events
435 void DeleteChildren(const wxTreeItemId& item);
436 // delete all items from the tree
437 // NB: this won't send wxEVT_COMMAND_TREE_ITEM_DELETED events
438 void DeleteAllItems();
439
440 // expand this item
441 void Expand(const wxTreeItemId& item);
442 // expand this item and all subitems recursively
443 void ExpandAll(const wxTreeItemId& item);
444 // collapse the item without removing its children
445 void Collapse(const wxTreeItemId& item);
446 // collapse the item and remove all children
447 void CollapseAndReset(const wxTreeItemId& item);
448 // toggles the current state
449 void Toggle(const wxTreeItemId& item);
450
451 // remove the selection from currently selected item (if any)
452 void Unselect();
453 void UnselectAll();
454 // select this item
455 void SelectItem(const wxTreeItemId& item, bool unselect_others=TRUE,
456 bool extended_select=FALSE);
457 // make sure this item is visible (expanding the parent item and/or
458 // scrolling to this item if necessary)
459 void EnsureVisible(const wxTreeItemId& item);
460 // scroll to this item (but don't expand its parent)
461 void ScrollTo(const wxTreeItemId& item);
462 void AdjustMyScrollbars();
463
464 // The first function is more portable (because easier to implement
465 // on other platforms), but the second one returns some extra info.
466 wxTreeItemId HitTest(const wxPoint& point)
467 { int dummy; return HitTest(point, dummy); }
468 wxTreeItemId HitTest(const wxPoint& point, int& flags)
469 { int col; return HitTest(point, flags, col); }
470 // ALB
471 wxTreeItemId HitTest(const wxPoint& point, int& flags, int& column);
472
473
474 // get the bounding rectangle of the item (or of its label only)
475 bool GetBoundingRect(const wxTreeItemId& item,
476 wxRect& rect,
477 bool textOnly = FALSE) const;
478
479 // Start editing the item label: this (temporarily) replaces the item
480 // with a one line edit control. The item will be selected if it hadn't
481 // been before.
482 void EditLabel( const wxTreeItemId& item ) { Edit( item ); }
483 void Edit( const wxTreeItemId& item );
484
485 // sorting
486 // this function is called to compare 2 items and should return -1, 0
487 // or +1 if the first item is less than, equal to or greater than the
488 // second one. The base class version performs alphabetic comparaison
489 // of item labels (GetText)
490 virtual int OnCompareItems(const wxTreeItemId& item1,
491 const wxTreeItemId& item2);
492 // sort the children of this item using OnCompareItems
493 //
494 // NB: this function is not reentrant and not MT-safe (FIXME)!
495 void SortChildren(const wxTreeItemId& item);
496
497 // deprecated functions: use Set/GetItemImage directly
498 // get the selected item image
499 int GetItemSelectedImage(const wxTreeItemId& item) const
500 { return GetItemImage(item, wxTreeItemIcon_Selected); }
501 // set the selected item image
502 void SetItemSelectedImage(const wxTreeItemId& item, int image)
503 { SetItemImage(item, image, wxTreeItemIcon_Selected); }
504
505 // implementation only from now on
506
507 // overridden base class virtuals
508 virtual bool SetBackgroundColour(const wxColour& colour);
509 virtual bool SetForegroundColour(const wxColour& colour);
510
511 // callbacks
512 void OnPaint( wxPaintEvent &event );
513 void OnSetFocus( wxFocusEvent &event );
514 void OnKillFocus( wxFocusEvent &event );
515 void OnChar( wxKeyEvent &event );
516 void OnMouse( wxMouseEvent &event );
517 void OnIdle( wxIdleEvent &event );
518 void OnSize(wxSizeEvent& event); // ALB
519 void OnScroll(wxScrollWinEvent& event); // ALB
520
521 // implementation helpers
522 void SendDeleteEvent(wxTreeListItem *itemBeingDeleted);
523
524 void DrawBorder(const wxTreeItemId& item);
525 void DrawLine(const wxTreeItemId& item, bool below);
526
527 size_t GetColumnCount() const
528 { return m_owner->GetHeaderWindow()->GetColumnCount(); }
529
530 void SetMainColumn(size_t column)
531 {
532 if(column < GetColumnCount())
533 m_main_column = column;
534 }
535 size_t GetMainColumn() const { return m_main_column; }
536
537 void SetItemText(const wxTreeItemId& item, size_t column,
538 const wxString& text);
539 wxString GetItemText(const wxTreeItemId& item, size_t column) const;
540
541 void SetItemImage(const wxTreeItemId& item, size_t column, int image,
542 wxTreeItemIcon which = wxTreeItemIcon_Normal);
543 int GetItemImage(const wxTreeItemId& item, size_t column,
544 wxTreeItemIcon which = wxTreeItemIcon_Normal) const;
545protected:
546 wxTreeListCtrl* m_owner; // ALB
547
548 size_t m_main_column; // ALB
549
550 friend class wxTreeListItem;
551 friend class wxTreeListRenameTimer;
552 friend class wxTreeListTextCtrl;
553
554 wxFont m_normalFont;
555 wxFont m_boldFont;
556
557 wxTreeListItem *m_anchor;
558 wxTreeListItem *m_current, *m_key_current, *m_currentEdit;
559 unsigned short m_indent;
560 unsigned short m_spacing;
561 int m_lineHeight;
562 unsigned short m_linespacing;
563 wxPen m_dottedPen;
564 wxBrush *m_hilightBrush,
565 *m_hilightUnfocusedBrush;
566 bool m_hasFocus;
567public:
568 bool m_dirty;
569protected:
570 bool m_ownsImageListNormal,
571 m_ownsImageListState,
572 m_ownsImageListButtons;
573 bool m_isDragging; // true between BEGIN/END drag events
574 bool m_renameAccept;
575 bool m_lastOnSame; // last click on the same item as prev
576 wxImageList *m_imageListNormal,
577 *m_imageListState,
578 *m_imageListButtons;
579
580 int m_dragCount;
581 wxPoint m_dragStart;
582 wxTreeListItem *m_dropTarget;
583 wxCursor m_oldCursor; // cursor is changed while dragging
584 wxTreeListItem *m_oldSelection;
585
586 wxTimer *m_renameTimer;
587 wxString m_renameRes;
588
589 // the common part of all ctors
590 void Init();
591
592 // misc helpers
593 wxTreeItemId DoInsertItem(const wxTreeItemId& parent,
594 size_t previous,
595 const wxString& text,
596 int image, int selectedImage,
597 wxTreeItemData *data);
598 bool HasButtons(void) const
599 { return (m_imageListButtons != NULL)
600 || HasFlag(wxTR_TWIST_BUTTONS|wxTR_HAS_BUTTONS); }
601
602protected:
603 void CalculateLineHeight();
604 int GetLineHeight(wxTreeListItem *item) const;
605 void PaintLevel( wxTreeListItem *item, wxDC& dc, int level, int &y,
606 int x_offset);
607 void PaintItem( wxTreeListItem *item, wxDC& dc);
608
609 void CalculateLevel( wxTreeListItem *item, wxDC &dc, int level, int &y,
610 int x_offset);
611 void CalculatePositions();
612 void CalculateSize( wxTreeListItem *item, wxDC &dc );
613
614 void RefreshSubtree( wxTreeListItem *item );
615 void RefreshLine( wxTreeListItem *item );
616
617 // redraw all selected items
618 void RefreshSelected();
619
620 // RefreshSelected() recursive helper
621 void RefreshSelectedUnder(wxTreeListItem *item);
622
623 void OnRenameTimer();
624 void OnRenameAccept();
625
626 void FillArray(wxTreeListItem*, wxArrayTreeItemIds&) const;
627 void SelectItemRange( wxTreeListItem *item1, wxTreeListItem *item2 );
628 bool TagAllChildrenUntilLast(wxTreeListItem *crt_item,
629 wxTreeListItem *last_item, bool select);
630 bool TagNextChildren(wxTreeListItem *crt_item, wxTreeListItem *last_item,
631 bool select);
632 void UnselectAllChildren( wxTreeListItem *item );
633
634 void DrawDropEffect(wxTreeListItem *item);
635
636private:
637 DECLARE_EVENT_TABLE()
638 DECLARE_DYNAMIC_CLASS(wxTreeListMainWindow)
639};
640
641
642// timer used for enabling in-place edit
643class wxTreeListRenameTimer: public wxTimer
644{
645public:
646 wxTreeListRenameTimer( wxTreeListMainWindow *owner );
647
648 void Notify();
649
650private:
651 wxTreeListMainWindow *m_owner;
652};
653
654// control used for in-place edit
655class wxTreeListTextCtrl: public wxTextCtrl
656{
657public:
658 wxTreeListTextCtrl( wxWindow *parent,
659 const wxWindowID id,
660 bool *accept,
661 wxString *res,
662 wxTreeListMainWindow *owner,
663 const wxString &value = wxEmptyString,
664 const wxPoint &pos = wxDefaultPosition,
665 const wxSize &size = wxDefaultSize,
666 int style = wxSIMPLE_BORDER,
667 const wxValidator& validator = wxDefaultValidator,
668 const wxString &name = wxTextCtrlNameStr );
669
670 void OnChar( wxKeyEvent &event );
671 void OnKeyUp( wxKeyEvent &event );
672 void OnKillFocus( wxFocusEvent &event );
673
674private:
675 bool *m_accept;
676 wxString *m_res;
677 wxTreeListMainWindow *m_owner;
678 wxString m_startValue;
679 bool m_finished;
680
681 DECLARE_EVENT_TABLE()
682};
683
684// a tree item
685class wxTreeListItem
686{
687public:
688 // ctors & dtor
689 wxTreeListItem() { m_data = NULL; }
690 wxTreeListItem( wxTreeListMainWindow *owner,
691 wxTreeListItem *parent,
692 const wxArrayString& text,
693 int image,
694 int selImage,
695 wxTreeItemData *data );
696
697 ~wxTreeListItem();
698
699 // trivial accessors
700 wxArrayTreeListItems& GetChildren() { return m_children; }
701
702 const wxString GetText() const
703 {
704 if(m_text.GetCount() > 0) return m_text[0];
705 return wxEmptyString;
706 }
707 const wxString GetText(size_t col) const
708 {
709 if(m_text.GetCount() > col) return m_text[col];
710 return wxEmptyString;
711 }
712 int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const
713 { return m_images[which]; }
714 int GetImage(size_t col, wxTreeItemIcon which=wxTreeItemIcon_Normal) const
715 {
716 if(col == m_owner->GetMainColumn()) return m_images[which];
717 if(col < m_col_images.GetCount()) return m_col_images[col];
718 return NO_IMAGE;
719 }
720 wxTreeItemData *GetData() const { return m_data; }
721
722 // returns the current image for the item (depending on its
723 // selected/expanded/whatever state)
724 int GetCurrentImage() const;
725
726 void SetText( const wxString &text );
727 void SetText(size_t col, const wxString& text) // ALB
728 {
729 if(col < m_text.GetCount())
730 m_text[col] = text;
731 else if(col < m_owner->GetColumnCount()) {
732 int howmany = m_owner->GetColumnCount();
733 for(int i = m_text.GetCount(); i < howmany; ++i)
734 m_text.Add(wxEmptyString);
735 m_text[col] = text;
736 }
737 }
738 void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; }
739 void SetImage(size_t col, int image, wxTreeItemIcon which)
740 {
741 if(col == m_owner->GetMainColumn()) m_images[which] = image;
742 else if(col < m_col_images.GetCount())
743 m_col_images[col] = image;
744 else if(col < m_owner->GetColumnCount()) {
745 int howmany = m_owner->GetColumnCount();
746 for(int i = m_col_images.GetCount(); i < howmany; ++i)
747 m_col_images.Add(NO_IMAGE);
748 m_col_images[col] = image;
749 }
750 }
751
752 void SetData(wxTreeItemData *data) { m_data = data; }
753
754 void SetHasPlus(bool has = TRUE) { m_hasPlus = has; }
755
756 void SetBold(bool bold) { m_isBold = bold; }
757
758 int GetX() const { return m_x; }
759 int GetY() const { return m_y; }
760
761 void SetX(int x) { m_x = x; }
762 void SetY(int y) { m_y = y; }
763
764 int GetHeight() const { return m_height; }
765 int GetWidth() const { return m_width; }
766
767 void SetHeight(int h) { m_height = h; }
768 void SetWidth(int w) { m_width = w; }
769
770 wxTreeListItem *GetParent() const { return m_parent; }
771
772 // operations
773 // deletes all children notifying the treectrl about it if !NULL
774 // pointer given
775 void DeleteChildren(wxTreeListMainWindow *tree = NULL);
776
777 // get count of all children (and grand children if 'recursively')
778 size_t GetChildrenCount(bool recursively = TRUE) const;
779
780 void Insert(wxTreeListItem *child, size_t index)
781 { m_children.Insert(child, index); }
782
783 void GetSize( int &x, int &y, const wxTreeListMainWindow* );
784
785 // return the item at given position (or NULL if no item), onButton is
786 // TRUE if the point belongs to the item's button, otherwise it lies
787 // on the button's label
788 wxTreeListItem *HitTest( const wxPoint& point,
789 const wxTreeListMainWindow *,
790 int &flags,
791 int level );
792 wxTreeListItem *HitTest( const wxPoint& point,
793 const wxTreeListMainWindow *,
794 int &flags, int& column /*ALB*/,
795 int level );
796
797 void Expand() { m_isCollapsed = FALSE; }
798 void Collapse() { m_isCollapsed = TRUE; }
799
800 void SetHilight( bool set = TRUE ) { m_hasHilight = set; }
801
802 // status inquiries
803 bool HasChildren() const { return !m_children.IsEmpty(); }
804 bool IsSelected() const { return m_hasHilight != 0; }
805 bool IsExpanded() const { return !m_isCollapsed; }
806 bool HasPlus() const { return m_hasPlus || HasChildren(); }
807 bool IsBold() const { return m_isBold != 0; }
808
809 // attributes
810 // get them - may be NULL
811 wxTreeItemAttr *GetAttributes() const { return m_attr; }
812 // get them ensuring that the pointer is not NULL
813 wxTreeItemAttr& Attr()
814 {
815 if ( !m_attr )
816 {
817 m_attr = new wxTreeItemAttr;
818 m_ownsAttr = TRUE;
819 }
820 return *m_attr;
821 }
822 // set them
823 void SetAttributes(wxTreeItemAttr *attr)
824 {
825 if ( m_ownsAttr ) delete m_attr;
826 m_attr = attr;
827 m_ownsAttr = FALSE;
828 }
829 // set them and delete when done
830 void AssignAttributes(wxTreeItemAttr *attr)
831 {
832 SetAttributes(attr);
833 m_ownsAttr = TRUE;
834 }
835
836private:
837 wxTreeListMainWindow *m_owner; // control the item belongs to
838
839 // since there can be very many of these, we save size by chosing
840 // the smallest representation for the elements and by ordering
841 // the members to avoid padding.
842 wxArrayString m_text; // labels to be rendered for item
843
844 wxTreeItemData *m_data; // user-provided data
845
846 wxArrayTreeListItems m_children; // list of children
847 wxTreeListItem *m_parent; // parent of this item
848
849 wxTreeItemAttr *m_attr; // attributes???
850
851 // tree ctrl images for the normal, selected, expanded and
852 // expanded+selected states
853 short m_images[wxTreeItemIcon_Max];
854 wxArrayShort m_col_images; // images for the various columns (!= main)
855
856 wxCoord m_x; // (virtual) offset from top
857 wxCoord m_y; // (virtual) offset from left
858 short m_width; // width of this item
859 unsigned char m_height; // height of this item
860
861 // use bitfields to save size
862 int m_isCollapsed :1;
863 int m_hasHilight :1; // same as focused
864 int m_hasPlus :1; // used for item which doesn't have
865 // children but has a [+] button
866 int m_isBold :1; // render the label in bold font
867 int m_ownsAttr :1; // delete attribute when done
868};
869
870// ===========================================================================
871// implementation
872// ===========================================================================
873
874// ----------------------------------------------------------------------------
875// private functions
876// ----------------------------------------------------------------------------
877
878// translate the key or mouse event flags to the type of selection we're
879// dealing with
880static void EventFlagsToSelType(long style,
881 bool shiftDown,
882 bool ctrlDown,
883 bool &is_multiple,
884 bool &extended_select,
885 bool &unselect_others)
886{
887 is_multiple = (style & wxTR_MULTIPLE) != 0;
888 extended_select = shiftDown && is_multiple;
889 unselect_others = !(extended_select || (ctrlDown && is_multiple));
890}
891
892// ---------------------------------------------------------------------------
893// wxTreeListRenameTimer (internal)
894// ---------------------------------------------------------------------------
895
896wxTreeListRenameTimer::wxTreeListRenameTimer( wxTreeListMainWindow *owner )
897{
898 m_owner = owner;
899}
900
901void wxTreeListRenameTimer::Notify()
902{
903 m_owner->OnRenameTimer();
904}
905
906//-----------------------------------------------------------------------------
907// wxTreeListTextCtrl (internal)
908//-----------------------------------------------------------------------------
909
910BEGIN_EVENT_TABLE(wxTreeListTextCtrl,wxTextCtrl)
911 EVT_CHAR (wxTreeListTextCtrl::OnChar)
912 EVT_KEY_UP (wxTreeListTextCtrl::OnKeyUp)
913 EVT_KILL_FOCUS (wxTreeListTextCtrl::OnKillFocus)
914END_EVENT_TABLE()
915
916wxTreeListTextCtrl::wxTreeListTextCtrl( wxWindow *parent,
917 const wxWindowID id,
918 bool *accept,
919 wxString *res,
920 wxTreeListMainWindow *owner,
921 const wxString &value,
922 const wxPoint &pos,
923 const wxSize &size,
924 int style,
925 const wxValidator& validator,
926 const wxString &name )
927 : wxTextCtrl( parent, id, value, pos, size, style, validator, name )
928{
929 m_res = res;
930 m_accept = accept;
931 m_owner = owner;
932 (*m_accept) = FALSE;
933 (*m_res) = wxEmptyString;
934 m_startValue = value;
935 m_finished = FALSE;
936}
937
938void wxTreeListTextCtrl::OnChar( wxKeyEvent &event )
939{
940 if (event.m_keyCode == WXK_RETURN)
941 {
942 (*m_accept) = TRUE;
943 (*m_res) = GetValue();
944
945 if ((*m_res) != m_startValue)
946 m_owner->OnRenameAccept();
947
948 if (!wxPendingDelete.Member(this))
949 wxPendingDelete.Append(this);
950
951 m_finished = TRUE;
952 m_owner->SetFocus(); // This doesn't work. TODO.
953
954 return;
955 }
956 if (event.m_keyCode == WXK_ESCAPE)
957 {
958 (*m_accept) = FALSE;
959 (*m_res) = wxEmptyString;
960
961 if (!wxPendingDelete.Member(this))
962 wxPendingDelete.Append(this);
963
964 m_finished = TRUE;
965 m_owner->SetFocus(); // This doesn't work. TODO.
966
967 return;
968 }
969 event.Skip();
970}
971
972void wxTreeListTextCtrl::OnKeyUp( wxKeyEvent &event )
973{
974 if (m_finished)
975 {
976 event.Skip();
977 return;
978 }
979
980 // auto-grow the textctrl:
981 wxSize parentSize = m_owner->GetSize();
982 wxPoint myPos = GetPosition();
983 wxSize mySize = GetSize();
984 int sx, sy;
985 GetTextExtent(GetValue() + _T("M"), &sx, &sy);
986 if (myPos.x + sx > parentSize.x) sx = parentSize.x - myPos.x;
987 if (mySize.x > sx) sx = mySize.x;
988 SetSize(sx, -1);
989
990 event.Skip();
991}
992
993void wxTreeListTextCtrl::OnKillFocus( wxFocusEvent &event )
994{
995 if (m_finished)
996 {
997 event.Skip();
998 return;
999 }
1000
1001 if (!wxPendingDelete.Member(this))
1002 wxPendingDelete.Append(this);
1003
1004 (*m_accept) = TRUE;
1005 (*m_res) = GetValue();
1006
1007 if ((*m_res) != m_startValue)
1008 m_owner->OnRenameAccept();
1009}
1010
1011//-----------------------------------------------------------------------------
1012// wxTreeListHeaderWindow
1013//-----------------------------------------------------------------------------
1014
1015IMPLEMENT_DYNAMIC_CLASS(wxTreeListHeaderWindow,wxWindow);
1016
1017BEGIN_EVENT_TABLE(wxTreeListHeaderWindow,wxWindow)
1018 EVT_PAINT (wxTreeListHeaderWindow::OnPaint)
1019 EVT_MOUSE_EVENTS (wxTreeListHeaderWindow::OnMouse)
1020 EVT_SET_FOCUS (wxTreeListHeaderWindow::OnSetFocus)
1021END_EVENT_TABLE()
1022
1023void wxTreeListHeaderWindow::Init()
1024{
1025 m_currentCursor = (wxCursor *) NULL;
1026 m_isDragging = FALSE;
1027 m_dirty = FALSE;
1028 m_total_col_width = 0;
1029}
1030
1031wxTreeListHeaderWindow::wxTreeListHeaderWindow()
1032{
1033 Init();
1034
1035 m_owner = (wxTreeListMainWindow *) NULL;
1036 m_resizeCursor = (wxCursor *) NULL;
1037}
1038
1039wxTreeListHeaderWindow::wxTreeListHeaderWindow( wxWindow *win,
1040 wxWindowID id,
1041 wxTreeListMainWindow *owner,
1042 const wxPoint& pos,
1043 const wxSize& size,
1044 long style,
1045 const wxString &name )
1046 : wxWindow( win, id, pos, size, style, name )
1047{
1048 Init();
1049
1050 m_owner = owner;
1051 m_resizeCursor = new wxCursor(wxCURSOR_SIZEWE);
1052
1053 SetBackgroundColour(wxSystemSettings::GetSystemColour(
1054 wxSYS_COLOUR_BTNFACE));
1055}
1056
1057wxTreeListHeaderWindow::~wxTreeListHeaderWindow()
1058{
1059 delete m_resizeCursor;
1060}
1061
1062void wxTreeListHeaderWindow::DoDrawRect( wxDC *dc, int x, int y, int w, int h )
1063{
1064#ifdef __WXGTK__
1065 GtkStateType state = m_parent->IsEnabled() ? GTK_STATE_NORMAL
1066 : GTK_STATE_INSENSITIVE;
1067
1068 x = dc->XLOG2DEV( x );
1069
1070 gtk_paint_box (m_wxwindow->style, GTK_PIZZA(m_wxwindow)->bin_window,
1071 state, GTK_SHADOW_OUT,
1072 (GdkRectangle*) NULL, m_wxwindow, "button",
1073 x-1, y-1, w+2, h+2);
1074#elif defined( __WXMAC__ )
1075 const int m_corner = 1;
1076
1077 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1078
1079 dc->SetPen( wxPen(wxSystemSettings::GetSystemColour(
1080 wxSYS_COLOUR_BTNSHADOW), 1, wxSOLID));
1081 dc->DrawLine( x+w-m_corner+1, y, x+w, y+h ); // right (outer)
1082 dc->DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer)
1083
1084 wxPen pen( wxColour( 0x88 , 0x88 , 0x88 ), 1, wxSOLID );
1085
1086 dc->SetPen( pen );
1087 dc->DrawLine( x+w-m_corner, y, x+w-1, y+h ); // right (inner)
1088 dc->DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner)
1089
1090 dc->SetPen( *wxWHITE_PEN );
1091 dc->DrawRectangle( x, y, w-m_corner+1, 1 ); // top (outer)
1092 dc->DrawRectangle( x, y, 1, h ); // left (outer)
1093 dc->DrawLine( x, y+h-1, x+1, y+h-1 );
1094 dc->DrawLine( x+w-1, y, x+w-1, y+1 );
1095#else // !GTK, !Mac
1096 const int m_corner = 1;
1097
1098 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1099
1100 dc->SetPen( *wxBLACK_PEN );
1101 dc->DrawLine( x+w-m_corner+1, y, x+w, y+h ); // right (outer)
1102 dc->DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer)
1103
1104 wxPen pen(wxSystemSettings::GetSystemColour(
1105 wxSYS_COLOUR_BTNSHADOW ), 1, wxSOLID);
1106
1107 dc->SetPen( pen );
1108 dc->DrawLine( x+w-m_corner, y, x+w-1, y+h ); // right (inner)
1109 dc->DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner)
1110
1111 dc->SetPen( *wxWHITE_PEN );
1112 dc->DrawRectangle( x, y, w-m_corner+1, 1 ); // top (outer)
1113 dc->DrawRectangle( x, y, 1, h ); // left (outer)
1114 dc->DrawLine( x, y+h-1, x+1, y+h-1 );
1115 dc->DrawLine( x+w-1, y, x+w-1, y+1 );
1116#endif
1117}
1118
1119// shift the DC origin to match the position of the main window horz
1120// scrollbar: this allows us to always use logical coords
1121void wxTreeListHeaderWindow::AdjustDC(wxDC& dc)
1122{
1123 int xpix;
1124 m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
1125
1126 int x;
1127 m_owner->GetViewStart( &x, NULL );
1128
1129 // account for the horz scrollbar offset
1130 dc.SetDeviceOrigin( -x * xpix, 0 );
1131}
1132
1133void wxTreeListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1134{
1135 static const int HEADER_OFFSET_X = 1, HEADER_OFFSET_Y = 1;
1136#ifdef __WXGTK__
1137 wxClientDC dc( this );
1138#else
1139 wxPaintDC dc( this );
1140#endif
1141
1142 PrepareDC( dc );
1143 AdjustDC( dc );
1144
1145 dc.BeginDrawing();
1146
1147 dc.SetFont( GetFont() );
1148
1149 // width and height of the entire header window
1150 int w, h;
1151 GetClientSize( &w, &h );
1152 m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
1153
1154 dc.SetBackgroundMode(wxTRANSPARENT);
1155
1156 // do *not* use the listctrl colour for headers - one day we will have a
1157 // function to set it separately
1158 //dc.SetTextForeground( *wxBLACK );
1159 dc.SetTextForeground(wxSystemSettings::
1160 GetSystemColour( wxSYS_COLOUR_WINDOWTEXT ));
1161
1162 int x = HEADER_OFFSET_X;
1163
1164 int numColumns = GetColumnCount();
1165 for ( int i = 0; i < numColumns && x < w; i++ )
1166 {
1167 wxTreeListColumnInfo& column = GetColumn(i);
1168 int wCol = column.GetWidth();
1169
1170 // the width of the rect to draw: make it smaller to fit entirely
1171 // inside the column rect
1172 int cw = wCol - 2;
1173
1174 dc.SetPen( *wxWHITE_PEN );
1175
1176 DoDrawRect( &dc, x, HEADER_OFFSET_Y, cw, h-2 );
1177
1178 // if we have an image, draw it on the right of the label
1179 int image = column.GetImage(); //item.m_image;
1180 int ix = -2, iy = 0;
1181 wxImageList* imageList = m_owner->GetImageList();
1182 if(image != -1) {
1183 if(imageList) {
1184 imageList->GetSize(image, ix, iy);
1185 }
1186 //else: ignore the column image
1187 }
1188
1189 // extra margins around the text label
1190 static const int EXTRA_WIDTH = 3;
1191 static const int EXTRA_HEIGHT = 4;
1192
1193 int text_width = 0;
1194 int text_x = x;
1195 int image_offset = cw - ix - 1;
1196
1197 switch(column.GetAlignment()) {
1198 case wxTL_ALIGN_LEFT:
1199 text_x += EXTRA_WIDTH;
1200 cw -= ix + 2;
1201 break;
1202 case wxTL_ALIGN_RIGHT:
1203 dc.GetTextExtent(column.GetText(), &text_width, NULL);
1204 text_x += cw - text_width - EXTRA_WIDTH;
1205 image_offset = 0;
1206 break;
1207 case wxTL_ALIGN_CENTER:
1208 dc.GetTextExtent(column.GetText(), &text_width, NULL);
1209 text_x += (cw - text_width)/2 + ix + 2;
1210 image_offset = (cw - text_width - ix - 2)/2;
1211 break;
1212 }
1213
1214 // draw the image
1215 if(image != -1 && imageList) {
1216 imageList->Draw(image, dc, x + image_offset/*cw - ix - 1*/,
1217 HEADER_OFFSET_Y + (h - 4 - iy)/2,
1218 wxIMAGELIST_DRAW_TRANSPARENT);
1219 }
1220
1221 // draw the text clipping it so that it doesn't overwrite the column
1222 // boundary
1223 wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 );
1224
1225 dc.DrawText( column.GetText(),
1226 text_x, HEADER_OFFSET_Y + EXTRA_HEIGHT );
1227
1228 x += wCol;
1229 }
1230
1231 dc.EndDrawing();
1232}
1233
1234void wxTreeListHeaderWindow::DrawCurrent()
1235{
1236 int x1 = m_currentX;
1237 int y1 = 0;
1238 ClientToScreen( &x1, &y1 );
1239
1240 int x2 = m_currentX-1;
1241#ifdef __WXMSW__
1242 ++x2; // but why ?
1243#endif
1244 int y2 = 0;
1245 m_owner->GetClientSize( NULL, &y2 );
1246 m_owner->ClientToScreen( &x2, &y2 );
1247
1248 wxScreenDC dc;
1249 dc.SetLogicalFunction( wxINVERT );
1250 dc.SetPen( wxPen( *wxBLACK, 2, wxSOLID ) );
1251 dc.SetBrush( *wxTRANSPARENT_BRUSH );
1252
1253 AdjustDC(dc);
1254
1255 dc.DrawLine( x1, y1, x2, y2 );
1256
1257 dc.SetLogicalFunction( wxCOPY );
1258
1259 dc.SetPen( wxNullPen );
1260 dc.SetBrush( wxNullBrush );
1261}
1262
1263void wxTreeListHeaderWindow::OnMouse( wxMouseEvent &event )
1264{
1265 // we want to work with logical coords
1266 int x;
1267 m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL);
1268 int y = event.GetY();
1269
1270 if (m_isDragging)
1271 {
1272 SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING,
1273 event.GetPosition());
1274
1275 // we don't draw the line beyond our window, but we allow dragging it
1276 // there
1277 int w = 0;
1278 GetClientSize( &w, NULL );
1279 m_owner->CalcUnscrolledPosition(w, 0, &w, NULL);
1280 w -= 6;
1281
1282 // erase the line if it was drawn
1283 if ( m_currentX < w )
1284 DrawCurrent();
1285
1286 if (event.ButtonUp())
1287 {
1288 ReleaseMouse();
1289 m_isDragging = FALSE;
1290 m_dirty = TRUE;
1291 SetColumnWidth( m_column, m_currentX - m_minX );
1292 Refresh();
1293 SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG,
1294 event.GetPosition());
1295 }
1296 else
1297 {
1298 if (x > m_minX + 7)
1299 m_currentX = x;
1300 else
1301 m_currentX = m_minX + 7;
1302
1303 // draw in the new location
1304 if ( m_currentX < w )
1305 DrawCurrent();
1306 }
1307 }
1308 else // not dragging
1309 {
1310 m_minX = 0;
1311 bool hit_border = FALSE;
1312
1313 // end of the current column
1314 int xpos = 0;
1315
1316 // find the column where this event occured
1317 int countCol = GetColumnCount();
1318 for (int col = 0; col < countCol; col++)
1319 {
1320 xpos += GetColumnWidth( col );
1321 m_column = col;
1322
1323 if ( (abs(x-xpos) < 3) && (y < 22) )
1324 {
1325 // near the column border
1326 hit_border = TRUE;
1327 break;
1328 }
1329
1330 if ( x < xpos )
1331 {
1332 // inside the column
1333 break;
1334 }
1335
1336 m_minX = xpos;
1337 }
1338
1339 if (event.LeftDown() || event.RightUp())
1340 {
1341 if (hit_border && event.LeftDown())
1342 {
1343 m_isDragging = TRUE;
1344 m_currentX = x;
1345 DrawCurrent();
1346 CaptureMouse();
1347 SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
1348 event.GetPosition());
1349 }
1350 else // click on a column
1351 {
1352 SendListEvent( event.LeftDown()
1353 ? wxEVT_COMMAND_LIST_COL_CLICK
1354 : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK,
1355 event.GetPosition());
1356 }
1357 }
1358 else if (event.Moving())
1359 {
1360 bool setCursor;
1361 if (hit_border)
1362 {
1363 setCursor = m_currentCursor == wxSTANDARD_CURSOR;
1364 m_currentCursor = m_resizeCursor;
1365 }
1366 else
1367 {
1368 setCursor = m_currentCursor != wxSTANDARD_CURSOR;
1369 m_currentCursor = wxSTANDARD_CURSOR;
1370 }
1371
1372 if ( setCursor )
1373 SetCursor(*m_currentCursor);
1374 }
1375 }
1376}
1377
1378void wxTreeListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1379{
1380 m_owner->SetFocus();
1381}
1382
1383void wxTreeListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos)
1384{
1385 wxWindow *parent = GetParent();
1386 wxListEvent le( type, parent->GetId() );
1387 le.SetEventObject( parent );
1388 le.m_pointDrag = pos;
1389
1390 // the position should be relative to the parent window, not
1391 // this one for compatibility with MSW and common sense: the
1392 // user code doesn't know anything at all about this header
1393 // window, so why should it get positions relative to it?
1394 le.m_pointDrag.y -= GetSize().y;
1395
1396 le.m_col = m_column;
1397 parent->GetEventHandler()->ProcessEvent( le );
1398}
1399
1400inline
1401void wxTreeListHeaderWindow::AddColumn(const wxTreeListColumnInfo& col)
1402{
1403 m_columns.Add(col);
1404 m_total_col_width += col.GetWidth();
1405 //m_owner->GetHeaderWindow()->Refresh();
1406 //m_dirty = TRUE;
1407 m_owner->AdjustMyScrollbars();
1408 m_owner->m_dirty = TRUE;
1409 Refresh();
1410}
1411
1412inline
1413void wxTreeListHeaderWindow::SetColumnWidth(size_t column, size_t width)
1414{
1415 if(column < GetColumnCount()) {
1416 m_total_col_width -= m_columns[column].GetWidth();
1417 m_columns[column].SetWidth(width);
1418 m_total_col_width += width;
1419 m_owner->AdjustMyScrollbars();
1420 m_owner->m_dirty = TRUE;
1421 //m_dirty = TRUE;
1422 Refresh();
1423 }
1424}
1425
1426
1427inline
1428void wxTreeListHeaderWindow::InsertColumn(size_t before,
1429 const wxTreeListColumnInfo& col)
1430{
1431 wxCHECK_RET(before < GetColumnCount(), wxT("Invalid column index"));
1432 m_columns.Insert(col, before);
1433 m_total_col_width += col.GetWidth();
1434 //m_dirty = TRUE;
1435 //m_owner->GetHeaderWindow()->Refresh();
1436 m_owner->AdjustMyScrollbars();
1437 m_owner->m_dirty = TRUE;
1438 Refresh();
1439}
1440
1441inline
1442void wxTreeListHeaderWindow::RemoveColumn(size_t column)
1443{
1444 wxCHECK_RET(column < GetColumnCount(), wxT("Invalid column"));
1445 m_total_col_width -= m_columns[column].GetWidth();
1446 m_columns.RemoveAt(column);
1447 //m_dirty = TRUE;
1448 m_owner->AdjustMyScrollbars();
1449 m_owner->m_dirty = TRUE;
1450 Refresh();
1451}
1452
1453inline
1454void wxTreeListHeaderWindow::SetColumn(size_t column,
1455 const wxTreeListColumnInfo& info)
1456{
1457 wxCHECK_RET(column < GetColumnCount(), wxT("Invalid column"));
1458 size_t w = m_columns[column].GetWidth();
1459 m_columns[column] = info;
1460 //m_owner->GetHeaderWindow()->Refresh();
1461 //m_dirty = TRUE;
1462 if(w != info.GetWidth()) {
1463 m_total_col_width += info.GetWidth() - w;
1464 m_owner->AdjustMyScrollbars();
1465 m_owner->m_dirty = TRUE;
1466 }
1467 Refresh();
1468}
1469
1470// ---------------------------------------------------------------------------
1471// wxTreeListItem
1472// ---------------------------------------------------------------------------
1473
1474wxTreeListItem::wxTreeListItem(wxTreeListMainWindow *owner,
1475 wxTreeListItem *parent,
1476 const wxArrayString& text,
1477 int image, int selImage,
1478 wxTreeItemData *data)
1479 : m_text(text)
1480{
1481 m_images[wxTreeItemIcon_Normal] = image;
1482 m_images[wxTreeItemIcon_Selected] = selImage;
1483 m_images[wxTreeItemIcon_Expanded] = NO_IMAGE;
1484 m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE;
1485
1486 m_data = data;
1487 m_x = m_y = 0;
1488
1489 m_isCollapsed = TRUE;
1490 m_hasHilight = FALSE;
1491 m_hasPlus = FALSE;
1492 m_isBold = FALSE;
1493
1494 m_owner = owner;
1495
1496 m_parent = parent;
1497
1498 m_attr = (wxTreeItemAttr *)NULL;
1499 m_ownsAttr = FALSE;
1500
1501 // We don't know the height here yet.
1502 m_width = 0;
1503 m_height = 0;
1504}
1505
1506wxTreeListItem::~wxTreeListItem()
1507{
1508 delete m_data;
1509
1510 if (m_ownsAttr) delete m_attr;
1511
1512 wxASSERT_MSG( m_children.IsEmpty(),
1513 wxT("please call DeleteChildren() before deleting the item") );
1514}
1515
1516void wxTreeListItem::DeleteChildren(wxTreeListMainWindow *tree)
1517{
1518 size_t count = m_children.Count();
1519 for ( size_t n = 0; n < count; n++ )
1520 {
1521 wxTreeListItem *child = m_children[n];
1522 if (tree)
1523 tree->SendDeleteEvent(child);
1524
1525 child->DeleteChildren(tree);
1526 delete child;
1527 }
1528
1529 m_children.Empty();
1530}
1531
1532void wxTreeListItem::SetText( const wxString &text )
1533{
1534 if(m_text.GetCount() > 0) m_text[0] = text;
1535 else {
1536 m_text.Add(text);
1537 }
1538}
1539
1540size_t wxTreeListItem::GetChildrenCount(bool recursively) const
1541{
1542 size_t count = m_children.Count();
1543 if ( !recursively )
1544 return count;
1545
1546 size_t total = count;
1547 for (size_t n = 0; n < count; ++n)
1548 {
1549 total += m_children[n]->GetChildrenCount();
1550 }
1551
1552 return total;
1553}
1554
1555void wxTreeListItem::GetSize( int &x, int &y,
1556 const wxTreeListMainWindow *theButton )
1557{
1558 int bottomY=m_y+theButton->GetLineHeight(this);
1559 if ( y < bottomY ) y = bottomY;
1560 int width = m_x + m_width;
1561 if ( x < width ) x = width;
1562
1563 if (IsExpanded())
1564 {
1565 size_t count = m_children.Count();
1566 for ( size_t n = 0; n < count; ++n )
1567 {
1568 m_children[n]->GetSize( x, y, theButton );
1569 }
1570 }
1571}
1572
1573wxTreeListItem *wxTreeListItem::HitTest(const wxPoint& point,
1574 const wxTreeListMainWindow *theCtrl,
1575 int &flags,
1576 int level)
1577{
1578 // for a hidden root node, don't evaluate it, but do evaluate children
1579 if ( !(level == 0 && theCtrl->HasFlag(wxTR_HIDE_ROOT)) )
1580 {
1581 // evaluate the item
1582 int h = theCtrl->GetLineHeight(this);
1583 if ((point.y > m_y) && (point.y < m_y + h))
1584 {
1585 int y_mid = m_y + h/2;
1586 if (point.y < y_mid )
1587 flags |= wxTREE_HITTEST_ONITEMUPPERPART;
1588 else
1589 flags |= wxTREE_HITTEST_ONITEMLOWERPART;
1590
1591 // 5 is the size of the plus sign
1592 int xCross = m_x - theCtrl->GetSpacing();
1593 if ((point.x > xCross-5) && (point.x < xCross+5) &&
1594 (point.y > y_mid-5) && (point.y < y_mid+5) &&
1595 HasPlus() && theCtrl->HasButtons() )
1596 {
1597 flags |= wxTREE_HITTEST_ONITEMBUTTON;
1598 return this;
1599 }
1600
1601 if ((point.x >= m_x) && (point.x <= m_x+m_width))
1602 {
1603 int image_w = -1;
1604 int image_h;
1605
1606 //assuming every image (normal and selected) has the same size!
1607 if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal )
1608 theCtrl->m_imageListNormal->GetSize(GetImage(),
1609 image_w, image_h);
1610
1611 if ((image_w != -1) && (point.x <= m_x + image_w + 1))
1612 flags |= wxTREE_HITTEST_ONITEMICON;
1613 else
1614 flags |= wxTREE_HITTEST_ONITEMLABEL;
1615
1616 return this;
1617 }
1618
1619 if (point.x < m_x)
1620 flags |= wxTREE_HITTEST_ONITEMINDENT;
1621 if (point.x > m_x+m_width)
1622 flags |= wxTREE_HITTEST_ONITEMRIGHT;
1623
1624 return this;
1625 }
1626
1627 // if children are expanded, fall through to evaluate them
1628 if (m_isCollapsed) return (wxTreeListItem*) NULL;
1629 }
1630
1631 // evaluate children
1632 size_t count = m_children.Count();
1633 for ( size_t n = 0; n < count; n++ )
1634 {
1635 wxTreeListItem *res = m_children[n]->HitTest(point, theCtrl,
1636 flags, level + 1);
1637 if ( res != NULL )
1638 return res;
1639 }
1640
1641 return (wxTreeListItem*) NULL;
1642}
1643
1644// ALB
1645wxTreeListItem *wxTreeListItem::HitTest(const wxPoint& point,
1646 const wxTreeListMainWindow *theCtrl,
1647 int &flags, int& column, int level)
1648{
1649 column = -1;
1650 wxTreeListItem* res = HitTest(point, theCtrl, flags, level);
1651
1652 if(!res) return res;
1653 if(flags & wxTREE_HITTEST_ONITEMINDENT) {
1654 int x = 0;
1655 for(size_t i = 0; i < theCtrl->GetMainColumn(); ++i) {
1656 int w = theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
1657 if(point.x >= x && point.x < x+w) {
1658 flags ^= wxTREE_HITTEST_ONITEMINDENT;
1659 flags |= wxTREE_HITTEST_ONITEMCOLUMN;
1660 column = i;
1661 return res;
1662 }
1663 }
1664 }
1665 else if(flags & wxTREE_HITTEST_ONITEMRIGHT) {
1666 int x = 0;
1667 size_t i;
1668 for(i = 0; i < theCtrl->GetMainColumn()+1; ++i) {
1669 x += theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
1670 }
1671 for(i = theCtrl->GetMainColumn()+1;
1672 i < theCtrl->GetColumnCount(); ++i) {
1673 int w = theCtrl->m_owner->GetHeaderWindow()->GetColumnWidth(i);
1674 if(point.x >= x && point.x < x+w) {
1675 flags ^= wxTREE_HITTEST_ONITEMRIGHT;
1676 flags |= wxTREE_HITTEST_ONITEMCOLUMN;
1677 column = i;
1678 return res;
1679 }
1680 }
1681 }
1682
1683 return res;
1684}
1685
1686
1687int wxTreeListItem::GetCurrentImage() const
1688{
1689 int image = NO_IMAGE;
1690 if ( IsExpanded() )
1691 {
1692 if ( IsSelected() )
1693 {
1694 image = GetImage(wxTreeItemIcon_SelectedExpanded);
1695 }
1696
1697 if ( image == NO_IMAGE )
1698 {
1699 // we usually fall back to the normal item, but try just the
1700 // expanded one (and not selected) first in this case
1701 image = GetImage(wxTreeItemIcon_Expanded);
1702 }
1703 }
1704 else // not expanded
1705 {
1706 if ( IsSelected() )
1707 image = GetImage(wxTreeItemIcon_Selected);
1708 }
1709
1710 // maybe it doesn't have the specific image we want,
1711 // try the default one instead
1712 if ( image == NO_IMAGE ) image = GetImage();
1713
1714 return image;
1715}
1716
1717// ---------------------------------------------------------------------------
1718// wxTreeListMainWindow implementation
1719// ---------------------------------------------------------------------------
1720
1721IMPLEMENT_DYNAMIC_CLASS(wxTreeListMainWindow, wxScrolledWindow)
1722
1723BEGIN_EVENT_TABLE(wxTreeListMainWindow, wxScrolledWindow)
1724 EVT_PAINT (wxTreeListMainWindow::OnPaint)
1725 EVT_MOUSE_EVENTS (wxTreeListMainWindow::OnMouse)
1726 EVT_CHAR (wxTreeListMainWindow::OnChar)
1727 EVT_SET_FOCUS (wxTreeListMainWindow::OnSetFocus)
1728 EVT_KILL_FOCUS (wxTreeListMainWindow::OnKillFocus)
1729 EVT_IDLE (wxTreeListMainWindow::OnIdle)
1730 //EVT_SIZE (wxTreeListMainWindow::OnSize)
1731 EVT_SCROLLWIN (wxTreeListMainWindow::OnScroll)
1732END_EVENT_TABLE()
1733
1734
1735// ---------------------------------------------------------------------------
1736// construction/destruction
1737// ---------------------------------------------------------------------------
1738
1739void wxTreeListMainWindow::Init()
1740{
1741 m_current = m_key_current = m_anchor = (wxTreeListItem *) NULL;
1742 m_hasFocus = FALSE;
1743 m_dirty = FALSE;
1744
1745 m_lineHeight = 10;
1746 m_indent = 9;
1747 m_spacing = 9;
1748 m_linespacing = 4;
1749
1750 m_hilightBrush = new wxBrush
1751 (
1752 wxSystemSettings::GetSystemColour
1753 (
1754 wxSYS_COLOUR_HIGHLIGHT
1755 ),
1756 wxSOLID
1757 );
1758
1759 m_hilightUnfocusedBrush = new wxBrush
1760 (
1761 wxSystemSettings::GetSystemColour
1762 (
1763 wxSYS_COLOUR_BTNSHADOW
1764 ),
1765 wxSOLID
1766 );
1767
1768 m_imageListNormal = m_imageListButtons =
1769 m_imageListState = (wxImageList *) NULL;
1770 m_ownsImageListNormal = m_ownsImageListButtons =
1771 m_ownsImageListState = FALSE;
1772
1773 m_dragCount = 0;
1774 m_isDragging = FALSE;
1775 m_dropTarget = m_oldSelection = (wxTreeListItem *)NULL;
1776
1777 m_renameTimer = new wxTreeListRenameTimer( this );
1778 m_lastOnSame = FALSE;
1779
1780 m_normalFont = wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT );
1781 m_boldFont = wxFont( m_normalFont.GetPointSize(),
1782 m_normalFont.GetFamily(),
1783 m_normalFont.GetStyle(),
1784 wxBOLD,
1785 m_normalFont.GetUnderlined());
1786}
1787
1788
1789static const int HEADER_HEIGHT = 23;
1790
1791bool wxTreeListMainWindow::Create(wxTreeListCtrl *parent,
1792 wxWindowID id,
1793 const wxPoint& pos,
1794 const wxSize& size,
1795 long style,
1796 const wxValidator &validator,
1797 const wxString& name )
1798{
1799#ifdef __WXMAC__
1800 int major,minor;
1801 wxGetOsVersion( &major, &minor );
1802
1803 if (style & wxTR_HAS_BUTTONS) style |= wxTR_MAC_BUTTONS;
1804 if (style & wxTR_HAS_BUTTONS) style &= ~wxTR_HAS_BUTTONS;
1805 style &= ~wxTR_LINES_AT_ROOT;
1806 style |= wxTR_NO_LINES;
1807 if (major < 10)
1808 style |= wxTR_ROW_LINES;
1809#endif
1810
1811 wxScrolledWindow::Create( parent, id, pos, size,
1812 style|wxHSCROLL|wxVSCROLL, name );
1813
1814 // If the tree display has no buttons, but does have
1815 // connecting lines, we can use a narrower layout.
1816 // It may not be a good idea to force this...
1817 if (!HasButtons() && !HasFlag(wxTR_NO_LINES))
1818 {
1819 m_indent= 10;
1820 m_spacing = 10;
1821 }
1822
1823#if wxUSE_VALIDATORS
1824 SetValidator( validator );
1825#endif
1826
1827 SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
1828
1829// #ifdef __WXMSW__
1830// m_dottedPen = wxPen( "black", 0, wxDOT ); // too slow under XFree86
1831// #else
1832 m_dottedPen = wxPen( wxT("grey"), 0, 0 );
1833// #endif
1834
1835 // ALB
1836 m_owner = parent;
1837 m_main_column = 0;
1838
1839 return TRUE;
1840}
1841
1842wxTreeListMainWindow::~wxTreeListMainWindow()
1843{
1844 delete m_hilightBrush;
1845 delete m_hilightUnfocusedBrush;
1846
1847 DeleteAllItems();
1848
1849 delete m_renameTimer;
1850 if (m_ownsImageListNormal) delete m_imageListNormal;
1851 if (m_ownsImageListState) delete m_imageListState;
1852 if (m_ownsImageListButtons) delete m_imageListButtons;
1853}
1854
1855
1856
1857//-----------------------------------------------------------------------------
1858// accessors
1859//-----------------------------------------------------------------------------
1860
1861inline
1862size_t wxTreeListMainWindow::GetCount() const
1863{
1864 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
1865}
1866
1867inline
1868void wxTreeListMainWindow::SetIndent(unsigned int indent)
1869{
1870 m_indent = indent;
1871 m_dirty = TRUE;
1872}
1873
1874inline
1875void wxTreeListMainWindow::SetSpacing(unsigned int spacing)
1876{
1877 m_spacing = spacing;
1878 m_dirty = TRUE;
1879}
1880
1881inline
1882void wxTreeListMainWindow::SetLineSpacing(unsigned int spacing)
1883{
1884 m_linespacing = spacing;
1885 m_dirty = TRUE;
1886 CalculateLineHeight();
1887}
1888
1889inline
1890size_t wxTreeListMainWindow::GetChildrenCount(const wxTreeItemId& item,
1891 bool recursively)
1892{
1893 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
1894
1895 return ((wxTreeListItem*) item.m_pItem)->GetChildrenCount(recursively);
1896}
1897
1898void wxTreeListMainWindow::SetWindowStyle(const long styles)
1899{
1900 // right now, just sets the styles. Eventually, we may
1901 // want to update the inherited styles, but right now
1902 // none of the parents has updatable styles
1903 m_windowStyle = styles;
1904 m_dirty = TRUE;
1905}
1906
1907//-----------------------------------------------------------------------------
1908// functions to work with tree items
1909//-----------------------------------------------------------------------------
1910
1911inline
1912int wxTreeListMainWindow::GetItemImage(const wxTreeItemId& item, size_t column,
1913 wxTreeItemIcon which) const
1914{
1915 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
1916
1917 return ((wxTreeListItem*) item.m_pItem)->GetImage(column, which);
1918}
1919
1920inline
1921wxTreeItemData *wxTreeListMainWindow::GetItemData(const wxTreeItemId& item)
1922 const
1923{
1924 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
1925
1926 return ((wxTreeListItem*) item.m_pItem)->GetData();
1927}
1928
1929inline
1930bool wxTreeListMainWindow::GetItemBold(const wxTreeItemId& item) const
1931{
1932 wxCHECK_MSG(item.IsOk(), FALSE, wxT("invalid tree item"));
1933 return ((wxTreeListItem *)item.m_pItem)->IsBold();
1934}
1935
1936inline
1937wxColour wxTreeListMainWindow::GetItemTextColour(const wxTreeItemId& item)
1938 const
1939{
1940 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1941
1942 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
1943 return pItem->Attr().GetTextColour();
1944}
1945
1946inline
1947wxColour wxTreeListMainWindow::GetItemBackgroundColour(
1948 const wxTreeItemId& item) const
1949{
1950 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1951
1952 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
1953 return pItem->Attr().GetBackgroundColour();
1954}
1955
1956inline
1957wxFont wxTreeListMainWindow::GetItemFont(const wxTreeItemId& item) const
1958{
1959 wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
1960
1961 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
1962 return pItem->Attr().GetFont();
1963}
1964
1965
1966
1967inline
1968void wxTreeListMainWindow::SetItemImage(const wxTreeItemId& item,
1969 size_t column,
1970 int image, wxTreeItemIcon which)
1971{
1972 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1973
1974 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
1975 pItem->SetImage(column, image, which);
1976
1977 wxClientDC dc(this);
1978 CalculateSize(pItem, dc);
1979 RefreshLine(pItem);
1980}
1981
1982inline
1983void wxTreeListMainWindow::SetItemData(const wxTreeItemId& item,
1984 wxTreeItemData *data)
1985{
1986 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1987
1988 ((wxTreeListItem*) item.m_pItem)->SetData(data);
1989}
1990
1991inline
1992void wxTreeListMainWindow::SetItemHasChildren(const wxTreeItemId& item,
1993 bool has)
1994{
1995 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1996
1997 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
1998 pItem->SetHasPlus(has);
1999 RefreshLine(pItem);
2000}
2001
2002inline
2003void wxTreeListMainWindow::SetItemBold(const wxTreeItemId& item, bool bold)
2004{
2005 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2006
2007 // avoid redrawing the tree if no real change
2008 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
2009 if ( pItem->IsBold() != bold )
2010 {
2011 pItem->SetBold(bold);
2012 RefreshLine(pItem);
2013 }
2014}
2015
2016inline
2017void wxTreeListMainWindow::SetItemTextColour(const wxTreeItemId& item,
2018 const wxColour& col)
2019{
2020 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2021
2022 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
2023 pItem->Attr().SetTextColour(col);
2024 RefreshLine(pItem);
2025}
2026
2027inline
2028void wxTreeListMainWindow::SetItemBackgroundColour(const wxTreeItemId& item,
2029 const wxColour& col)
2030{
2031 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2032
2033 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
2034 pItem->Attr().SetBackgroundColour(col);
2035 RefreshLine(pItem);
2036}
2037
2038inline
2039void wxTreeListMainWindow::SetItemFont(const wxTreeItemId& item,
2040 const wxFont& font)
2041{
2042 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2043
2044 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
2045 pItem->Attr().SetFont(font);
2046 RefreshLine(pItem);
2047}
2048
2049inline
2050bool wxTreeListMainWindow::SetFont( const wxFont &font )
2051{
2052 wxScrolledWindow::SetFont(font);
2053
2054 m_normalFont = font ;
2055 m_boldFont = wxFont( m_normalFont.GetPointSize(),
2056 m_normalFont.GetFamily(),
2057 m_normalFont.GetStyle(),
2058 wxBOLD,
2059 m_normalFont.GetUnderlined());
2060
2061 return TRUE;
2062}
2063
2064
2065// ----------------------------------------------------------------------------
2066// item status inquiries
2067// ----------------------------------------------------------------------------
2068
2069inline
2070bool wxTreeListMainWindow::IsVisible(const wxTreeItemId& item) const
2071{
2072 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
2073
2074 // An item is only visible if it's not a descendant of a collapsed item
2075 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
2076 wxTreeListItem* parent = pItem->GetParent();
2077 while (parent)
2078 {
2079 if (!parent->IsExpanded())
2080 return FALSE;
2081 parent = parent->GetParent();
2082 }
2083
2084 int startX, startY;
2085 GetViewStart(& startX, & startY);
2086
2087 wxSize clientSize = GetClientSize();
2088
2089 wxRect rect;
2090 if (!GetBoundingRect(item, rect))
2091 return FALSE;
2092 if (rect.GetWidth() == 0 || rect.GetHeight() == 0)
2093 return FALSE;
2094 if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y)
2095 return FALSE;
2096 if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x)
2097 return FALSE;
2098
2099 return TRUE;
2100}
2101
2102inline
2103bool wxTreeListMainWindow::ItemHasChildren(const wxTreeItemId& item) const
2104{
2105 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
2106
2107 // consider that the item does have children if it has the "+" button: it
2108 // might not have them (if it had never been expanded yet) but then it
2109 // could have them as well and it's better to err on this side rather than
2110 // disabling some operations which are restricted to the items with
2111 // children for an item which does have them
2112 return ((wxTreeListItem*) item.m_pItem)->HasPlus();
2113}
2114
2115inline
2116bool wxTreeListMainWindow::IsExpanded(const wxTreeItemId& item) const
2117{
2118 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
2119
2120 return ((wxTreeListItem*) item.m_pItem)->IsExpanded();
2121}
2122
2123inline
2124bool wxTreeListMainWindow::IsSelected(const wxTreeItemId& item) const
2125{
2126 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
2127
2128 return ((wxTreeListItem*) item.m_pItem)->IsSelected();
2129}
2130
2131inline
2132bool wxTreeListMainWindow::IsBold(const wxTreeItemId& item) const
2133{
2134 wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
2135
2136 return ((wxTreeListItem*) item.m_pItem)->IsBold();
2137}
2138
2139// ----------------------------------------------------------------------------
2140// navigation
2141// ----------------------------------------------------------------------------
2142
2143inline
2144wxTreeItemId wxTreeListMainWindow::GetParent(const wxTreeItemId& item) const
2145{
2146 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2147
2148 return ((wxTreeListItem*) item.m_pItem)->GetParent();
2149}
2150
2151inline
2152wxTreeItemId wxTreeListMainWindow::GetFirstChild(const wxTreeItemId& item,
2153 long& cookie) const
2154{
2155 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2156
2157 cookie = 0;
2158 return GetNextChild(item, cookie);
2159}
2160
2161inline
2162wxTreeItemId wxTreeListMainWindow::GetNextChild(const wxTreeItemId& item,
2163 long& cookie) const
2164{
2165 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2166
2167 wxArrayTreeListItems& children = ((wxTreeListItem*)
2168 item.m_pItem)->GetChildren();
2169 if ( (size_t)cookie < children.Count() )
2170 {
2171 return children.Item((size_t)cookie++);
2172 }
2173 else
2174 {
2175 // there are no more of them
2176 return wxTreeItemId();
2177 }
2178}
2179
2180inline
2181wxTreeItemId wxTreeListMainWindow::GetLastChild(const wxTreeItemId& item) const
2182{
2183 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2184
2185 wxArrayTreeListItems& children = ((wxTreeListItem*) item.m_pItem)->GetChildren();
2186 return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
2187}
2188
2189inline
2190wxTreeItemId wxTreeListMainWindow::GetNextSibling(const wxTreeItemId& item) const
2191{
2192 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2193
2194 wxTreeListItem *i = (wxTreeListItem*) item.m_pItem;
2195 wxTreeListItem *parent = i->GetParent();
2196 if ( parent == NULL )
2197 {
2198 // root item doesn't have any siblings
2199 return wxTreeItemId();
2200 }
2201
2202 wxArrayTreeListItems& siblings = parent->GetChildren();
2203 int index = siblings.Index(i);
2204 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
2205
2206 size_t n = (size_t)(index + 1);
2207 return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
2208}
2209
2210inline
2211wxTreeItemId wxTreeListMainWindow::GetPrevSibling(const wxTreeItemId& item)
2212 const
2213{
2214 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2215
2216 wxTreeListItem *i = (wxTreeListItem*) item.m_pItem;
2217 wxTreeListItem *parent = i->GetParent();
2218 if ( parent == NULL )
2219 {
2220 // root item doesn't have any siblings
2221 return wxTreeItemId();
2222 }
2223
2224 wxArrayTreeListItems& siblings = parent->GetChildren();
2225 int index = siblings.Index(i);
2226 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
2227
2228 return index == 0 ? wxTreeItemId()
2229 : wxTreeItemId(siblings[(size_t)(index - 1)]);
2230}
2231
2232// Only for internal use right now, but should probably be public
2233wxTreeItemId wxTreeListMainWindow::GetNext(const wxTreeItemId& item) const
2234{
2235 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2236
2237 wxTreeListItem *i = (wxTreeListItem*) item.m_pItem;
2238
2239 // First see if there are any children.
2240 wxArrayTreeListItems& children = i->GetChildren();
2241 if (children.GetCount() > 0)
2242 {
2243 return children.Item(0);
2244 }
2245 else
2246 {
2247 // Try a sibling of this or ancestor instead
2248 wxTreeItemId p = item;
2249 wxTreeItemId toFind;
2250 do
2251 {
2252 toFind = GetNextSibling(p);
2253 p = GetParent(p);
2254 } while (p.IsOk() && !toFind.IsOk());
2255 return toFind;
2256 }
2257}
2258
2259inline
2260wxTreeItemId wxTreeListMainWindow::GetFirstVisibleItem() const
2261{
2262 wxTreeItemId id = GetRootItem();
2263 if (!id.IsOk())
2264 return id;
2265
2266 do
2267 {
2268 if (IsVisible(id))
2269 return id;
2270 id = GetNext(id);
2271 } while (id.IsOk());
2272
2273 return wxTreeItemId();
2274}
2275
2276inline
2277wxTreeItemId wxTreeListMainWindow::GetNextVisible(const wxTreeItemId& item)
2278 const
2279{
2280 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2281
2282 wxTreeItemId id = item;
2283 if (id.IsOk())
2284 {
2285 while (id = GetNext(id), id.IsOk())
2286 {
2287 if (IsVisible(id))
2288 return id;
2289 }
2290 }
2291 return wxTreeItemId();
2292}
2293
2294inline
2295wxTreeItemId wxTreeListMainWindow::GetPrevVisible(const wxTreeItemId& item)
2296 const
2297{
2298 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
2299
2300 wxFAIL_MSG(wxT("not implemented"));
2301
2302 return wxTreeItemId();
2303}
2304
2305// ----------------------------------------------------------------------------
2306// operations
2307// ----------------------------------------------------------------------------
2308
2309wxTreeItemId wxTreeListMainWindow::DoInsertItem(const wxTreeItemId& parentId,
2310 size_t previous,
2311 const wxString& text,
2312 int image, int selImage,
2313 wxTreeItemData *data)
2314{
2315 wxTreeListItem *parent = (wxTreeListItem*) parentId.m_pItem;
2316 if ( !parent )
2317 {
2318 // should we give a warning here?
2319 return AddRoot(text, image, selImage, data);
2320 }
2321
2322 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
2323
2324 // ALB
2325 wxArrayString arr;
2326 arr.Alloc(GetColumnCount());
2327 for(size_t i = 0; i < GetColumnCount(); ++i) {
2328 arr.Add(wxEmptyString);
2329 }
2330 arr[m_main_column] = text;
2331 wxTreeListItem *item =
2332 new wxTreeListItem( this, parent, arr, image, selImage, data );
2333
2334 if ( data != NULL )
2335 {
2336 data->SetId((long)item);
2337 }
2338
2339 parent->Insert( item, previous );
2340
2341 return item;
2342}
2343
2344wxTreeItemId wxTreeListMainWindow::AddRoot(const wxString& text,
2345 int image, int selImage,
2346 wxTreeItemData *data)
2347{
2348 wxCHECK_MSG(!m_anchor, wxTreeItemId(), wxT("tree can have only one root"));
2349 wxCHECK_MSG(GetColumnCount(), wxTreeItemId(), wxT("Add column(s) before adding the root item"));
2350
2351 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
2352
2353 // ALB
2354 wxArrayString arr;
2355 arr.Alloc(GetColumnCount());
2356 for(size_t i = 0; i < GetColumnCount(); ++i) {
2357 arr.Add(wxEmptyString);
2358 }
2359 arr[m_main_column] = text;
2360 m_anchor = new wxTreeListItem( this, (wxTreeListItem *)NULL, arr,
2361 image, selImage, data);
2362 if (HasFlag(wxTR_HIDE_ROOT))
2363 {
2364 // if root is hidden, make sure we can navigate
2365 // into children
2366 m_anchor->SetHasPlus();
2367 Expand(m_anchor);
2368 }
2369 if ( data != NULL )
2370 {
2371 data->SetId((long)m_anchor);
2372 }
2373
2374 if (!HasFlag(wxTR_MULTIPLE))
2375 {
2376 m_current = m_key_current = m_anchor;
2377 m_current->SetHilight( TRUE );
2378 }
2379
2380 return m_anchor;
2381}
2382
2383inline
2384wxTreeItemId wxTreeListMainWindow::PrependItem(const wxTreeItemId& parent,
2385 const wxString& text,
2386 int image, int selImage,
2387 wxTreeItemData *data)
2388{
2389 return DoInsertItem(parent, 0u, text, image, selImage, data);
2390}
2391
2392inline
2393wxTreeItemId wxTreeListMainWindow::InsertItem(const wxTreeItemId& parentId,
2394 const wxTreeItemId& idPrevious,
2395 const wxString& text,
2396 int image, int selImage,
2397 wxTreeItemData *data)
2398{
2399 wxTreeListItem *parent = (wxTreeListItem*) parentId.m_pItem;
2400 if ( !parent )
2401 {
2402 // should we give a warning here?
2403 return AddRoot(text, image, selImage, data);
2404 }
2405
2406 int index = parent->GetChildren().Index((wxTreeListItem*) idPrevious.m_pItem);
2407 wxASSERT_MSG( index != wxNOT_FOUND,
2408 wxT("previous item in wxTreeListMainWindow::InsertItem() is not a sibling") );
2409
2410 return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
2411}
2412
2413inline
2414wxTreeItemId wxTreeListMainWindow::InsertItem(const wxTreeItemId& parentId,
2415 size_t before,
2416 const wxString& text,
2417 int image, int selImage,
2418 wxTreeItemData *data)
2419{
2420 wxTreeListItem *parent = (wxTreeListItem*) parentId.m_pItem;
2421 if ( !parent )
2422 {
2423 // should we give a warning here?
2424 return AddRoot(text, image, selImage, data);
2425 }
2426
2427 return DoInsertItem(parentId, before, text, image, selImage, data);
2428}
2429
2430inline
2431wxTreeItemId wxTreeListMainWindow::AppendItem(const wxTreeItemId& parentId,
2432 const wxString& text,
2433 int image, int selImage,
2434 wxTreeItemData *data)
2435{
2436 wxTreeListItem *parent = (wxTreeListItem*) parentId.m_pItem;
2437 if ( !parent )
2438 {
2439 // should we give a warning here?
2440 return AddRoot(text, image, selImage, data);
2441 }
2442
2443 return DoInsertItem( parent, parent->GetChildren().Count(), text,
2444 image, selImage, data);
2445}
2446
2447void wxTreeListMainWindow::SendDeleteEvent(wxTreeListItem *item)
2448{
2449 wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, m_owner->GetId() );
2450 event.SetItem((long) item);
2451 event.SetEventObject( /*this*/m_owner );
2452 m_owner->ProcessEvent( event );
2453}
2454
2455inline
2456void wxTreeListMainWindow::DeleteChildren(const wxTreeItemId& itemId)
2457{
2458 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
2459
2460 wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem;
2461 item->DeleteChildren(this);
2462}
2463
2464inline
2465void wxTreeListMainWindow::Delete(const wxTreeItemId& itemId)
2466{
2467 m_dirty = TRUE; // do this first so stuff below doesn't cause flicker
2468
2469 wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem;
2470
2471 // don't stay with invalid m_key_current or we will crash in
2472 // the next call to OnChar()
2473 bool changeKeyCurrent = FALSE;
2474 wxTreeListItem *itemKey = m_key_current;
2475 while ( itemKey )
2476 {
2477 if ( itemKey == item )
2478 {
2479 // m_key_current is a descendant of the item being deleted
2480 changeKeyCurrent = TRUE;
2481 break;
2482 }
2483 itemKey = itemKey->GetParent();
2484 }
2485
2486 wxTreeListItem *parent = item->GetParent();
2487 if ( parent )
2488 {
2489 parent->GetChildren().Remove( item ); // remove by value
2490 }
2491
2492 if ( changeKeyCurrent )
2493 {
2494 // may be NULL or not
2495 m_key_current = parent;
2496 }
2497
2498 item->DeleteChildren(this);
2499 SendDeleteEvent(item);
2500 delete item;
2501}
2502
2503inline
2504void wxTreeListMainWindow::DeleteAllItems()
2505{
2506 if ( m_anchor )
2507 {
2508 m_dirty = TRUE;
2509
2510 m_anchor->DeleteChildren(this);
2511 delete m_anchor;
2512
2513 m_anchor = NULL;
2514 }
2515}
2516
2517void wxTreeListMainWindow::Expand(const wxTreeItemId& itemId)
2518{
2519 wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem;
2520
2521 wxCHECK_RET( item, _T("invalid item in wxTreeListMainWindow::Expand") );
2522
2523 if ( !item->HasPlus() )
2524 return;
2525
2526 if ( item->IsExpanded() )
2527 return;
2528
2529 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, m_owner->GetId() );
2530 event.SetItem( (long) item );
2531 event.SetEventObject( /*this*/m_owner );
2532
2533 if ( m_owner->ProcessEvent( event ) && !event.IsAllowed() )
2534 {
2535 // cancelled by program
2536 return;
2537 }
2538
2539 item->Expand();
2540 CalculatePositions();
2541
2542 RefreshSubtree(item);
2543
2544 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
2545 ProcessEvent( event );
2546}
2547
2548void wxTreeListMainWindow::ExpandAll(const wxTreeItemId& item)
2549{
2550 Expand(item);
2551 if ( IsExpanded(item) )
2552 {
2553 long cookie;
2554 wxTreeItemId child = GetFirstChild(item, cookie);
2555 while ( child.IsOk() )
2556 {
2557 ExpandAll(child);
2558
2559 child = GetNextChild(item, cookie);
2560 }
2561 }
2562}
2563
2564void wxTreeListMainWindow::Collapse(const wxTreeItemId& itemId)
2565{
2566 wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem;
2567
2568 if ( !item->IsExpanded() )
2569 return;
2570
2571 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, m_owner->GetId() );
2572 event.SetItem( (long) item );
2573 event.SetEventObject( /*this*/m_owner );
2574 if ( m_owner->ProcessEvent( event ) && !event.IsAllowed() )
2575 {
2576 // cancelled by program
2577 return;
2578 }
2579
2580 item->Collapse();
2581
2582#if 0 // TODO why should items be collapsed recursively?
2583 wxArrayTreeListItems& children = item->GetChildren();
2584 size_t count = children.Count();
2585 for ( size_t n = 0; n < count; n++ )
2586 {
2587 Collapse(children[n]);
2588 }
2589#endif
2590
2591 CalculatePositions();
2592
2593 RefreshSubtree(item);
2594
2595 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
2596 ProcessEvent( event );
2597}
2598
2599void wxTreeListMainWindow::CollapseAndReset(const wxTreeItemId& item)
2600{
2601 Collapse(item);
2602 DeleteChildren(item);
2603}
2604
2605void wxTreeListMainWindow::Toggle(const wxTreeItemId& itemId)
2606{
2607 wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem;
2608
2609 if (item->IsExpanded())
2610 Collapse(itemId);
2611 else
2612 Expand(itemId);
2613}
2614
2615void wxTreeListMainWindow::Unselect()
2616{
2617 if (m_current)
2618 {
2619 m_current->SetHilight( FALSE );
2620 RefreshLine( m_current );
2621 }
2622}
2623
2624void wxTreeListMainWindow::UnselectAllChildren(wxTreeListItem *item)
2625{
2626 if (item->IsSelected())
2627 {
2628 item->SetHilight(FALSE);
2629 RefreshLine(item);
2630 }
2631
2632 if (item->HasChildren())
2633 {
2634 wxArrayTreeListItems& children = item->GetChildren();
2635 size_t count = children.Count();
2636 for ( size_t n = 0; n < count; ++n )
2637 {
2638 UnselectAllChildren(children[n]);
2639 }
2640 }
2641}
2642
2643void wxTreeListMainWindow::UnselectAll()
2644{
2645 UnselectAllChildren((wxTreeListItem*) GetRootItem().m_pItem);
2646}
2647
2648// Recursive function !
2649// To stop we must have crt_item<last_item
2650// Algorithm :
2651// Tag all next children, when no more children,
2652// Move to parent (not to tag)
2653// Keep going... if we found last_item, we stop.
2654bool wxTreeListMainWindow::TagNextChildren(wxTreeListItem *crt_item, wxTreeListItem *last_item, bool select)
2655{
2656 wxTreeListItem *parent = crt_item->GetParent();
2657
2658 if (parent == NULL) // This is root item
2659 return TagAllChildrenUntilLast(crt_item, last_item, select);
2660
2661 wxArrayTreeListItems& children = parent->GetChildren();
2662 int index = children.Index(crt_item);
2663 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
2664
2665 size_t count = children.Count();
2666 for (size_t n=(size_t)(index+1); n<count; ++n)
2667 {
2668 if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
2669 }
2670
2671 return TagNextChildren(parent, last_item, select);
2672}
2673
2674bool wxTreeListMainWindow::TagAllChildrenUntilLast(wxTreeListItem *crt_item, wxTreeListItem *last_item, bool select)
2675{
2676 crt_item->SetHilight(select);
2677 RefreshLine(crt_item);
2678
2679 if (crt_item==last_item)
2680 return TRUE;
2681
2682 if (crt_item->HasChildren())
2683 {
2684 wxArrayTreeListItems& children = crt_item->GetChildren();
2685 size_t count = children.Count();
2686 for ( size_t n = 0; n < count; ++n )
2687 {
2688 if (TagAllChildrenUntilLast(children[n], last_item, select))
2689 return TRUE;
2690 }
2691 }
2692
2693 return FALSE;
2694}
2695
2696void wxTreeListMainWindow::SelectItemRange(wxTreeListItem *item1, wxTreeListItem *item2)
2697{
2698 // item2 is not necessary after item1
2699 wxTreeListItem *first=NULL, *last=NULL;
2700
2701 // choice first' and 'last' between item1 and item2
2702 if (item1->GetY()<item2->GetY())
2703 {
2704 first=item1;
2705 last=item2;
2706 }
2707 else
2708 {
2709 first=item2;
2710 last=item1;
2711 }
2712
2713 bool select = m_current->IsSelected();
2714
2715 if ( TagAllChildrenUntilLast(first,last,select) )
2716 return;
2717
2718 TagNextChildren(first,last,select);
2719}
2720
2721void wxTreeListMainWindow::SelectItem(const wxTreeItemId& itemId,
2722 bool unselect_others,
2723 bool extended_select)
2724{
2725 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
2726
2727 bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
2728 wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem;
2729
2730 //wxCHECK_RET( ( (!unselect_others) && is_single),
2731 // wxT("this is a single selection tree") );
2732
2733 // to keep going anyhow !!!
2734 if (is_single)
2735 {
2736 if (item->IsSelected())
2737 return; // nothing to do
2738 unselect_others = TRUE;
2739 extended_select = FALSE;
2740 }
2741 else if ( unselect_others && item->IsSelected() )
2742 {
2743 // selection change if there is more than one item currently selected
2744 wxArrayTreeItemIds selected_items;
2745 if ( GetSelections(selected_items) == 1 )
2746 return;
2747 }
2748
2749 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, m_owner->GetId() );
2750 event.SetItem( (long) item );
2751 event.SetOldItem( (long) m_current );
2752 event.SetEventObject( /*this*/m_owner );
2753 // TODO : Here we don't send any selection mode yet !
2754
2755 if(m_owner->GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed())
2756 return;
2757
2758 wxTreeItemId parent = GetParent( itemId );
2759 while (parent.IsOk())
2760 {
2761 if (!IsExpanded(parent))
2762 Expand( parent );
2763
2764 parent = GetParent( parent );
2765 }
2766
2767 EnsureVisible( itemId );
2768
2769 // ctrl press
2770 if (unselect_others)
2771 {
2772 if (is_single) Unselect(); // to speed up thing
2773 else UnselectAll();
2774 }
2775
2776 // shift press
2777 if (extended_select)
2778 {
2779 if ( !m_current )
2780 {
2781 m_current = m_key_current = (wxTreeListItem*) GetRootItem().m_pItem;
2782 }
2783
2784 // don't change the mark (m_current)
2785 SelectItemRange(m_current, item);
2786 }
2787 else
2788 {
2789 bool select=TRUE; // the default
2790
2791 // Check if we need to toggle hilight (ctrl mode)
2792 if (!unselect_others)
2793 select=!item->IsSelected();
2794
2795 m_current = m_key_current = item;
2796 m_current->SetHilight(select);
2797 RefreshLine( m_current );
2798 }
2799
2800 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
2801 GetEventHandler()->ProcessEvent( event );
2802}
2803
2804void wxTreeListMainWindow::FillArray(wxTreeListItem *item,
2805 wxArrayTreeItemIds &array) const
2806{
2807 if ( item->IsSelected() )
2808 array.Add(wxTreeItemId(item));
2809
2810 if ( item->HasChildren() )
2811 {
2812 wxArrayTreeListItems& children = item->GetChildren();
2813 size_t count = children.GetCount();
2814 for ( size_t n = 0; n < count; ++n )
2815 FillArray(children[n], array);
2816 }
2817}
2818
2819size_t wxTreeListMainWindow::GetSelections(wxArrayTreeItemIds &array) const
2820{
2821 array.Empty();
2822 wxTreeItemId idRoot = GetRootItem();
2823 if ( idRoot.IsOk() )
2824 {
2825 FillArray((wxTreeListItem*) idRoot.m_pItem, array);
2826 }
2827 //else: the tree is empty, so no selections
2828
2829 return array.Count();
2830}
2831
2832void wxTreeListMainWindow::EnsureVisible(const wxTreeItemId& item)
2833{
2834 if (!item.IsOk()) return;
2835
2836 wxTreeListItem *gitem = (wxTreeListItem*) item.m_pItem;
2837
2838 // first expand all parent branches
2839 wxTreeListItem *parent = gitem->GetParent();
2840 while ( parent )
2841 {
2842 Expand(parent);
2843 parent = parent->GetParent();
2844 }
2845
2846 //if (parent) CalculatePositions();
2847
2848 ScrollTo(item);
2849}
2850
2851void wxTreeListMainWindow::ScrollTo(const wxTreeItemId &item)
2852{
2853 if (!item.IsOk()) return;
2854
2855 // We have to call this here because the label in
2856 // question might just have been added and no screen
2857 // update taken place.
2858 if (m_dirty) wxYieldIfNeeded();
2859
2860 wxTreeListItem *gitem = (wxTreeListItem*) item.m_pItem;
2861
2862 // now scroll to the item
2863 int item_y = gitem->GetY();
2864
2865 int start_x = 0;
2866 int start_y = 0;
2867 GetViewStart( &start_x, &start_y );
2868 start_y *= PIXELS_PER_UNIT;
2869
2870 int client_h = 0;
2871 int client_w = 0;
2872 GetClientSize( &client_w, &client_h );
2873
2874 if (item_y < start_y+3)
2875 {
2876 // going down
2877 int x = 0;
2878 int y = 0;
2879 m_anchor->GetSize( x, y, this );
2880 x = m_owner->GetHeaderWindow()->GetWidth(); //m_total_col_width; // ALB
2881 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2882 //x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2883 int x_pos = GetScrollPos( wxHORIZONTAL );
2884 // Item should appear at top
2885 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, item_y/PIXELS_PER_UNIT );
2886 }
2887 else if (item_y+GetLineHeight(gitem) > start_y+client_h)
2888 {
2889 // going up
2890 int x = 0;
2891 int y = 0;
2892 m_anchor->GetSize( x, y, this );
2893 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2894 //x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
2895 x = m_owner->GetHeaderWindow()->GetWidth(); //m_total_col_width; // ALB
2896 item_y += PIXELS_PER_UNIT+2;
2897 int x_pos = GetScrollPos( wxHORIZONTAL );
2898 // Item should appear at bottom
2899 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 );
2900 }
2901}
2902
2903// FIXME: tree sorting functions are not reentrant and not MT-safe!
2904static wxTreeListMainWindow *s_treeBeingSorted = NULL;
2905
2906static int LINKAGEMODE tree_ctrl_compare_func(wxTreeListItem **item1,
2907 wxTreeListItem **item2)
2908{
2909 wxCHECK_MSG( s_treeBeingSorted, 0, wxT("bug in wxTreeListMainWindow::SortChildren()") );
2910
2911 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
2912}
2913
2914int wxTreeListMainWindow::OnCompareItems(const wxTreeItemId& item1,
2915 const wxTreeItemId& item2)
2916{
2917 // ALB: delegate to m_owner, to let the user overrride the comparison
2918 //return wxStrcmp(GetItemText(item1), GetItemText(item2));
2919 return m_owner->OnCompareItems(item1, item2);
2920}
2921
2922void wxTreeListMainWindow::SortChildren(const wxTreeItemId& itemId)
2923{
2924 wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
2925
2926 wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem;
2927
2928 wxCHECK_RET( !s_treeBeingSorted,
2929 wxT("wxTreeListMainWindow::SortChildren is not reentrant") );
2930
2931 wxArrayTreeListItems& children = item->GetChildren();
2932 if ( children.Count() > 1 )
2933 {
2934 m_dirty = TRUE;
2935
2936 s_treeBeingSorted = this;
2937 children.Sort(tree_ctrl_compare_func);
2938 s_treeBeingSorted = NULL;
2939 }
2940 //else: don't make the tree dirty as nothing changed
2941}
2942
2943inline
2944wxImageList *wxTreeListMainWindow::GetImageList() const
2945{
2946 return m_imageListNormal;
2947}
2948
2949inline
2950wxImageList *wxTreeListMainWindow::GetButtonsImageList() const
2951{
2952 return m_imageListButtons;
2953}
2954
2955inline
2956wxImageList *wxTreeListMainWindow::GetStateImageList() const
2957{
2958 return m_imageListState;
2959}
2960
2961void wxTreeListMainWindow::CalculateLineHeight()
2962{
2963 wxClientDC dc(this);
2964 m_lineHeight = (int)(dc.GetCharHeight() + m_linespacing*2);
2965
2966 if ( m_imageListNormal )
2967 {
2968 // Calculate a m_lineHeight value from the normal Image sizes.
2969 // May be toggle off. Then wxTreeListMainWindow will spread when
2970 // necessary (which might look ugly).
2971 int n = m_imageListNormal->GetImageCount();
2972 for (int i = 0; i < n ; i++)
2973 {
2974 int width = 0, height = 0;
2975 m_imageListNormal->GetSize(i, width, height);
2976 if (height > m_lineHeight) m_lineHeight = height;
2977 }
2978 }
2979
2980 if (m_imageListButtons)
2981 {
2982 // Calculate a m_lineHeight value from the Button image sizes.
2983 // May be toggle off. Then wxTreeListMainWindow will spread when
2984 // necessary (which might look ugly).
2985 int n = m_imageListButtons->GetImageCount();
2986 for (int i = 0; i < n ; i++)
2987 {
2988 int width = 0, height = 0;
2989 m_imageListButtons->GetSize(i, width, height);
2990 if (height > m_lineHeight) m_lineHeight = height;
2991 }
2992 }
2993
2994 if (m_lineHeight < 30)
2995 m_lineHeight += 2; // at least 2 pixels
2996 else
2997 m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
2998}
2999
3000inline
3001void wxTreeListMainWindow::SetImageList(wxImageList *imageList)
3002{
3003 if (m_ownsImageListNormal) delete m_imageListNormal;
3004 m_imageListNormal = imageList;
3005 m_ownsImageListNormal = FALSE;
3006 m_dirty = TRUE;
3007 CalculateLineHeight();
3008}
3009
3010inline
3011void wxTreeListMainWindow::SetStateImageList(wxImageList *imageList)
3012{
3013 if (m_ownsImageListState) delete m_imageListState;
3014 m_imageListState = imageList;
3015 m_ownsImageListState = FALSE;
3016}
3017
3018inline
3019void wxTreeListMainWindow::SetButtonsImageList(wxImageList *imageList)
3020{
3021 if (m_ownsImageListButtons) delete m_imageListButtons;
3022 m_imageListButtons = imageList;
3023 m_ownsImageListButtons = FALSE;
3024 m_dirty = TRUE;
3025 CalculateLineHeight();
3026}
3027
3028inline
3029void wxTreeListMainWindow::AssignImageList(wxImageList *imageList)
3030{
3031 SetImageList(imageList);
3032 m_ownsImageListNormal = TRUE;
3033}
3034
3035inline
3036void wxTreeListMainWindow::AssignStateImageList(wxImageList *imageList)
3037{
3038 SetStateImageList(imageList);
3039 m_ownsImageListState = TRUE;
3040}
3041
3042inline
3043void wxTreeListMainWindow::AssignButtonsImageList(wxImageList *imageList)
3044{
3045 SetButtonsImageList(imageList);
3046 m_ownsImageListButtons = TRUE;
3047}
3048
3049// ----------------------------------------------------------------------------
3050// helpers
3051// ----------------------------------------------------------------------------
3052
3053void wxTreeListMainWindow::AdjustMyScrollbars()
3054{
3055 if (m_anchor)
3056 {
3057 int x = 0, y = 0;
3058 m_anchor->GetSize( x, y, this );
3059 y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
3060 //x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
3061 int x_pos = GetScrollPos( wxHORIZONTAL );
3062 int y_pos = GetScrollPos( wxVERTICAL );
3063 x = m_owner->GetHeaderWindow()->GetWidth() + 2;
3064 if(x < GetClientSize().GetWidth()) x_pos = 0;
3065 //m_total_col_width + 2; // ALB
3066 SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT,
3067 y/PIXELS_PER_UNIT, x_pos, y_pos );
3068 }
3069 else
3070 {
3071 SetScrollbars( 0, 0, 0, 0 );
3072 }
3073}
3074
3075int wxTreeListMainWindow::GetLineHeight(wxTreeListItem *item) const
3076{
3077 if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
3078 return item->GetHeight();
3079 else
3080 return m_lineHeight;
3081}
3082
3083void wxTreeListMainWindow::PaintItem(wxTreeListItem *item, wxDC& dc)
3084{
3085 // TODO implement "state" icon on items
3086
3087 wxTreeItemAttr *attr = item->GetAttributes();
3088 if ( attr && attr->HasFont() )
3089 dc.SetFont(attr->GetFont());
3090 else if (item->IsBold())
3091 dc.SetFont(m_boldFont);
3092
3093 long text_w = 0, text_h = 0;
3094
3095 dc.GetTextExtent( item->GetText(GetMainColumn()), &text_w, &text_h );
3096
3097 int total_h = GetLineHeight(item);
3098
3099 if ( item->IsSelected() )
3100 {
3101 dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
3102 }
3103 else
3104 {
3105 wxColour colBg;
3106 if ( attr && attr->HasBackgroundColour() )
3107 colBg = attr->GetBackgroundColour();
3108 else
3109 colBg = m_backgroundColour;
3110 dc.SetBrush(wxBrush(colBg, wxSOLID));
3111 }
3112
3113 int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0;
3114
3115 dc.DrawRectangle(0, item->GetY()+offset,
3116 m_owner->GetHeaderWindow()->GetWidth(),
3117 total_h-offset);
3118
3119 dc.SetBackgroundMode(wxTRANSPARENT);
3120 int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0;
3121 int extra_offset = 0;
3122 for(size_t i = 0; i < GetColumnCount(); ++i) {
3123 int coord_x = extra_offset, image_x = coord_x;
3124 int clip_width = m_owner->GetHeaderWindow()->GetColumnWidth(i);
3125 int image_h = 0, image_w = 0; //2;
3126 int image = NO_IMAGE;
3127
3128 if(i == GetMainColumn()) {
3129 image = item->GetCurrentImage();
3130 coord_x = item->GetX();
3131 }
3132 else {
3133 image = item->GetImage(i);
3134 }
3135
3136 if(image != NO_IMAGE) {
3137 if(m_imageListNormal) {
3138 m_imageListNormal->GetSize( image, image_w, image_h );
3139 image_w += 4;
3140 }
3141 else {
3142 image = NO_IMAGE;
3143 }
3144 }
3145
3146 // honor text alignment
3147 wxString text = item->GetText(i);
3148
3149 switch(m_owner->GetHeaderWindow()->GetColumn(i).GetAlignment()) {
3150 case wxTL_ALIGN_LEFT:
3151 coord_x += image_w + 2;
3152 image_x = coord_x - image_w;
3153 break;
3154 case wxTL_ALIGN_RIGHT:
3155 dc.GetTextExtent(text, &text_w, NULL);
3156 coord_x += clip_width - text_w - image_w - 2;
3157 image_x = coord_x - image_w;
3158 break;
3159 case wxTL_ALIGN_CENTER:
3160 dc.GetTextExtent(text, &text_w, NULL);
3161 //coord_x += (clip_width - text_w)/2 + image_w;
3162 image_x += (clip_width - text_w - image_w)/2 + 2;
3163 coord_x = image_x + image_w;
3164 }
3165
3166 wxDCClipper clipper(dc, /*coord_x,*/ extra_offset,
3167 item->GetY() + extraH, clip_width,
3168 total_h);
3169
3170 if(image != NO_IMAGE) {
3171 m_imageListNormal->Draw( image, dc, image_x,
3172 item->GetY() +((total_h > image_h)?
3173 ((total_h-image_h)/2):0),
3174 wxIMAGELIST_DRAW_TRANSPARENT );
3175 }
3176
3177 dc.DrawText( text,
3178 (wxCoord)(coord_x /*image_w + item->GetX()*/),
3179 (wxCoord)(item->GetY() + extraH));
3180 extra_offset += m_owner->GetHeaderWindow()->GetColumnWidth(i);
3181 }
3182
3183 // restore normal font
3184 dc.SetFont( m_normalFont );
3185}
3186
3187// Now y stands for the top of the item, whereas it used to stand for middle !
3188void wxTreeListMainWindow::PaintLevel( wxTreeListItem *item, wxDC &dc,
3189 int level, int &y, int x_offset )
3190{
3191 int x = level*m_indent + x_offset;
3192 if (!HasFlag(wxTR_HIDE_ROOT))
3193 {
3194 x += m_indent;
3195 }
3196 else if (level == 0)
3197 {
3198 // always expand hidden root
3199 int origY = y;
3200 wxArrayTreeListItems& children = item->GetChildren();
3201 int count = children.Count();
3202 if (count > 0)
3203 {
3204 int n = 0, oldY;
3205 do {
3206 oldY = y;
3207 PaintLevel(children[n], dc, 1, y, x_offset);
3208 } while (++n < count);
3209
3210 if (!HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT) &&
3211 count > 0)
3212 {
3213 // draw line down to last child
3214 origY += GetLineHeight(children[0])>>1;
3215 oldY += GetLineHeight(children[n-1])>>1;
3216 dc.DrawLine(3, origY, 3, oldY);
3217 }
3218 }
3219 return;
3220 }
3221
3222 item->SetX(x+m_spacing);
3223 item->SetY(y);
3224
3225 int h = GetLineHeight(item);
3226 int y_top = y;
3227 int y_mid = y_top + (h>>1);
3228 y += h;
3229
3230 int exposed_x = dc.LogicalToDeviceX(0);
3231 int exposed_y = dc.LogicalToDeviceY(y_top);
3232
3233 if (IsExposed(exposed_x, exposed_y, 10000, h)) // 10000 = very much
3234 {
3235 wxPen *pen =
3236#ifndef __WXMAC__
3237 // don't draw rect outline if we already have the
3238 // background color under Mac
3239 (item->IsSelected() && m_hasFocus) ? wxBLACK_PEN :
3240#endif // !__WXMAC__
3241 wxTRANSPARENT_PEN;
3242
3243 wxColour colText;
3244 if ( item->IsSelected() )
3245 {
3246 colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
3247 }
3248 else
3249 {
3250 wxTreeItemAttr *attr = item->GetAttributes();
3251 if (attr && attr->HasTextColour())
3252 colText = attr->GetTextColour();
3253 else
3254 //colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT);
3255 colText = GetForegroundColour();
3256 }
3257
3258 // prepare to draw
3259 dc.SetTextForeground(colText);
3260 dc.SetPen(*pen);
3261
3262 // draw
3263 PaintItem(item, dc);
3264
3265 if (HasFlag(wxTR_ROW_LINES))
3266 {
3267 int total_width = m_owner->GetHeaderWindow()->GetWidth();
3268 // if the background colour is white, choose a
3269 // contrasting color for the lines
3270 dc.SetPen(*((GetBackgroundColour() == *wxWHITE)
3271 ? wxMEDIUM_GREY_PEN : wxWHITE_PEN));
3272 dc.DrawLine(0, y_top, total_width, y_top);
3273 dc.DrawLine(0, y, total_width, y);
3274 }
3275
3276 // restore DC objects
3277 dc.SetBrush(*wxWHITE_BRUSH);
3278 dc.SetPen(m_dottedPen);
3279 dc.SetTextForeground(*wxBLACK);
3280
3281 size_t clip_width = m_owner->GetHeaderWindow()->GetColumn(
3282 m_main_column).GetWidth();
3283 //m_columns[m_main_column].GetWidth();
3284 if (item->HasPlus() && HasButtons()) // should the item show a button?
3285 {
3286 // clip to the column width
3287 wxDCClipper clipper(dc, x_offset, y_top, clip_width, 10000);
3288
3289 if (!HasFlag(wxTR_NO_LINES))
3290 {
3291 if (x > (signed)m_indent)
3292 dc.DrawLine(x - m_indent, y_mid, x - 5, y_mid);
3293 else if (HasFlag(wxTR_LINES_AT_ROOT))
3294 dc.DrawLine(3, y_mid, x - 5, y_mid);
3295 dc.DrawLine(x + 5, y_mid, x + m_spacing, y_mid);
3296 }
3297
3298 if (m_imageListButtons != NULL)
3299 {
3300 // draw the image button here
3301 int image_h = 0, image_w = 0, image = wxTreeItemIcon_Normal;
3302 if (item->IsExpanded()) image = wxTreeItemIcon_Expanded;
3303 if (item->IsSelected())
3304 image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal;
3305 m_imageListButtons->GetSize(image, image_w, image_h);
3306 int xx = x - (image_w>>1);
3307 int yy = y_mid - (image_h>>1);
3308 dc.SetClippingRegion(xx, yy, image_w, image_h);
3309 m_imageListButtons->Draw(image, dc, xx, yy,
3310 wxIMAGELIST_DRAW_TRANSPARENT);
3311 dc.DestroyClippingRegion();
3312 }
3313 else if (HasFlag(wxTR_TWIST_BUTTONS))
3314 {
3315 // draw the twisty button here
3316 dc.SetPen(*wxBLACK_PEN);
3317 dc.SetBrush(*m_hilightBrush);
3318
3319 wxPoint button[3];
3320
3321 if (item->IsExpanded())
3322 {
3323 button[0].x = x-5;
3324 button[0].y = y_mid-2;
3325 button[1].x = x+5;
3326 button[1].y = y_mid-2;
3327 button[2].x = x;
3328 button[2].y = y_mid+3;
3329 }
3330 else
3331 {
3332 button[0].y = y_mid-5;
3333 button[0].x = x-2;
3334 button[1].y = y_mid+5;
3335 button[1].x = x-2;
3336 button[2].y = y_mid;
3337 button[2].x = x+3;
3338 }
3339 dc.DrawPolygon(3, button);
3340
3341 dc.SetPen(m_dottedPen);
3342 }
3343 else // if (HasFlag(wxTR_HAS_BUTTONS))
3344 {
3345 // draw the plus sign here
3346 dc.SetPen(*wxGREY_PEN);
3347 dc.SetBrush(*wxWHITE_BRUSH);
3348 dc.DrawRectangle(x-5, y_mid-4, 11, 9);
3349 dc.SetPen(*wxBLACK_PEN);
3350 dc.DrawLine(x-2, y_mid, x+3, y_mid);
3351 if (!item->IsExpanded())
3352 dc.DrawLine(x, y_mid-2, x, y_mid+3);
3353 dc.SetPen(m_dottedPen);
3354 }
3355 }
3356 else if (!HasFlag(wxTR_NO_LINES)) // no button; maybe a line?
3357 {
3358 // clip to the column width
3359 wxDCClipper clipper(dc, x_offset, y_top, clip_width, 10000);
3360 // draw the horizontal line here
3361 int x_start = x;
3362 if (x > (signed)m_indent)
3363 x_start -= m_indent;
3364 else if (HasFlag(wxTR_LINES_AT_ROOT))
3365 x_start = 3;
3366 dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
3367 }
3368 }
3369
3370 if (item->IsExpanded())
3371 {
3372 wxArrayTreeListItems& children = item->GetChildren();
3373 int count = children.Count();
3374 if (count > 0)
3375 {
3376 int n = 0, oldY;
3377 ++level;
3378 do {
3379 oldY = y;
3380 PaintLevel(children[n], dc, level, y, x_offset);
3381 } while (++n < count);
3382
3383 if (!HasFlag(wxTR_NO_LINES) && count > 0)
3384 {
3385 size_t clip_width = m_owner->GetHeaderWindow()->GetColumn(
3386 m_main_column).GetWidth();
3387 //m_columns[m_main_column].GetWidth();
3388 // clip to the column width
3389 wxDCClipper clipper(dc, x_offset, y_top, clip_width, 10000);
3390 // draw line down to last child
3391 oldY += GetLineHeight(children[n-1])>>1;
3392 if (HasButtons()) y_mid += 5;
3393 dc.DrawLine(x, y_mid, x, oldY);
3394 }
3395 }
3396 }
3397}
3398
3399void wxTreeListMainWindow::DrawDropEffect(wxTreeListItem *item)
3400{
3401 if ( item )
3402 {
3403 if ( item->HasPlus() )
3404 {
3405 // it's a folder, indicate it by a border
3406 DrawBorder(item);
3407 }
3408 else
3409 {
3410 // draw a line under the drop target because the item will be
3411 // dropped there
3412 DrawLine(item, TRUE /* below */);
3413 }
3414
3415 SetCursor(wxCURSOR_BULLSEYE);
3416 }
3417 else
3418 {
3419 // can't drop here
3420 SetCursor(wxCURSOR_NO_ENTRY);
3421 }
3422}
3423
3424void wxTreeListMainWindow::DrawBorder(const wxTreeItemId &item)
3425{
3426 wxCHECK_RET( item.IsOk(), _T("invalid item in wxTreeListMainWindow::DrawLine") );
3427
3428 wxTreeListItem *i = (wxTreeListItem*) item.m_pItem;
3429
3430 wxClientDC dc(this);
3431 PrepareDC( dc );
3432 dc.SetLogicalFunction(wxINVERT);
3433 dc.SetBrush(*wxTRANSPARENT_BRUSH);
3434
3435 int w = i->GetWidth() + 2;
3436 int h = GetLineHeight(i) + 2;
3437
3438 dc.DrawRectangle( i->GetX() - 1, i->GetY() - 1, w, h);
3439}
3440
3441void wxTreeListMainWindow::DrawLine(const wxTreeItemId &item, bool below)
3442{
3443 wxCHECK_RET( item.IsOk(), _T("invalid item in wxTreeListMainWindow::DrawLine") );
3444
3445 wxTreeListItem *i = (wxTreeListItem*) item.m_pItem;
3446
3447 wxClientDC dc(this);
3448 PrepareDC( dc );
3449 dc.SetLogicalFunction(wxINVERT);
3450
3451 int x = i->GetX(),
3452 y = i->GetY();
3453 if ( below )
3454 {
3455 y += GetLineHeight(i) - 1;
3456 }
3457
3458 dc.DrawLine( x, y, x + i->GetWidth(), y);
3459}
3460
3461// ----------------------------------------------------------------------------
3462// wxWindows callbacks
3463// ----------------------------------------------------------------------------
3464
3465void wxTreeListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
3466{
3467 wxPaintDC dc(this);
3468
3469 PrepareDC( dc );
3470
3471 if(!GetColumnCount()) return; // ALB
3472
3473 if ( !m_anchor)
3474 return;
3475
3476 dc.SetFont( m_normalFont );
3477 dc.SetPen( m_dottedPen );
3478
3479 // this is now done dynamically
3480 //if(GetImageList() == NULL)
3481 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
3482
3483 int y = 0; //HEADER_HEIGHT; //2;
3484 int x_offset = 0;
3485 for(size_t i = 0; i < GetMainColumn(); ++i) {
3486 x_offset += m_owner->GetHeaderWindow()->GetColumnWidth(i);
3487 }
3488 PaintLevel( m_anchor, dc, 0, y, x_offset );
3489}
3490
3491void wxTreeListMainWindow::OnSetFocus( wxFocusEvent &event )
3492{
3493 m_hasFocus = TRUE;
3494
3495 RefreshSelected();
3496
3497 event.Skip();
3498}
3499
3500void wxTreeListMainWindow::OnKillFocus( wxFocusEvent &event )
3501{
3502 m_hasFocus = FALSE;
3503
3504 RefreshSelected();
3505
3506 event.Skip();
3507}
3508
3509void wxTreeListMainWindow::OnChar( wxKeyEvent &event )
3510{
3511 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, m_owner->GetId() );
3512 te.SetKeyEvent( event );
3513 te.SetEventObject( /*this*/m_owner );
3514 if ( m_owner->GetEventHandler()->ProcessEvent( te ) )
3515 {
3516 // intercepted by the user code
3517 return;
3518 }
3519
3520 if ( (m_current == 0) || (m_key_current == 0) )
3521 {
3522 event.Skip();
3523 return;
3524 }
3525
3526 // how should the selection work for this event?
3527 bool is_multiple, extended_select, unselect_others;
3528 EventFlagsToSelType(GetWindowStyleFlag(),
3529 event.ShiftDown(),
3530 event.ControlDown(),
3531 is_multiple, extended_select, unselect_others);
3532
3533 // + : Expand (not on Win32)
3534 // - : Collaspe (not on Win32)
3535 // * : Expand all/Collapse all
3536 // ' ' | return : activate
3537 // up : go up (not last children!)
3538 // down : go down
3539 // left : go to parent (or collapse on Win32)
3540 // right : open if parent and go next (or expand on Win32)
3541 // home : go to root
3542 // end : go to last item without opening parents
3543 switch (event.KeyCode())
3544 {
3545#ifndef __WXMSW__ // mimic the standard win32 tree ctrl
3546 case '+':
3547 case WXK_ADD:
3548 if (m_current->HasPlus() && !IsExpanded(m_current))
3549 {
3550 Expand(m_current);
3551 }
3552 break;
3553#endif // __WXMSW__
3554
3555 case '*':
3556 case WXK_MULTIPLY:
3557 if ( !IsExpanded(m_current) )
3558 {
3559 // expand all
3560 ExpandAll(m_current);
3561 break;
3562 }
3563 //else: fall through to Collapse() it
3564
3565#ifndef __WXMSW__ // mimic the standard wxTreeCtrl behaviour
3566 case '-':
3567 case WXK_SUBTRACT:
3568 if (IsExpanded(m_current))
3569 {
3570 Collapse(m_current);
3571 }
3572 break;
3573#endif // __WXMSW__
3574
3575 case ' ':
3576 case WXK_RETURN:
3577 {
3578 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
3579 m_owner->GetId() );
3580 event.SetItem( (long) m_current);
3581 event.SetEventObject( /*this*/m_owner );
3582 m_owner->GetEventHandler()->ProcessEvent( event );
3583 }
3584 break;
3585
3586 // up goes to the previous sibling or to the last
3587 // of its children if it's expanded
3588 case WXK_UP:
3589 {
3590 wxTreeItemId prev = GetPrevSibling( m_key_current );
3591 if (!prev)
3592 {
3593 prev = GetParent( m_key_current );
3594 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
3595 {
3596 break; // don't go to root if it is hidden
3597 }
3598 if (prev)
3599 {
3600 long cookie = 0;
3601 wxTreeItemId current = m_key_current;
3602 // TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
3603 if (current == GetFirstChild( prev, cookie ))
3604 {
3605 // otherwise we return to where we came from
3606 SelectItem( prev, unselect_others, extended_select );
3607 m_key_current= (wxTreeListItem*) prev.m_pItem;
3608 EnsureVisible( prev );
3609 break;
3610 }
3611 }
3612 }
3613 if (prev)
3614 {
3615 while ( IsExpanded(prev) && HasChildren(prev) )
3616 {
3617 wxTreeItemId child = GetLastChild(prev);
3618 if ( child )
3619 {
3620 prev = child;
3621 }
3622 }
3623
3624 SelectItem( prev, unselect_others, extended_select );
3625 m_key_current=(wxTreeListItem*) prev.m_pItem;
3626 EnsureVisible( prev );
3627 }
3628 }
3629 break;
3630
3631 // left arrow goes to the parent
3632 case WXK_LEFT:
3633#if defined(__WXMSW__) // mimic the standard win32 tree ctrl
3634 if (IsExpanded(m_current))
3635 {
3636 Collapse(m_current);
3637 }
3638 else
3639#endif // __WXMSW__
3640 {
3641 wxTreeItemId prev = GetParent( m_current );
3642 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
3643 {
3644 // don't go to root if it is hidden
3645 prev = GetPrevSibling( m_current );
3646 }
3647 if (prev)
3648 {
3649 EnsureVisible( prev );
3650 SelectItem( prev, unselect_others, extended_select );
3651 }
3652 }
3653 break;
3654
3655 case WXK_RIGHT:
3656#if defined(__WXMSW__) // mimic the standard win32 tree ctrl
3657 if (m_current->HasPlus() && !IsExpanded(m_current))
3658 {
3659 Expand(m_current);
3660 break;
3661 }
3662#endif // __WXMSW__
3663
3664 // this works the same as the down arrow except that we
3665 // also expand the item if it wasn't expanded yet
3666 Expand(m_current);
3667 // fall through
3668
3669 case WXK_DOWN:
3670 {
3671 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
3672 {
3673 long cookie = 0;
3674 wxTreeItemId child = GetFirstChild( m_key_current, cookie );
3675 SelectItem( child, unselect_others, extended_select );
3676 m_key_current=(wxTreeListItem*) child.m_pItem;
3677 EnsureVisible( child );
3678 }
3679 else
3680 {
3681 wxTreeItemId next = GetNextSibling( m_key_current );
3682 if (!next)
3683 {
3684 wxTreeItemId current = m_key_current;
3685 while (current && !next)
3686 {
3687 current = GetParent( current );
3688 if (current) next = GetNextSibling( current );
3689 }
3690 }
3691 if (next)
3692 {
3693 SelectItem( next, unselect_others, extended_select );
3694 m_key_current=(wxTreeListItem*) next.m_pItem;
3695 EnsureVisible( next );
3696 }
3697 }
3698 }
3699 break;
3700
3701 // <End> selects the last visible tree item
3702 case WXK_END:
3703 {
3704 wxTreeItemId last = GetRootItem();
3705
3706 while ( last.IsOk() && IsExpanded(last) )
3707 {
3708 wxTreeItemId lastChild = GetLastChild(last);
3709
3710 // it may happen if the item was expanded but then all of
3711 // its children have been deleted - so IsExpanded() returned
3712 // TRUE, but GetLastChild() returned invalid item
3713 if ( !lastChild )
3714 break;
3715
3716 last = lastChild;
3717 }
3718
3719 if ( last.IsOk() )
3720 {
3721 EnsureVisible( last );
3722 SelectItem( last, unselect_others, extended_select );
3723 }
3724 }
3725 break;
3726
3727 // <Home> selects the root item
3728 case WXK_HOME:
3729 {
3730 wxTreeItemId prev = GetRootItem();
3731 if (!prev) break;
3732 if (HasFlag(wxTR_HIDE_ROOT))
3733 {
3734 long dummy;
3735 prev = GetFirstChild(prev, dummy);
3736 if (!prev) break;
3737 }
3738 EnsureVisible( prev );
3739 SelectItem( prev, unselect_others, extended_select );
3740 }
3741 break;
3742
3743 default:
3744 event.Skip();
3745 }
3746}
3747
3748wxTreeItemId wxTreeListMainWindow::HitTest(const wxPoint& point, int& flags,
3749 int& column)
3750{
3751 // JACS: removed wxYieldIfNeeded() because it can cause the window
3752 // to be deleted from under us if a close window event is pending
3753
3754 int w, h;
3755 GetSize(&w, &h);
3756 flags=0;
3757 if (point.x<0) flags |= wxTREE_HITTEST_TOLEFT;
3758 if (point.x>w) flags |= wxTREE_HITTEST_TORIGHT;
3759 if (point.y<0) flags |= wxTREE_HITTEST_ABOVE;
3760 if (point.y>h) flags |= wxTREE_HITTEST_BELOW;
3761 if (flags) return wxTreeItemId();
3762
3763 if (m_anchor == NULL)
3764 {
3765 flags = wxTREE_HITTEST_NOWHERE;
3766 return wxTreeItemId();
3767 }
3768
3769 wxClientDC dc(this);
3770 PrepareDC(dc);
3771 wxCoord x = dc.DeviceToLogicalX( point.x );
3772 wxCoord y = dc.DeviceToLogicalY( point.y );
3773 wxTreeListItem *hit = m_anchor->HitTest(wxPoint(x, y), this, flags,
3774 column, 0);
3775 if (hit == NULL)
3776 {
3777 flags = wxTREE_HITTEST_NOWHERE;
3778 return wxTreeItemId();
3779 }
3780 return hit;
3781}
3782
3783// get the bounding rectangle of the item (or of its label only)
3784bool wxTreeListMainWindow::GetBoundingRect(const wxTreeItemId& item,
3785 wxRect& rect,
3786 bool WXUNUSED(textOnly)) const
3787{
3788 wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxTreeListMainWindow::GetBoundingRect") );
3789
3790 wxTreeListItem *i = (wxTreeListItem*) item.m_pItem;
3791
3792 int startX, startY;
3793 GetViewStart(& startX, & startY);
3794
3795 rect.x = i->GetX() - startX*PIXELS_PER_UNIT;
3796 rect.y = i->GetY() - startY*PIXELS_PER_UNIT;
3797 rect.width = i->GetWidth();
3798 //rect.height = i->GetHeight();
3799 rect.height = GetLineHeight(i);
3800
3801 return TRUE;
3802}
3803
3804/* **** */
3805
3806void wxTreeListMainWindow::Edit( const wxTreeItemId& item )
3807{
3808 if (!item.IsOk()) return;
3809
3810 m_currentEdit = (wxTreeListItem*) item.m_pItem;
3811
3812 wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, m_owner->GetId() );
3813 te.SetItem( (long) m_currentEdit);
3814 te.SetEventObject( /*this*/m_owner );
3815 m_owner->GetEventHandler()->ProcessEvent( te );
3816
3817 if (!te.IsAllowed()) return;
3818
3819 // We have to call this here because the label in
3820 // question might just have been added and no screen
3821 // update taken place.
3822 if (m_dirty) wxYieldIfNeeded();
3823
3824 wxString s = m_currentEdit->GetText(/*ALB*/m_main_column);
3825 int x = m_currentEdit->GetX();
3826 int y = m_currentEdit->GetY();
3827 int w = m_currentEdit->GetWidth();
3828 int h = m_currentEdit->GetHeight();
3829
3830 int image_h = 0;
3831 int image_w = 0;
3832
3833 int image = m_currentEdit->GetCurrentImage();
3834 if ( image != NO_IMAGE )
3835 {
3836 if ( m_imageListNormal )
3837 {
3838 m_imageListNormal->GetSize( image, image_w, image_h );
3839 image_w += 4;
3840 }
3841 else
3842 {
3843 wxFAIL_MSG(_T("you must create an image list to use images!"));
3844 }
3845 }
3846 x += image_w;
3847 w -= image_w + 4; // I don't know why +4 is needed
3848
3849 wxClientDC dc(this);
3850 PrepareDC( dc );
3851 x = dc.LogicalToDeviceX( x );
3852 y = dc.LogicalToDeviceY( y );
3853
3854 wxTreeListTextCtrl *text = new wxTreeListTextCtrl(this, -1,
3855 &m_renameAccept,
3856 &m_renameRes,
3857 this,
3858 s,
3859 wxPoint(x-4,y-4),
3860 wxSize(w+11,h+8));
3861 text->SetFocus();
3862}
3863
3864void wxTreeListMainWindow::OnRenameTimer()
3865{
3866 Edit( m_current );
3867}
3868
3869void wxTreeListMainWindow::OnRenameAccept()
3870{
3871 // TODO if the validator fails this causes a crash
3872 wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, m_owner->GetId() );
3873 le.SetItem( (long) m_currentEdit );
3874 le.SetEventObject( /*this*/m_owner );
3875 le.SetLabel( m_renameRes );
3876 m_owner->GetEventHandler()->ProcessEvent( le );
3877
3878 if (!le.IsAllowed()) return;
3879
3880 SetItemText( m_currentEdit, m_renameRes );
3881}
3882
3883void wxTreeListMainWindow::OnMouse( wxMouseEvent &event )
3884{
3885 if ( !m_anchor ) return;
3886
3887 // we process left mouse up event (enables in-place edit), right down
3888 // (pass to the user code), left dbl click (activate item) and
3889 // dragging/moving events for items drag-and-drop
3890 if ( !(event.LeftDown() ||
3891 event.LeftUp() ||
3892 event.RightDown() ||
3893 event.LeftDClick() ||
3894 event.Dragging() ||
3895 ((event.Moving() || event.RightUp()) && m_isDragging)) )
3896 {
3897 event.Skip();
3898
3899 return;
3900 }
3901
3902 if ( event.LeftDown() )
3903 SetFocus();
3904
3905 wxClientDC dc(this);
3906 PrepareDC(dc);
3907 wxCoord x = dc.DeviceToLogicalX( event.GetX() );
3908 wxCoord y = dc.DeviceToLogicalY( event.GetY() );
3909
3910 int flags = 0;
3911 wxTreeListItem *item = m_anchor->HitTest(wxPoint(x,y), this, flags, 0);
3912
3913 if ( event.Dragging() && !m_isDragging )
3914 {
3915 if (m_dragCount == 0)
3916 m_dragStart = wxPoint(x,y);
3917
3918 m_dragCount++;
3919
3920 if (m_dragCount != 3)
3921 {
3922 // wait until user drags a bit further...
3923 return;
3924 }
3925
3926 wxEventType command = event.RightIsDown()
3927 ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
3928 : wxEVT_COMMAND_TREE_BEGIN_DRAG;
3929
3930 wxTreeEvent nevent( command,/*ALB*/ m_owner->GetId() );
3931 nevent.SetItem( (long) m_current);
3932 nevent.SetEventObject(/*this*/m_owner); // ALB
3933
3934 // by default the dragging is not supported, the user code must
3935 // explicitly allow the event for it to take place
3936 nevent.Veto();
3937
3938 if ( m_owner->GetEventHandler()->ProcessEvent(nevent) &&
3939 nevent.IsAllowed() )
3940 {
3941 // we're going to drag this item
3942 m_isDragging = TRUE;
3943
3944 // remember the old cursor because we will change it while
3945 // dragging
3946 m_oldCursor = m_cursor;
3947
3948 // in a single selection control, hide the selection temporarily
3949 if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
3950 {
3951 m_oldSelection = (wxTreeListItem*) GetSelection().m_pItem;
3952
3953 if ( m_oldSelection )
3954 {
3955 m_oldSelection->SetHilight(FALSE);
3956 RefreshLine(m_oldSelection);
3957 }
3958 }
3959
3960 CaptureMouse();
3961 }
3962 }
3963 else if ( event.Moving() )
3964 {
3965 if ( item != m_dropTarget )
3966 {
3967 // unhighlight the previous drop target
3968 DrawDropEffect(m_dropTarget);
3969
3970 m_dropTarget = item;
3971
3972 // highlight the current drop target if any
3973 DrawDropEffect(m_dropTarget);
3974
3975 wxYieldIfNeeded();
3976 }
3977 }
3978 else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
3979 {
3980 // erase the highlighting
3981 DrawDropEffect(m_dropTarget);
3982
3983 if ( m_oldSelection )
3984 {
3985 m_oldSelection->SetHilight(TRUE);
3986 RefreshLine(m_oldSelection);
3987 m_oldSelection = (wxTreeListItem *)NULL;
3988 }
3989
3990 // generate the drag end event
3991 wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG,/*ALB*/m_owner->GetId());
3992
3993 event.SetItem( (long) item );
3994 event.SetPoint( wxPoint(x, y) );
3995 event.SetEventObject(/*this*/m_owner);
3996
3997 (void)m_owner->GetEventHandler()->ProcessEvent(event);
3998
3999 m_isDragging = FALSE;
4000 m_dropTarget = (wxTreeListItem *)NULL;
4001
4002 ReleaseMouse();
4003
4004 SetCursor(m_oldCursor);
4005
4006 wxYieldIfNeeded();
4007 }
4008 else
4009 {
4010 // here we process only the messages which happen on tree items
4011
4012 m_dragCount = 0;
4013
4014 if (item == NULL) return; /* we hit the blank area */
4015
4016 if ( event.RightDown() )
4017 {
4018 SetFocus();
4019 wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,
4020 m_owner->GetId());
4021 nevent.SetItem( (long) item );
4022 int nx, ny;
4023 CalcScrolledPosition(x, y, &nx, &ny);
4024 nevent.SetPoint( wxPoint(nx, ny));
4025 nevent.SetEventObject(/*this*/m_owner);
4026 m_owner->GetEventHandler()->ProcessEvent(nevent);
4027 }
4028 else if ( event.LeftUp() )
4029 {
4030 if ( m_lastOnSame )
4031 {
4032 if ( (item == m_current) &&
4033 (flags & wxTREE_HITTEST_ONITEMLABEL) &&
4034 HasFlag(wxTR_EDIT_LABELS) )
4035 {
4036 if ( m_renameTimer->IsRunning() )
4037 m_renameTimer->Stop();
4038
4039 m_renameTimer->Start( 100, TRUE );
4040 }
4041
4042 m_lastOnSame = FALSE;
4043 }
4044 }
4045 else // !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
4046 {
4047 if ( event.LeftDown() )
4048 {
4049 SetFocus();
4050 m_lastOnSame = item == m_current;
4051 }
4052
4053 if ( flags & wxTREE_HITTEST_ONITEMBUTTON )
4054 {
4055 // only toggle the item for a single click, double click on
4056 // the button doesn't do anything (it toggles the item twice)
4057 if ( event.LeftDown() )
4058 {
4059 Toggle( item );
4060 }
4061
4062 // don't select the item if the button was clicked
4063 return;
4064 }
4065
4066 // how should the selection work for this event?
4067 bool is_multiple, extended_select, unselect_others;
4068 EventFlagsToSelType(GetWindowStyleFlag(),
4069 event.ShiftDown(),
4070 event.ControlDown(),
4071 is_multiple, extended_select, unselect_others);
4072
4073 SelectItem(item, unselect_others, extended_select);
4074
4075 // For some reason, Windows isn't recognizing a left double-click,
4076 // so we need to simulate it here. Allow 200 milliseconds for now.
4077 if ( event.LeftDClick() )
4078 {
4079 // double clicking should not start editing the item label
4080 m_renameTimer->Stop();
4081 m_lastOnSame = FALSE;
4082
4083 // send activate event first
4084 wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
4085 m_owner->GetId() );
4086 nevent.SetItem( (long) item );
4087 int nx, ny;
4088 CalcScrolledPosition(x, y, &nx, &ny);
4089 nevent.SetPoint( wxPoint(nx, ny) );
4090 nevent.SetEventObject( /*this*/m_owner );
4091 if ( !m_owner->GetEventHandler()->ProcessEvent( nevent ) )
4092 {
4093 // if the user code didn't process the activate event,
4094 // handle it ourselves by toggling the item when it is
4095 // double clicked
4096 if ( item->HasPlus() )
4097 {
4098 Toggle(item);
4099 }
4100 }
4101 }
4102 }
4103 }
4104}
4105
4106void wxTreeListMainWindow::OnIdle( wxIdleEvent &WXUNUSED(event) )
4107{
4108 /* after all changes have been done to the tree control,
4109 * we actually redraw the tree when everything is over */
4110
4111 if (!m_dirty) return;
4112
4113 m_dirty = FALSE;
4114
4115 CalculatePositions();
4116 Refresh();
4117 AdjustMyScrollbars();
4118}
4119
4120void wxTreeListMainWindow::OnSize(wxSizeEvent& WXUNUSED(event))
4121{
4122// int w, h;
4123// GetClientSize(&w, &h);
4124// m_header_win->SetSize(0, 0, w, HEADER_HEIGHT);
4125}
4126
4127void wxTreeListMainWindow::OnScroll(wxScrollWinEvent& event)
4128{
4129 // FIXME
4130#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
4131 wxScrolledWindow::OnScroll(event);
4132#else
4133 HandleOnScroll( event );
4134#endif
4135
4136 if(event.GetOrientation() == wxHORIZONTAL)
4137 {
4138 m_owner->GetHeaderWindow()->Refresh();
4139#ifdef __WXMAC__
4140 m_owner->GetHeaderWindow()->MacUpdateImmediately();
4141#endif
4142 }
4143}
4144
4145
4146void wxTreeListMainWindow::CalculateSize( wxTreeListItem *item, wxDC &dc )
4147{
4148 wxCoord text_w = 0;
4149 wxCoord text_h = 0;
4150
4151 if (item->IsBold())
4152 dc.SetFont(m_boldFont);
4153
4154 dc.GetTextExtent( item->GetText(/*ALB*/m_main_column), &text_w, &text_h );
4155 text_h+=2;
4156
4157 // restore normal font
4158 dc.SetFont( m_normalFont );
4159
4160 int image_h = 0;
4161 int image_w = 0;
4162 int image = item->GetCurrentImage();
4163 if ( image != NO_IMAGE )
4164 {
4165 if ( m_imageListNormal )
4166 {
4167 m_imageListNormal->GetSize( image, image_w, image_h );
4168 image_w += 4;
4169 image_h += 2;
4170 }
4171 }
4172
4173 int total_h = (image_h > text_h) ? image_h : text_h;
4174
4175// if (total_h < 30)
4176// total_h += 2; // at least 2 pixels
4177// else
4178// total_h += total_h/10; // otherwise 10% extra spacing
4179
4180 item->SetHeight(total_h);
4181 if (total_h>m_lineHeight)
4182 m_lineHeight=total_h;
4183
4184 item->SetWidth(image_w+text_w+2);
4185}
4186
4187// -----------------------------------------------------------------------------
4188// for developper : y is now the top of the level
4189// not the middle of it !
4190void wxTreeListMainWindow::CalculateLevel( wxTreeListItem *item, wxDC &dc,
4191 int level, int &y, int x_offset )
4192{
4193 int x = level*m_indent + x_offset;
4194 if (!HasFlag(wxTR_HIDE_ROOT))
4195 {
4196 x += m_indent;
4197 }
4198 else if (level == 0)
4199 {
4200 // a hidden root is not evaluated, but its
4201 // children are always calculated
4202 goto Recurse;
4203 }
4204
4205 CalculateSize( item, dc );
4206
4207 // set its position
4208 item->SetX( x+m_spacing );
4209 item->SetY( y );
4210 y += GetLineHeight(item);
4211
4212 if ( !item->IsExpanded() )
4213 {
4214 // we don't need to calculate collapsed branches
4215 return;
4216 }
4217
4218 Recurse:
4219 wxArrayTreeListItems& children = item->GetChildren();
4220 size_t n, count = children.Count();
4221 ++level;
4222 for (n = 0; n < count; ++n )
4223 CalculateLevel( children[n], dc, level, y, x_offset ); // recurse
4224}
4225
4226void wxTreeListMainWindow::CalculatePositions()
4227{
4228 if ( !m_anchor ) return;
4229
4230 wxClientDC dc(this);
4231 PrepareDC( dc );
4232
4233 dc.SetFont( m_normalFont );
4234
4235 dc.SetPen( m_dottedPen );
4236 //if(GetImageList() == NULL)
4237 // m_lineHeight = (int)(dc.GetCharHeight() + 4);
4238
4239 int y = 2;
4240 int x_offset = 0;
4241 for(size_t i = 0; i < GetMainColumn(); ++i) {
4242 x_offset += m_owner->GetHeaderWindow()->GetColumnWidth(i);
4243 }
4244 CalculateLevel( m_anchor, dc, 0, y, x_offset ); // start recursion
4245}
4246
4247void wxTreeListMainWindow::RefreshSubtree(wxTreeListItem *item)
4248{
4249 if (m_dirty) return;
4250
4251 wxClientDC dc(this);
4252 PrepareDC(dc);
4253
4254 int cw = 0;
4255 int ch = 0;
4256 GetClientSize( &cw, &ch );
4257
4258 wxRect rect;
4259 rect.x = dc.LogicalToDeviceX( 0 );
4260 rect.width = cw;
4261 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
4262 rect.height = ch;
4263
4264 Refresh( TRUE, &rect );
4265
4266 AdjustMyScrollbars();
4267}
4268
4269void wxTreeListMainWindow::RefreshLine( wxTreeListItem *item )
4270{
4271 if (m_dirty) return;
4272
4273 wxClientDC dc(this);
4274 PrepareDC( dc );
4275
4276 int cw = 0;
4277 int ch = 0;
4278 GetClientSize( &cw, &ch );
4279
4280 wxRect rect;
4281 rect.x = dc.LogicalToDeviceX( 0 );
4282 rect.y = dc.LogicalToDeviceY( item->GetY() );
4283 rect.width = cw;
4284 rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
4285
4286 Refresh( TRUE, &rect );
4287}
4288
4289void wxTreeListMainWindow::RefreshSelected()
4290{
4291 // TODO: this is awfully inefficient, we should keep the list of all
4292 // selected items internally, should be much faster
4293 if ( m_anchor )
4294 RefreshSelectedUnder(m_anchor);
4295}
4296
4297void wxTreeListMainWindow::RefreshSelectedUnder(wxTreeListItem *item)
4298{
4299 if ( item->IsSelected() )
4300 RefreshLine(item);
4301
4302 const wxArrayTreeListItems& children = item->GetChildren();
4303 size_t count = children.GetCount();
4304 for ( size_t n = 0; n < count; n++ )
4305 {
4306 RefreshSelectedUnder(children[n]);
4307 }
4308}
4309
4310// ----------------------------------------------------------------------------
4311// changing colours: we need to refresh the tree control
4312// ----------------------------------------------------------------------------
4313
4314bool wxTreeListMainWindow::SetBackgroundColour(const wxColour& colour)
4315{
4316 if ( !wxWindow::SetBackgroundColour(colour) )
4317 return FALSE;
4318
4319 Refresh();
4320
4321 return TRUE;
4322}
4323
4324bool wxTreeListMainWindow::SetForegroundColour(const wxColour& colour)
4325{
4326 if ( !wxWindow::SetForegroundColour(colour) )
4327 return FALSE;
4328
4329 Refresh();
4330
4331 return TRUE;
4332}
4333
4334//----------- ALB -------------
4335inline
4336void wxTreeListMainWindow::SetItemText(const wxTreeItemId& item, size_t column,
4337 const wxString& text)
4338{
4339 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
4340
4341 wxClientDC dc(this);
4342 wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem;
4343 pItem->SetText(column, text);
4344 CalculateSize(pItem, dc);
4345 RefreshLine(pItem);
4346}
4347
4348inline
4349wxString wxTreeListMainWindow::GetItemText(const wxTreeItemId& item,
4350 size_t column) const
4351{
4352 wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") );
4353
4354 return ((wxTreeListItem*) item.m_pItem)->GetText(column);
4355}
4356
4357//-----------------------------
4358
4359//-----------------------------------------------------------------------------
4360// wxTreeListCtrl
4361//-----------------------------------------------------------------------------
4362
4363IMPLEMENT_DYNAMIC_CLASS(wxTreeListCtrl, wxControl);
4364
4365BEGIN_EVENT_TABLE(wxTreeListCtrl, wxControl)
4366 EVT_SIZE(wxTreeListCtrl::OnSize)
4367END_EVENT_TABLE();
4368
4369bool wxTreeListCtrl::Create(wxWindow *parent, wxWindowID id,
4370 const wxPoint& pos,
4371 const wxSize& size,
4372 long style, const wxValidator &validator,
4373 const wxString& name)
4374{
4375 long main_style = style & ~(wxRAISED_BORDER|wxSUNKEN_BORDER
4376 |wxSIMPLE_BORDER|wxNO_BORDER|wxDOUBLE_BORDER
4377 |wxSTATIC_BORDER);
4378 if(!wxControl::Create(parent, id, pos, size, style, validator, name))
4379 return false;
4380
4381 m_main_win = new wxTreeListMainWindow(this, -1, wxPoint(0, 0), size,
4382 main_style, validator);
4383 m_header_win = new wxTreeListHeaderWindow(this, -1, m_main_win,
4384 wxPoint(0, 0), wxDefaultSize,
4385 wxTAB_TRAVERSAL);
4386 return TRUE;
4387}
4388
4389void wxTreeListCtrl::OnSize(wxSizeEvent& event)
4390{
4391 int w, h;
4392 GetClientSize(&w, &h);
4393 if(m_header_win)
4394 m_header_win->SetSize(0, 0, w, HEADER_HEIGHT);
4395 if(m_main_win)
4396 m_main_win->SetSize(0, HEADER_HEIGHT + 1, w, h - HEADER_HEIGHT - 1);
4397}
4398
4399size_t wxTreeListCtrl::GetCount() const { return m_main_win->GetCount(); }
4400
4401unsigned int wxTreeListCtrl::GetIndent() const
4402{ return m_main_win->GetIndent(); }
4403
4404void wxTreeListCtrl::SetIndent(unsigned int indent)
4405{ m_main_win->SetIndent(indent); }
4406
4407unsigned int wxTreeListCtrl::GetSpacing() const
4408{ return m_main_win->GetSpacing(); }
4409
4410void wxTreeListCtrl::SetSpacing(unsigned int spacing)
4411{ m_main_win->SetSpacing(spacing); }
4412
4413unsigned int wxTreeListCtrl::GetLineSpacing() const
4414{ return m_main_win->GetLineSpacing(); }
4415
4416void wxTreeListCtrl::SetLineSpacing(unsigned int spacing)
4417{ m_main_win->SetLineSpacing(spacing); }
4418
4419wxImageList* wxTreeListCtrl::GetImageList() const
4420{ return m_main_win->GetImageList(); }
4421
4422wxImageList* wxTreeListCtrl::GetStateImageList() const
4423{ return m_main_win->GetStateImageList(); }
4424
4425wxImageList* wxTreeListCtrl::GetButtonsImageList() const
4426{ return m_main_win->GetButtonsImageList(); }
4427
4428void wxTreeListCtrl::SetImageList(wxImageList* imageList)
4429{ m_main_win->SetImageList(imageList); }
4430
4431void wxTreeListCtrl::SetStateImageList(wxImageList* imageList)
4432{ m_main_win->SetStateImageList(imageList); }
4433
4434void wxTreeListCtrl::SetButtonsImageList(wxImageList* imageList)
4435{ m_main_win->SetButtonsImageList(imageList); }
4436
4437void wxTreeListCtrl::AssignImageList(wxImageList* imageList)
4438{ m_main_win->AssignImageList(imageList); }
4439
4440void wxTreeListCtrl::AssignStateImageList(wxImageList* imageList)
4441{ m_main_win->AssignStateImageList(imageList); }
4442
4443void wxTreeListCtrl::AssignButtonsImageList(wxImageList* imageList)
4444{ m_main_win->AssignButtonsImageList(imageList); }
4445
4446wxString wxTreeListCtrl::GetItemText(const wxTreeItemId& item, size_t column)
4447 const
4448{ return m_main_win->GetItemText(item, column); }
4449
4450int wxTreeListCtrl::GetItemImage(const wxTreeItemId& item, size_t column,
4451 wxTreeItemIcon which) const
4452{ return m_main_win->GetItemImage(item, column, which); }
4453
4454wxTreeItemData* wxTreeListCtrl::GetItemData(const wxTreeItemId& item) const
4455{ return m_main_win->GetItemData(item); }
4456
4457bool wxTreeListCtrl::GetItemBold(const wxTreeItemId& item) const
4458{ return m_main_win->GetItemBold(item); }
4459
4460wxColour wxTreeListCtrl::GetItemTextColour(const wxTreeItemId& item) const
4461{ return m_main_win->GetItemTextColour(item); }
4462
4463wxColour wxTreeListCtrl::GetItemBackgroundColour(const wxTreeItemId& item)
4464 const
4465{ return m_main_win->GetItemBackgroundColour(item); }
4466
4467wxFont wxTreeListCtrl::GetItemFont(const wxTreeItemId& item) const
4468{ return m_main_win->GetItemFont(item); }
4469
4470
4471void wxTreeListCtrl::SetItemText(const wxTreeItemId& item, size_t column,
4472 const wxString& text)
4473{ m_main_win->SetItemText(item, column, text); }
4474
4475void wxTreeListCtrl::SetItemImage(const wxTreeItemId& item,
4476 size_t column,
4477 int image,
4478 wxTreeItemIcon which)
4479{ m_main_win->SetItemImage(item, column, image, which); }
4480
4481void wxTreeListCtrl::SetItemData(const wxTreeItemId& item,
4482 wxTreeItemData* data)
4483{ m_main_win->SetItemData(item, data); }
4484
4485void wxTreeListCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
4486{ m_main_win->SetItemHasChildren(item, has); }
4487
4488void wxTreeListCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
4489{ m_main_win->SetItemBold(item, bold); }
4490
4491void wxTreeListCtrl::SetItemTextColour(const wxTreeItemId& item,
4492 const wxColour& col)
4493{ m_main_win->SetItemTextColour(item, col); }
4494
4495void wxTreeListCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
4496 const wxColour& col)
4497{ m_main_win->SetItemBackgroundColour(item, col); }
4498
4499void wxTreeListCtrl::SetItemFont(const wxTreeItemId& item,
4500 const wxFont& font)
4501{ m_main_win->SetItemFont(item, font); }
4502
4503bool wxTreeListCtrl::SetFont(const wxFont& font)
4504{
4505 if(m_header_win) m_header_win->SetFont(font);
4506 if(m_main_win)
4507 return m_main_win->SetFont(font);
4508 else return FALSE;
4509}
4510
4511void wxTreeListCtrl::SetWindowStyle(const long style)
4512{
4513 if(m_main_win)
4514 m_main_win->SetWindowStyle(style);
4515 // TODO: provide something like wxTL_NO_HEADERS to hide m_header_win
4516}
4517
4518long wxTreeListCtrl::GetWindowStyle() const
4519{
4520 long style = m_windowStyle;
4521 if(m_main_win)
4522 style |= m_main_win->GetWindowStyle();
4523 return style;
4524}
4525
4526bool wxTreeListCtrl::IsVisible(const wxTreeItemId& item) const
4527{ return m_main_win->IsVisible(item); }
4528
4529bool wxTreeListCtrl::ItemHasChildren(const wxTreeItemId& item) const
4530{ return m_main_win->ItemHasChildren(item); }
4531
4532bool wxTreeListCtrl::IsExpanded(const wxTreeItemId& item) const
4533{ return m_main_win->IsExpanded(item); }
4534
4535bool wxTreeListCtrl::IsSelected(const wxTreeItemId& item) const
4536{ return m_main_win->IsSelected(item); }
4537
4538bool wxTreeListCtrl::IsBold(const wxTreeItemId& item) const
4539{ return m_main_win->IsBold(item); }
4540
4541size_t wxTreeListCtrl::GetChildrenCount(const wxTreeItemId& item, bool rec)
4542{ return m_main_win->GetChildrenCount(item, rec); }
4543
4544wxTreeItemId wxTreeListCtrl::GetRootItem() const
4545{ return m_main_win->GetRootItem(); }
4546
4547wxTreeItemId wxTreeListCtrl::GetSelection() const
4548{ return m_main_win->GetSelection(); }
4549
4550size_t wxTreeListCtrl::GetSelections(wxArrayTreeItemIds& arr) const
4551{ return m_main_win->GetSelections(arr); }
4552
4553wxTreeItemId wxTreeListCtrl::GetParent(const wxTreeItemId& item) const
4554{ return m_main_win->GetParent(item); }
4555
4556wxTreeItemId wxTreeListCtrl::GetFirstChild(const wxTreeItemId& item,
4557 long& cookie) const
4558{ return m_main_win->GetFirstChild(item, cookie); }
4559
4560wxTreeItemId wxTreeListCtrl::GetNextChild(const wxTreeItemId& item,
4561 long& cookie) const
4562{ return m_main_win->GetNextChild(item, cookie); }
4563
4564wxTreeItemId wxTreeListCtrl::GetLastChild(const wxTreeItemId& item) const
4565{ return m_main_win->GetLastChild(item); }
4566
4567wxTreeItemId wxTreeListCtrl::GetNextSibling(const wxTreeItemId& item) const
4568{ return m_main_win->GetNextSibling(item); }
4569
4570wxTreeItemId wxTreeListCtrl::GetPrevSibling(const wxTreeItemId& item) const
4571{ return m_main_win->GetPrevSibling(item); }
4572
4573wxTreeItemId wxTreeListCtrl::GetFirstVisibleItem() const
4574{ return m_main_win->GetFirstVisibleItem(); }
4575
4576wxTreeItemId wxTreeListCtrl::GetNextVisible(const wxTreeItemId& item) const
4577{ return m_main_win->GetNextVisible(item); }
4578
4579wxTreeItemId wxTreeListCtrl::GetPrevVisible(const wxTreeItemId& item) const
4580{ return m_main_win->GetPrevVisible(item); }
4581
4582wxTreeItemId wxTreeListCtrl::GetNext(const wxTreeItemId& item) const
4583{ return m_main_win->GetNext(item); }
4584
4585wxTreeItemId wxTreeListCtrl::AddRoot(const wxString& text, int image,
4586 int selectedImage, wxTreeItemData* data)
4587{ return m_main_win->AddRoot(text, image, selectedImage, data); }
4588
4589wxTreeItemId wxTreeListCtrl::PrependItem(const wxTreeItemId& parent,
4590 const wxString& text, int image,
4591 int selectedImage,
4592 wxTreeItemData* data)
4593{ return m_main_win->PrependItem(parent, text, image, selectedImage, data); }
4594
4595wxTreeItemId wxTreeListCtrl::InsertItem(const wxTreeItemId& parent,
4596 const wxTreeItemId& previous,
4597 const wxString& text, int image,
4598 int selectedImage,
4599 wxTreeItemData* data)
4600{
4601 return m_main_win->InsertItem(parent, previous, text, image,
4602 selectedImage, data);
4603}
4604
4605wxTreeItemId wxTreeListCtrl::InsertItem(const wxTreeItemId& parent,
4606 size_t index,
4607 const wxString& text, int image,
4608 int selectedImage,
4609 wxTreeItemData* data)
4610{
4611 return m_main_win->InsertItem(parent, index, text, image,
4612 selectedImage, data);
4613}
4614
4615wxTreeItemId wxTreeListCtrl::AppendItem(const wxTreeItemId& parent,
4616 const wxString& text, int image,
4617 int selectedImage,
4618 wxTreeItemData* data)
4619{ return m_main_win->AppendItem(parent, text, image, selectedImage, data); }
4620
4621void wxTreeListCtrl::Delete(const wxTreeItemId& item)
4622{ m_main_win->Delete(item); }
4623
4624void wxTreeListCtrl::DeleteChildren(const wxTreeItemId& item)
4625{ m_main_win->DeleteChildren(item); }
4626
4627void wxTreeListCtrl::DeleteAllItems()
4628{ m_main_win->DeleteAllItems(); }
4629
4630void wxTreeListCtrl::Expand(const wxTreeItemId& item)
4631{ m_main_win->Expand(item); }
4632
4633void wxTreeListCtrl::ExpandAll(const wxTreeItemId& item)
4634{ m_main_win->ExpandAll(item); }
4635
4636void wxTreeListCtrl::Collapse(const wxTreeItemId& item)
4637{ m_main_win->Collapse(item); }
4638
4639void wxTreeListCtrl::CollapseAndReset(const wxTreeItemId& item)
4640{ m_main_win->CollapseAndReset(item); }
4641
4642void wxTreeListCtrl::Toggle(const wxTreeItemId& item)
4643{ m_main_win->Toggle(item); }
4644
4645void wxTreeListCtrl::Unselect()
4646{ m_main_win->Unselect(); }
4647
4648void wxTreeListCtrl::UnselectAll()
4649{ m_main_win->UnselectAll(); }
4650
4651void wxTreeListCtrl::SelectItem(const wxTreeItemId& item, bool unselect_others,
4652 bool extended_select)
4653{ m_main_win->SelectItem(item, unselect_others, extended_select); }
4654
4655void wxTreeListCtrl::EnsureVisible(const wxTreeItemId& item)
4656{ m_main_win->EnsureVisible(item); }
4657
4658void wxTreeListCtrl::ScrollTo(const wxTreeItemId& item)
4659{ m_main_win->ScrollTo(item); }
4660
4661wxTreeItemId wxTreeListCtrl::HitTest(const wxPoint& pos, int& flags,
4662 int& column)
4663{
4664 return m_main_win->HitTest(m_main_win->ScreenToClient(ClientToScreen(pos)),
4665 flags, column);
4666}
4667
4668bool wxTreeListCtrl::GetBoundingRect(const wxTreeItemId& item, wxRect& rect,
4669 bool textOnly) const
4670{ return m_main_win->GetBoundingRect(item, rect, textOnly); }
4671
4672void wxTreeListCtrl::Edit(const wxTreeItemId& item)
4673{ m_main_win->Edit(item); }
4674
4675int wxTreeListCtrl::OnCompareItems(const wxTreeItemId& item1,
4676 const wxTreeItemId& item2)
4677{
4678 // ALB: do the comparison here, and not delegate to m_main_win, in order
4679 // to let the user override it
4680 //return m_main_win->OnCompareItems(item1, item2);
4681 return wxStrcmp(GetItemText(item1), GetItemText(item2));
4682}
4683
4684void wxTreeListCtrl::SortChildren(const wxTreeItemId& item)
4685{ m_main_win->SortChildren(item); }
4686
4687bool wxTreeListCtrl::SetBackgroundColour(const wxColour& colour)
4688{ return m_main_win->SetBackgroundColour(colour); }
4689
4690bool wxTreeListCtrl::SetForegroundColour(const wxColour& colour)
4691{ return m_main_win->SetForegroundColour(colour); }
4692
4693size_t wxTreeListCtrl::GetColumnCount() const
4694{ return m_main_win->GetColumnCount(); }
4695
4696void wxTreeListCtrl::SetColumnWidth(size_t column, size_t width)
4697{ m_header_win->SetColumnWidth(column, width); }
4698
4699int wxTreeListCtrl::GetColumnWidth(size_t column) const
4700{ return m_header_win->GetColumnWidth(column); }
4701
4702void wxTreeListCtrl::SetMainColumn(size_t column)
4703{ m_main_win->SetMainColumn(column); }
4704
4705size_t wxTreeListCtrl::GetMainColumn() const
4706{ return m_main_win->GetMainColumn(); }
4707
4708void wxTreeListCtrl::SetColumnText(size_t column, const wxString& text)
4709{
4710 m_header_win->SetColumnText(column, text);
4711 m_header_win->Refresh();
4712}
4713
4714wxString wxTreeListCtrl::GetColumnText(size_t column) const
4715{ return m_header_win->GetColumnText(column); }
4716
4717void wxTreeListCtrl::AddColumn(const wxTreeListColumnInfo& col)
4718{ m_header_win->AddColumn(col); }
4719
4720void wxTreeListCtrl::InsertColumn(size_t before,
4721 const wxTreeListColumnInfo& col)
4722{ m_header_win->InsertColumn(before, col); }
4723
4724void wxTreeListCtrl::RemoveColumn(size_t column)
4725{ m_header_win->RemoveColumn(column); }
4726
4727void wxTreeListCtrl::SetColumn(size_t column, const wxTreeListColumnInfo& col)
4728{ m_header_win->SetColumn(column, col); }
4729
4730const wxTreeListColumnInfo& wxTreeListCtrl::GetColumn(size_t column) const
4731{ return m_header_win->GetColumn(column); }
4732
4733wxTreeListColumnInfo& wxTreeListCtrl::GetColumn(size_t column)
4734{ return m_header_win->GetColumn(column); }
4735
4736void wxTreeListCtrl::SetColumnImage(size_t column, int image)
4737{
4738 m_header_win->SetColumn(column, GetColumn(column).SetImage(image));
4739}
4740
4741int wxTreeListCtrl::GetColumnImage(size_t column) const
4742{
4743 return m_header_win->GetColumn(column).GetImage();
4744}
4745
4746void wxTreeListCtrl::SetColumnAlignment(size_t column,
4747 wxTreeListColumnAlign align)
4748{
4749 m_header_win->SetColumn(column, GetColumn(column).SetAlignment(align));
4750}
4751
4752wxTreeListColumnAlign wxTreeListCtrl::GetColumnAlignment(size_t column) const
4753{
4754 return m_header_win->GetColumn(column).GetAlignment();
4755}
4756
4757void wxTreeListCtrl::Refresh(bool erase, const wxRect* rect)
4758{
4759 m_main_win->Refresh(erase, rect);
4760 m_header_win->Refresh(erase, rect);
4761}
4762
4763void wxTreeListCtrl::SetFocus()
4764{ m_main_win->SetFocus(); }