X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0a33446c8e71292bfdb960ed7e26e1f196938cf0..b1d4dd7add411ebff1ce4a8d1d992e0e43ee26b1:/src/generic/treectlg.cpp diff --git a/src/generic/treectlg.cpp b/src/generic/treectlg.cpp index 64e615f681..054b388fcc 100644 --- a/src/generic/treectlg.cpp +++ b/src/generic/treectlg.cpp @@ -45,7 +45,6 @@ class WXDLLEXPORT wxGenericTreeItem; WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems); -//WX_DEFINE_OBJARRAY(wxArrayTreeItemIds); // ---------------------------------------------------------------------------- // constants @@ -53,55 +52,55 @@ WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems); static const int NO_IMAGE = -1; -#define PIXELS_PER_UNIT 10 +static const int PIXELS_PER_UNIT = 10; // ---------------------------------------------------------------------------- // Aqua arrows // ---------------------------------------------------------------------------- - /* XPM */ - static char *aqua_arrow_right[] = { - /* columns rows colors chars-per-pixel */ - "13 11 4 1", - " c None", - "b c #C0C0C0", - "c c #707070", - "d c #A0A0A0", - /* pixels */ - " b ", - " ddb ", - " cccdb ", - " cccccd ", - " ccccccdb ", - " ccccccccd", - " ccccccdb ", - " cccccb ", - " cccdb ", - " ddb ", - " b " - }; - - /* XPM */ - static char *aqua_arrow_down[] = { - /* columns rows colors chars-per-pixel */ - "13 11 4 1", - " c None", - "b c #C0C0C0", - "c c #707070", - "d c #A0A0A0", - /* pixels */ - " ", - " ", - " bdcccccccdb ", - " dcccccccd ", - " bcccccccb ", - " dcccccd ", - " bcccccb ", - " bcccd ", - " dcd ", - " bcb ", - " d " - }; +/* XPM */ +static const char *aqua_arrow_right[] = { +/* columns rows colors chars-per-pixel */ +"13 11 4 1", +" c None", +"b c #C0C0C0", +"c c #707070", +"d c #A0A0A0", +/* pixels */ +" b ", +" ddb ", +" cccdb ", +" cccccd ", +" ccccccdb ", +" ccccccccd", +" ccccccdb ", +" cccccb ", +" cccdb ", +" ddb ", +" b " +}; + +/* XPM */ +static const char *aqua_arrow_down[] = { +/* columns rows colors chars-per-pixel */ +"13 11 4 1", +" c None", +"b c #C0C0C0", +"c c #707070", +"d c #A0A0A0", +/* pixels */ +" ", +" ", +" bdcccccccdb ", +" dcccccccd ", +" bcccccccb ", +" dcccccd ", +" bcccccb ", +" bcccd ", +" dcd ", +" bcb ", +" d " +}; // ----------------------------------------------------------------------------- // private classes @@ -111,42 +110,60 @@ static const int NO_IMAGE = -1; class WXDLLEXPORT wxTreeRenameTimer: public wxTimer { public: + // start editing the current item after half a second (if the mouse hasn't + // been clicked/moved) + enum { DELAY = 500 }; + wxTreeRenameTimer( wxGenericTreeCtrl *owner ); - void Notify(); + virtual void Notify(); private: - wxGenericTreeCtrl *m_owner; + wxGenericTreeCtrl *m_owner; + + DECLARE_NO_COPY_CLASS(wxTreeRenameTimer) }; // control used for in-place edit class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl { public: - wxTreeTextCtrl( wxWindow *parent, - const wxWindowID id, - bool *accept, - wxString *res, - wxGenericTreeCtrl *owner, - const wxString &value = wxEmptyString, - const wxPoint &pos = wxDefaultPosition, - const wxSize &size = wxDefaultSize, - int style = wxSIMPLE_BORDER, - const wxValidator& validator = wxDefaultValidator, - const wxString &name = wxTextCtrlNameStr ); + wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item); +protected: void OnChar( wxKeyEvent &event ); void OnKeyUp( wxKeyEvent &event ); void OnKillFocus( wxFocusEvent &event ); + bool AcceptChanges(); + void Finish(); + private: - bool *m_accept; - wxString *m_res; wxGenericTreeCtrl *m_owner; + wxGenericTreeItem *m_itemEdited; wxString m_startValue; bool m_finished; DECLARE_EVENT_TABLE() + DECLARE_NO_COPY_CLASS(wxTreeTextCtrl) +}; + +// timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed +// for a sufficiently long time +class WXDLLEXPORT wxTreeFindTimer : public wxTimer +{ +public: + // reset the current prefix after half a second of inactivity + enum { DELAY = 500 }; + + wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; } + + virtual void Notify() { m_owner->m_findPrefix.clear(); } + +private: + wxGenericTreeCtrl *m_owner; + + DECLARE_NO_COPY_CLASS(wxTreeFindTimer) }; // a tree item @@ -286,6 +303,8 @@ private: // children but has a [+] button int m_isBold :1; // render the label in bold font int m_ownsAttr :1; // delete attribute when done + + DECLARE_NO_COPY_CLASS(wxGenericTreeItem) }; // ============================================================================= @@ -310,6 +329,23 @@ static void EventFlagsToSelType(long style, unselect_others = !(extended_select || (ctrlDown && is_multiple)); } +// check if the given item is under another one +static bool IsDescendantOf(wxGenericTreeItem *parent, wxGenericTreeItem *item) +{ + while ( item ) + { + if ( item == parent ) + { + // item is a descendant of parent + return TRUE; + } + + item = item->GetParent(); + } + + return FALSE; +} + // ----------------------------------------------------------------------------- // wxTreeRenameTimer (internal) // ----------------------------------------------------------------------------- @@ -334,99 +370,134 @@ BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl) EVT_KILL_FOCUS (wxTreeTextCtrl::OnKillFocus) END_EVENT_TABLE() -wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent, - const wxWindowID id, - bool *accept, - wxString *res, - wxGenericTreeCtrl *owner, - const wxString &value, - const wxPoint &pos, - const wxSize &size, - int style, - const wxValidator& validator, - const wxString &name ) - : wxTextCtrl( parent, id, value, pos, size, style, validator, name ) -{ - m_res = res; - m_accept = accept; +wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner, + wxGenericTreeItem *item) + : m_itemEdited(item), m_startValue(item->GetText()) +{ m_owner = owner; - (*m_accept) = FALSE; - (*m_res) = wxEmptyString; - m_startValue = value; m_finished = FALSE; + + int w = m_itemEdited->GetWidth(), + h = m_itemEdited->GetHeight(); + + int x, y; + m_owner->CalcScrolledPosition(item->GetX(), item->GetY(), &x, &y); + + int image_h = 0, + image_w = 0; + + int image = item->GetCurrentImage(); + if ( image != NO_IMAGE ) + { + if ( m_owner->m_imageListNormal ) + { + m_owner->m_imageListNormal->GetSize( image, image_w, image_h ); + image_w += 4; + } + else + { + wxFAIL_MSG(_T("you must create an image list to use images!")); + } + } + + // FIXME: what are all these hardcoded 4, 8 and 11s really? + x += image_w; + w -= image_w + 4; + + (void)Create(m_owner, wxID_ANY, m_startValue, + wxPoint(x - 4, y - 4), wxSize(w + 11, h + 8)); } -void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) +bool wxTreeTextCtrl::AcceptChanges() { - if (event.m_keyCode == WXK_RETURN) + const wxString value = GetValue(); + + if ( value == m_startValue ) + { + // nothing changed, always accept + return TRUE; + } + + if ( !m_owner->OnRenameAccept(m_itemEdited, value) ) { - (*m_accept) = TRUE; - (*m_res) = GetValue(); + // vetoed by the user + return FALSE; + } - if ((*m_res) != m_startValue) - m_owner->OnRenameAccept(); + // accepted, do rename the item + m_owner->SetItemText(m_itemEdited, value); - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); - - m_finished = TRUE; - m_owner->SetFocus(); // This doesn't work. TODO. + return TRUE; +} - return; - } - if (event.m_keyCode == WXK_ESCAPE) +void wxTreeTextCtrl::Finish() +{ + if ( !m_finished ) { - (*m_accept) = FALSE; - (*m_res) = ""; + m_owner->ResetTextControl(); - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); + wxPendingDelete.Append(this); m_finished = TRUE; + m_owner->SetFocus(); // This doesn't work. TODO. + } +} - return; +void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) +{ + switch ( event.m_keyCode ) + { + case WXK_RETURN: + if ( !AcceptChanges() ) + { + // vetoed by the user, don't disappear + break; + } + //else: fall through + + case WXK_ESCAPE: + Finish(); + m_owner->OnRenameCancelled(m_itemEdited); + break; + + default: + event.Skip(); } - event.Skip(); } void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event ) { - if (m_finished) + if ( !m_finished ) { - event.Skip(); - return; + // auto-grow the textctrl: + wxSize parentSize = m_owner->GetSize(); + wxPoint myPos = GetPosition(); + wxSize mySize = GetSize(); + int sx, sy; + GetTextExtent(GetValue() + _T("M"), &sx, &sy); + if (myPos.x + sx > parentSize.x) + sx = parentSize.x - myPos.x; + if (mySize.x > sx) + sx = mySize.x; + SetSize(sx, -1); } - // auto-grow the textctrl: - wxSize parentSize = m_owner->GetSize(); - wxPoint myPos = GetPosition(); - wxSize mySize = GetSize(); - int sx, sy; - GetTextExtent(GetValue() + _T("M"), &sx, &sy); - if (myPos.x + sx > parentSize.x) sx = parentSize.x - myPos.x; - if (mySize.x > sx) sx = mySize.x; - SetSize(sx, -1); - event.Skip(); } void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event ) { - if (m_finished) + if ( m_finished ) { event.Skip(); return; } - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); - - (*m_accept) = TRUE; - (*m_res) = GetValue(); - - if ((*m_res) != m_startValue) - m_owner->OnRenameAccept(); + if ( AcceptChanges() ) + { + Finish(); + } } // ----------------------------------------------------------------------------- @@ -669,7 +740,7 @@ void wxGenericTreeCtrl::Init() m_hilightBrush = new wxBrush ( - wxSystemSettings::GetSystemColour + wxSystemSettings::GetColour ( wxSYS_COLOUR_HIGHLIGHT ), @@ -678,7 +749,7 @@ void wxGenericTreeCtrl::Init() m_hilightUnfocusedBrush = new wxBrush ( - wxSystemSettings::GetSystemColour + wxSystemSettings::GetColour ( wxSYS_COLOUR_BTNSHADOW ), @@ -693,16 +764,21 @@ void wxGenericTreeCtrl::Init() m_dragCount = 0; m_isDragging = FALSE; m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL; + m_textCtrl = NULL; + + m_renameTimer = NULL; + m_findTimer = NULL; - m_renameTimer = new wxTreeRenameTimer( this ); m_lastOnSame = FALSE; - m_normalFont = wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT ); - m_boldFont = wxFont( m_normalFont.GetPointSize(), - m_normalFont.GetFamily(), - m_normalFont.GetStyle(), - wxBOLD, - m_normalFont.GetUnderlined()); + m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); + m_boldFont = wxFont(m_normalFont.GetPointSize(), + m_normalFont.GetFamily(), + m_normalFont.GetStyle(), + wxBOLD, + m_normalFont.GetUnderlined(), + m_normalFont.GetFaceName(), + m_normalFont.GetEncoding()); } bool wxGenericTreeCtrl::Create(wxWindow *parent, @@ -716,7 +792,7 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, #ifdef __WXMAC__ int major,minor; wxGetOsVersion( &major, &minor ); - + if (style & wxTR_HAS_BUTTONS) style |= wxTR_MAC_BUTTONS; if (style & wxTR_HAS_BUTTONS) style &= ~wxTR_HAS_BUTTONS; style &= ~wxTR_LINES_AT_ROOT; @@ -741,9 +817,9 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name ); - // If the tree display has no buttons, but does have - // connecting lines, we can use a narrower layout. - // It may not be a good idea to force this... + // If the tree display has no buttons, but does have + // connecting lines, we can use a narrower layout. + // It may not be a good idea to force this... if (!HasButtons() && !HasFlag(wxTR_NO_LINES)) { m_indent= 10; @@ -754,7 +830,8 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, SetValidator( validator ); #endif - SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) ); + SetForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) ); + SetBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX) ); // m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86 m_dottedPen = wxPen( wxT("grey"), 0, 0 ); @@ -766,16 +843,21 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl() { delete m_hilightBrush; delete m_hilightUnfocusedBrush; - - if (m_arrowRight) delete m_arrowRight; - if (m_arrowDown) delete m_arrowDown; + + delete m_arrowRight; + delete m_arrowDown; DeleteAllItems(); delete m_renameTimer; - if (m_ownsImageListNormal) delete m_imageListNormal; - if (m_ownsImageListState) delete m_imageListState; - if (m_ownsImageListButtons) delete m_imageListButtons; + delete m_findTimer; + + if (m_ownsImageListNormal) + delete m_imageListNormal; + if (m_ownsImageListState) + delete m_imageListState; + if (m_ownsImageListButtons) + delete m_imageListButtons; } // ----------------------------------------------------------------------------- @@ -789,13 +871,13 @@ size_t wxGenericTreeCtrl::GetCount() const void wxGenericTreeCtrl::SetIndent(unsigned int indent) { - m_indent = indent; + m_indent = (unsigned short) indent; m_dirty = TRUE; } void wxGenericTreeCtrl::SetSpacing(unsigned int spacing) { - m_spacing = spacing; + m_spacing = (unsigned short) spacing; m_dirty = TRUE; } @@ -808,9 +890,17 @@ size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recurs void wxGenericTreeCtrl::SetWindowStyle(const long styles) { - // right now, just sets the styles. Eventually, we may - // want to update the inherited styles, but right now - // none of the parents has updatable styles + if (!HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT)) + { + // if we will hide the root, make sure children are visible + m_anchor->SetHasPlus(); + m_anchor->Expand(); + CalculatePositions(); + } + + // right now, just sets the styles. Eventually, we may + // want to update the inherited styles, but right now + // none of the parents has updatable styles m_windowStyle = styles; m_dirty = TRUE; } @@ -841,6 +931,30 @@ wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const return ((wxGenericTreeItem*) item.m_pItem)->GetData(); } +wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->Attr().GetTextColour(); +} + +wxColour wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->Attr().GetBackgroundColour(); +} + +wxFont wxGenericTreeCtrl::GetItemFont(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->Attr().GetFont(); +} + void wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); @@ -929,11 +1043,13 @@ bool wxGenericTreeCtrl::SetFont( const wxFont &font ) wxScrolledWindow::SetFont(font); m_normalFont = font ; - m_boldFont = wxFont( m_normalFont.GetPointSize(), - m_normalFont.GetFamily(), - m_normalFont.GetStyle(), - wxBOLD, - m_normalFont.GetUnderlined()); + m_boldFont = wxFont(m_normalFont.GetPointSize(), + m_normalFont.GetFamily(), + m_normalFont.GetStyle(), + wxBOLD, + m_normalFont.GetUnderlined(), + m_normalFont.GetFaceName(), + m_normalFont.GetEncoding()); return TRUE; } @@ -1012,7 +1128,7 @@ bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const // navigation // ----------------------------------------------------------------------------- -wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const +wxTreeItemId wxGenericTreeCtrl::GetItemParent(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); @@ -1112,7 +1228,7 @@ wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const do { toFind = GetNextSibling(p); - p = GetParent(p); + p = GetItemParent(p); } while (p.IsOk() && !toFind.IsOk()); return toFind; } @@ -1159,6 +1275,58 @@ wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const return wxTreeItemId(); } +// called by wxTextTreeCtrl when it marks itself for deletion +void wxGenericTreeCtrl::ResetTextControl() +{ + m_textCtrl = NULL; +} + +// find the first item starting with the given prefix after the given item +wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent, + const wxString& prefixOrig) const +{ + // match is case insensitive as this is more convenient to the user: having + // to press Shift-letter to go to the item starting with a capital letter + // would be too bothersome + wxString prefix = prefixOrig.Lower(); + + // determine the starting point: we shouldn't take the current item (this + // allows to switch between two items starting with the same letter just by + // pressing it) but we shouldn't jump to the next one if the user is + // continuing to type as otherwise he might easily skip the item he wanted + wxTreeItemId id = idParent; + if ( prefix.length() == 1 ) + { + id = GetNext(id); + } + + // look for the item starting with the given prefix after it + while ( id.IsOk() && !GetItemText(id).Lower().StartsWith(prefix) ) + { + id = GetNext(id); + } + + // if we haven't found anything... + if ( !id.IsOk() ) + { + // ... wrap to the beginning + id = GetRootItem(); + if ( HasFlag(wxTR_HIDE_ROOT) ) + { + // can't select virtual root + id = GetNext(id); + } + + // and try all the items (stop when we get to the one we started from) + while ( id != idParent && !GetItemText(id).Lower().StartsWith(prefix) ) + { + id = GetNext(id); + } + } + + return id; +} + // ----------------------------------------------------------------------------- // operations // ----------------------------------------------------------------------------- @@ -1201,16 +1369,18 @@ wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text, m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, image, selImage, data); + if ( data != NULL ) + { + data->m_pItem = (long) m_anchor; + } + if (HasFlag(wxTR_HIDE_ROOT)) { // if root is hidden, make sure we can navigate // into children m_anchor->SetHasPlus(); - Expand(m_anchor); - } - if ( data != NULL ) - { - data->m_pItem = (long) m_anchor; + m_anchor->Expand(); + CalculatePositions(); } if (!HasFlag(wxTR_MULTIPLE)) @@ -1243,9 +1413,13 @@ wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId, return AddRoot(text, image, selImage, data); } - int index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem); - wxASSERT_MSG( index != wxNOT_FOUND, - wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") ); + int index = -1; + if (idPrevious.IsOk()) + { + index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem); + wxASSERT_MSG( index != wxNOT_FOUND, + wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") ); + } return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data); } @@ -1304,33 +1478,31 @@ void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId) wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; - // don't stay with invalid m_key_current or we will crash in - // the next call to OnChar() - bool changeKeyCurrent = FALSE; - wxGenericTreeItem *itemKey = m_key_current; - while ( itemKey ) + wxGenericTreeItem *parent = item->GetParent(); + + // don't keep stale pointers around! + if ( IsDescendantOf(item, m_key_current) ) { - if ( itemKey == item ) - { - // m_key_current is a descendant of the item being deleted - changeKeyCurrent = TRUE; - break; - } - itemKey = itemKey->GetParent(); + m_key_current = parent; } - wxGenericTreeItem *parent = item->GetParent(); + if ( IsDescendantOf(item, m_current) ) + { + m_current = parent; + } + + // remove the item from the tree if ( parent ) { parent->GetChildren().Remove( item ); // remove by value } - - if ( changeKeyCurrent ) + else // deleting the root { - // may be NULL or not - m_key_current = parent; + // nothing will be left in the tree + m_anchor = NULL; } + // and delete all of its children and the item itself now item->DeleteChildren(this); SendDeleteEvent(item); delete item; @@ -1340,12 +1512,7 @@ void wxGenericTreeCtrl::DeleteAllItems() { if ( m_anchor ) { - m_dirty = TRUE; - - m_anchor->DeleteChildren(this); - delete m_anchor; - - m_anchor = NULL; + Delete(m_anchor); } } @@ -1354,6 +1521,8 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId) wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") ); + wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(), + _T("can't expand hidden root") ); if ( !item->HasPlus() ) return; @@ -1382,22 +1551,28 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId) void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item) { - Expand(item); - if ( IsExpanded(item) ) + if ( !HasFlag(wxTR_HIDE_ROOT) || item != GetRootItem()) { - long cookie; - wxTreeItemId child = GetFirstChild(item, cookie); - while ( child.IsOk() ) - { - ExpandAll(child); + Expand(item); + if ( !IsExpanded(item) ) + return; + } - child = GetNextChild(item, cookie); - } + long cookie; + wxTreeItemId child = GetFirstChild(item, cookie); + while ( child.IsOk() ) + { + ExpandAll(child); + + child = GetNextChild(item, cookie); } } void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId) { + wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(), + _T("can't collapse hidden root") ); + wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; if ( !item->IsExpanded() ) @@ -1453,6 +1628,8 @@ void wxGenericTreeCtrl::Unselect() { m_current->SetHilight( FALSE ); RefreshLine( m_current ); + + m_current = NULL; } } @@ -1560,8 +1737,8 @@ void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeI } void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, - bool unselect_others, - bool extended_select) + bool unselect_others, + bool extended_select) { wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") ); @@ -1596,13 +1773,13 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() ) return; - wxTreeItemId parent = GetParent( itemId ); + wxTreeItemId parent = GetItemParent( itemId ); while (parent.IsOk()) { if (!IsExpanded(parent)) Expand( parent ); - parent = GetParent( parent ); + parent = GetItemParent( parent ); } EnsureVisible( itemId ); @@ -1643,7 +1820,7 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, } void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item, - wxArrayTreeItemIds &array) const + wxArrayTreeItemIds &array) const { if ( item->IsSelected() ) array.Add(wxTreeItemId(item)); @@ -1678,10 +1855,22 @@ void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item) // first expand all parent branches wxGenericTreeItem *parent = gitem->GetParent(); - while ( parent ) + + if ( HasFlag(wxTR_HIDE_ROOT) ) { - Expand(parent); - parent = parent->GetParent(); + while ( parent != m_anchor ) + { + Expand(parent); + parent = parent->GetParent(); + } + } + else + { + while ( parent ) + { + Expand(parent); + parent = parent->GetParent(); + } } //if (parent) CalculatePositions(); @@ -1837,7 +2026,10 @@ void wxGenericTreeCtrl::SetImageList(wxImageList *imageList) m_imageListNormal = imageList; m_ownsImageListNormal = FALSE; m_dirty = TRUE; - CalculateLineHeight(); + // Don't do any drawing if we're setting the list to NULL, + // since we may be in the process of deleting the tree control. + if (imageList) + CalculateLineHeight(); } void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList) @@ -2052,7 +2244,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level wxColour colText; if ( item->IsSelected() ) { - colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); } else { @@ -2060,7 +2252,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level if (attr && attr->HasTextColour()) colText = attr->GetTextColour(); else - colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT); + colText = GetForegroundColour(); } // prepare to draw @@ -2114,7 +2306,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level else if (HasFlag(wxTR_TWIST_BUTTONS)) { // draw the twisty button here - + if (HasFlag(wxTR_AQUA_BUTTONS)) { if (item->IsExpanded()) @@ -2336,7 +2528,9 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) // right : open if parent and go next // home : go to root // end : go to last item without opening parents - switch (event.KeyCode()) + // alnum : start or continue searching for the item with this prefix + int keyCode = event.GetKeyCode(); + switch ( keyCode ) { case '+': case WXK_ADD: @@ -2366,12 +2560,20 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) case ' ': case WXK_RETURN: + if ( !event.HasModifiers() ) { wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() ); event.m_item = (long) m_current; event.SetEventObject( this ); GetEventHandler()->ProcessEvent( event ); } + + // in any case, also generate the normal key event for this key, + // even if we generated the ACTIVATED event above: this is what + // wxMSW does and it makes sense because you might not want to + // process ACTIVATED event at all and handle Space and Return + // directly (and differently) which would be impossible otherwise + event.Skip(); break; // up goes to the previous sibling or to the last @@ -2381,7 +2583,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) wxTreeItemId prev = GetPrevSibling( m_key_current ); if (!prev) { - prev = GetParent( m_key_current ); + prev = GetItemParent( m_key_current ); if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT)) { break; // don't go to root if it is hidden @@ -2396,7 +2598,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) // otherwise we return to where we came from SelectItem( prev, unselect_others, extended_select ); m_key_current= (wxGenericTreeItem*) prev.m_pItem; - EnsureVisible( prev ); break; } } @@ -2414,7 +2615,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) SelectItem( prev, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) prev.m_pItem; - EnsureVisible( prev ); } } break; @@ -2422,7 +2622,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) // left arrow goes to the parent case WXK_LEFT: { - wxTreeItemId prev = GetParent( m_current ); + wxTreeItemId prev = GetItemParent( m_current ); if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT)) { // don't go to root if it is hidden @@ -2430,7 +2630,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } if (prev) { - EnsureVisible( prev ); SelectItem( prev, unselect_others, extended_select ); } } @@ -2450,7 +2649,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) wxTreeItemId child = GetFirstChild( m_key_current, cookie ); SelectItem( child, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) child.m_pItem; - EnsureVisible( child ); } else { @@ -2460,7 +2658,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) wxTreeItemId current = m_key_current; while (current && !next) { - current = GetParent( current ); + current = GetItemParent( current ); if (current) next = GetNextSibling( current ); } } @@ -2468,7 +2666,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) { SelectItem( next, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) next.m_pItem; - EnsureVisible( next ); } } } @@ -2494,7 +2691,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) if ( last.IsOk() ) { - EnsureVisible( last ); SelectItem( last, unselect_others, extended_select ); } } @@ -2504,20 +2700,56 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) case WXK_HOME: { wxTreeItemId prev = GetRootItem(); - if (!prev) break; - if (HasFlag(wxTR_HIDE_ROOT)) + if (!prev) + break; + + if ( HasFlag(wxTR_HIDE_ROOT) ) { long dummy; prev = GetFirstChild(prev, dummy); - if (!prev) break; + if (!prev) + break; } - EnsureVisible( prev ); + SelectItem( prev, unselect_others, extended_select ); } break; default: - event.Skip(); + // do not use wxIsalnum() here + if ( !event.HasModifiers() && + ((keyCode >= '0' && keyCode <= '9') || + (keyCode >= 'a' && keyCode <= 'z') || + (keyCode >= 'A' && keyCode <= 'Z' ))) + { + // find the next item starting with the given prefix + char ch = (char)keyCode; + + wxTreeItemId id = FindItem(m_current, m_findPrefix + (wxChar)ch); + if ( !id.IsOk() ) + { + // no such item + break; + } + + SelectItem(id); + + m_findPrefix += ch; + + // also start the timer to reset the current prefix if the user + // doesn't press any more alnum keys soon -- we wouldn't want + // to use this prefix for a new item search + if ( !m_findTimer ) + { + m_findTimer = new wxTreeFindTimer(this); + } + + m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT); + } + else + { + event.Skip(); + } } } @@ -2541,14 +2773,8 @@ wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags) return wxTreeItemId(); } - wxClientDC dc(this); - PrepareDC(dc); - wxCoord x = dc.DeviceToLogicalX( point.x ); - wxCoord y = dc.DeviceToLogicalY( point.y ); - wxGenericTreeItem *hit = m_anchor->HitTest(wxPoint(x, y), - this, - flags, - 0 ); + wxGenericTreeItem *hit = m_anchor->HitTest(CalcUnscrolledPosition(point), + this, flags, 0); if (hit == NULL) { flags = wxTREE_HITTEST_NOWHERE; @@ -2559,8 +2785,8 @@ wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags) // get the bounding rectangle of the item (or of its label only) bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item, - wxRect& rect, - bool WXUNUSED(textOnly)) const + wxRect& rect, + bool WXUNUSED(textOnly)) const { wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") ); @@ -2578,83 +2804,70 @@ bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item, return TRUE; } -/* **** */ - void wxGenericTreeCtrl::Edit( const wxTreeItemId& item ) { - if (!item.IsOk()) return; + wxCHECK_RET( item.IsOk(), _T("can't edit an invalid item") ); - m_currentEdit = (wxGenericTreeItem*) item.m_pItem; + wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem; wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() ); - te.m_item = (long) m_currentEdit; + te.m_item = (long) itemEdit; te.SetEventObject( this ); - GetEventHandler()->ProcessEvent( te ); - - if (!te.IsAllowed()) return; + if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() ) + { + // vetoed by user + return; + } // We have to call this here because the label in // question might just have been added and no screen // update taken place. - if (m_dirty) wxYieldIfNeeded(); - - wxString s = m_currentEdit->GetText(); - int x = m_currentEdit->GetX(); - int y = m_currentEdit->GetY(); - int w = m_currentEdit->GetWidth(); - int h = m_currentEdit->GetHeight(); - - int image_h = 0; - int image_w = 0; + if ( m_dirty ) + wxYieldIfNeeded(); - int image = m_currentEdit->GetCurrentImage(); - if ( image != NO_IMAGE ) - { - if ( m_imageListNormal ) - { - m_imageListNormal->GetSize( image, image_w, image_h ); - image_w += 4; - } - else - { - wxFAIL_MSG(_T("you must create an image list to use images!")); - } - } - x += image_w; - w -= image_w + 4; // I don't know why +4 is needed + m_textCtrl = new wxTreeTextCtrl(this, itemEdit); - wxClientDC dc(this); - PrepareDC( dc ); - x = dc.LogicalToDeviceX( x ); - y = dc.LogicalToDeviceY( y ); + m_textCtrl->SetFocus(); +} - wxTreeTextCtrl *text = new wxTreeTextCtrl(this, -1, - &m_renameAccept, - &m_renameRes, - this, - s, - wxPoint(x-4,y-4), - wxSize(w+11,h+8)); - text->SetFocus(); +// returns a pointer to the text edit control if the item is being +// edited, NULL otherwise (it's assumed that no more than one item may +// be edited simultaneously) +wxTextCtrl* wxGenericTreeCtrl::GetEditControl() const +{ + return m_textCtrl; } -void wxGenericTreeCtrl::OnRenameTimer() +bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item, + const wxString& value) { - Edit( m_current ); + wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() ); + le.m_item = (long) item; + le.SetEventObject( this ); + le.m_label = value; + le.m_editCancelled = FALSE; + + return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed(); } -void wxGenericTreeCtrl::OnRenameAccept() +void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item) { - // TODO if the validator fails this causes a crash + // let owner know that the edit was cancelled wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() ); - le.m_item = (long) m_currentEdit; + le.m_item = (long) item; le.SetEventObject( this ); - le.m_label = m_renameRes; + le.m_label = wxEmptyString; + le.m_editCancelled = FALSE; + GetEventHandler()->ProcessEvent( le ); +} + - if (!le.IsAllowed()) return; - SetItemText( m_currentEdit, m_renameRes ); + +void wxGenericTreeCtrl::OnRenameTimer() +{ + Edit( m_current ); } void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) @@ -2676,21 +2889,15 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) return; } - wxClientDC dc(this); - PrepareDC(dc); - wxCoord x = dc.DeviceToLogicalX( event.GetX() ); - wxCoord y = dc.DeviceToLogicalY( event.GetY() ); + wxPoint pt = CalcUnscrolledPosition(event.GetPosition()); int flags = 0; - wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), - this, - flags, - 0 ); + wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0); if ( event.Dragging() && !m_isDragging ) { if (m_dragCount == 0) - m_dragStart = wxPoint(x,y); + m_dragStart = pt; m_dragCount++; @@ -2767,7 +2974,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId()); event.m_item = (long) item; - event.m_pointDrag = wxPoint(x, y); + event.m_pointDrag = pt; event.SetEventObject(this); (void)GetEventHandler()->ProcessEvent(event); @@ -2793,9 +3000,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) { wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId()); nevent.m_item = (long) item; - CalcScrolledPosition(x, y, - &nevent.m_pointDrag.x, - &nevent.m_pointDrag.y); + nevent.m_pointDrag = CalcScrolledPosition(pt); nevent.SetEventObject(this); GetEventHandler()->ProcessEvent(nevent); } @@ -2807,10 +3012,17 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) (flags & wxTREE_HITTEST_ONITEMLABEL) && HasFlag(wxTR_EDIT_LABELS) ) { - if ( m_renameTimer->IsRunning() ) - m_renameTimer->Stop(); + if ( m_renameTimer ) + { + if ( m_renameTimer->IsRunning() ) + m_renameTimer->Stop(); + } + else + { + m_renameTimer = new wxTreeRenameTimer( this ); + } - m_renameTimer->Start( 100, TRUE ); + m_renameTimer->Start( wxTreeRenameTimer::DELAY, TRUE ); } m_lastOnSame = FALSE; @@ -2850,15 +3062,15 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( event.LeftDClick() ) { // double clicking should not start editing the item label - m_renameTimer->Stop(); + if ( m_renameTimer ) + m_renameTimer->Stop(); + m_lastOnSame = FALSE; // send activate event first wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() ); nevent.m_item = (long) item; - CalcScrolledPosition(x, y, - &nevent.m_pointDrag.x, - &nevent.m_pointDrag.y); + nevent.m_pointDrag = CalcScrolledPosition(pt); nevent.SetEventObject( this ); if ( !GetEventHandler()->ProcessEvent( nevent ) ) { @@ -2894,7 +3106,10 @@ void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) wxCoord text_w = 0; wxCoord text_h = 0; - if (item->IsBold()) + wxTreeItemAttr *attr = item->GetAttributes(); + if ( attr && attr->HasFont() ) + dc.SetFont(attr->GetFont()); + else if ( item->IsBold() ) dc.SetFont(m_boldFont); dc.GetTextExtent( item->GetText(), &text_w, &text_h ); @@ -2988,20 +3203,14 @@ void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) { if (m_dirty) return; - wxClientDC dc(this); - PrepareDC(dc); - - int cw = 0; - int ch = 0; - GetClientSize( &cw, &ch ); + wxSize client = GetClientSize(); wxRect rect; - rect.x = dc.LogicalToDeviceX( 0 ); - rect.width = cw; - rect.y = dc.LogicalToDeviceY( item->GetY() ); - rect.height = ch; + CalcScrolledPosition(0, item->GetY(), NULL, &rect.y); + rect.width = client.x; + rect.height = client.y; - Refresh( TRUE, &rect ); + Refresh(TRUE, &rect); AdjustMyScrollbars(); } @@ -3010,20 +3219,12 @@ void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item ) { if (m_dirty) return; - wxClientDC dc(this); - PrepareDC( dc ); - - int cw = 0; - int ch = 0; - GetClientSize( &cw, &ch ); - wxRect rect; - rect.x = dc.LogicalToDeviceX( 0 ); - rect.y = dc.LogicalToDeviceY( item->GetY() ); - rect.width = cw; + CalcScrolledPosition(0, item->GetY(), NULL, &rect.y); + rect.width = GetClientSize().x; rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6; - Refresh( TRUE, &rect ); + Refresh(TRUE, &rect); } void wxGenericTreeCtrl::RefreshSelected()