// Created: 01/02/97
// Modified: 22/10/98 - almost total rewrite, simpler interface (VZ)
// Id: $Id$
-// Copyright: (c) 1998 Robert Roebling, Julian Smart and Markus Holzem
+// Copyright: (c) 1998 Robert Roebling and Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/settings.h"
#include "wx/dcclient.h"
+#ifdef __WXMAC__
+ #include "wx/mac/private.h"
+#endif
+
// -----------------------------------------------------------------------------
// array types
// -----------------------------------------------------------------------------
class WXDLLEXPORT wxGenericTreeItem;
WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
-//WX_DEFINE_OBJARRAY(wxArrayTreeItemIds);
// ----------------------------------------------------------------------------
// constants
static const int NO_IMAGE = -1;
-#define PIXELS_PER_UNIT 10
+static const int PIXELS_PER_UNIT = 10;
// ----------------------------------------------------------------------------
// Aqua arrows
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
short m_images[wxTreeItemIcon_Max];
wxCoord m_x; // (virtual) offset from top
- short m_y; // (virtual) offset from left
+ wxCoord m_y; // (virtual) offset from left
short m_width; // width of this item
unsigned char m_height; // height of this item
// 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)
};
// =============================================================================
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)
// -----------------------------------------------------------------------------
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 )
{
- (*m_accept) = TRUE;
- (*m_res) = GetValue();
+ // nothing changed, always accept
+ return TRUE;
+ }
- if ((*m_res) != m_startValue)
- m_owner->OnRenameAccept();
+ if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
+ {
+ // vetoed by the user
+ return FALSE;
+ }
- if (!wxPendingDelete.Member(this))
- wxPendingDelete.Append(this);
+ // accepted, do rename the item
+ m_owner->SetItemText(m_itemEdited, value);
- 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();
+ }
}
// -----------------------------------------------------------------------------
void wxGenericTreeCtrl::Init()
{
- m_current = m_key_current = m_anchor = (wxGenericTreeItem *) NULL;
+ m_current = m_key_current = m_anchor = m_select_me = (wxGenericTreeItem *) NULL;
m_hasFocus = FALSE;
m_dirty = FALSE;
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::GetFont( wxSYS_DEFAULT_GUI_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());
}
bool wxGenericTreeCtrl::Create(wxWindow *parent,
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;
}
// -----------------------------------------------------------------------------
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;
}
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") );
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;
}
// navigation
// -----------------------------------------------------------------------------
-wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const
+wxTreeItemId wxGenericTreeCtrl::GetItemParent(const wxTreeItemId& item) const
{
wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
do
{
toFind = GetNextSibling(p);
- p = GetParent(p);
+ p = GetItemParent(p);
} while (p.IsOk() && !toFind.IsOk());
return toFind;
}
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
// -----------------------------------------------------------------------------
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);
}
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();
+ // Don't silently change the selection:
+ // do it properly in idle time, so event
+ // handlers get called.
+
+ // m_key_current = parent;
+ m_key_current = NULL;
}
- wxGenericTreeItem *parent = item->GetParent();
+ // m_select_me records whether we need to select
+ // a different item, in idle time.
+ if ( m_select_me && IsDescendantOf(item, m_select_me) )
+ {
+ m_select_me = parent;
+ }
+
+ if ( IsDescendantOf(item, m_current) )
+ {
+ // Don't silently change the selection:
+ // do it properly in idle time, so event
+ // handlers get called.
+
+ // m_current = parent;
+ m_current = NULL;
+ m_select_me = 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;
{
if ( m_anchor )
{
- m_dirty = TRUE;
-
- m_anchor->DeleteChildren(this);
- delete m_anchor;
-
- m_anchor = NULL;
+ Delete(m_anchor);
}
}
wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") );
- wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
+ wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
_T("can't expand hidden root") );
if ( !item->HasPlus() )
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(),
+ wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
_T("can't collapse hidden root") );
wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
{
m_current->SetHilight( FALSE );
RefreshLine( m_current );
+
+ m_current = NULL;
+ m_select_me = NULL;
}
}
{
// item2 is not necessary after item1
wxGenericTreeItem *first=NULL, *last=NULL;
+ m_select_me = NULL;
// choice first' and 'last' between item1 and item2
if (item1->GetY()<item2->GetY())
}
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") );
+ m_select_me = NULL;
+
bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
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 );
}
void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
- wxArrayTreeItemIds &array) const
+ wxArrayTreeItemIds &array) const
{
if ( item->IsSelected() )
array.Add(wxTreeItemId(item));
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)
if ( item->IsSelected() )
{
+// under mac selections are only a rectangle in case they don't have the focus
+#ifdef __WXMAC__
+ if ( !m_hasFocus )
+ {
+ dc.SetBrush( *wxTRANSPARENT_BRUSH ) ;
+ dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) , 1 , wxSOLID ) ) ;
+ }
+ else
+ {
+ dc.SetBrush( *m_hilightBrush ) ;
+ }
+#else
dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
+#endif
}
else
{
if (HasFlag(wxTR_AQUA_BUTTONS))
{
+ // This causes update problems, so disabling for now.
+#if 0 // def __WXMAC__
+ wxMacPortSetter helper(&dc) ;
+ wxMacWindowClipper clipper(this) ;
+ wxDC::MacSetupBackgroundForCurrentPort( MacGetBackgroundBrush() ) ;
+
+ int loc_x = x - 5 ;
+ int loc_y = y_mid - 6 ;
+ MacWindowToRootWindow( & loc_x , & loc_y ) ;
+ Rect bounds = { loc_y , loc_x , loc_y + 18 , loc_x + 12 } ;
+ ThemeButtonDrawInfo info = { kThemeStateActive , item->IsExpanded() ? kThemeDisclosureDown : kThemeDisclosureRight ,
+ kThemeAdornmentNone };
+ DrawThemeButton( &bounds, kThemeDisclosureButton ,
+ &info , NULL , NULL , NULL , NULL ) ;
+#else
if (item->IsExpanded())
dc.DrawBitmap( *m_arrowDown, x-5, y_mid-6, TRUE );
else
dc.DrawBitmap( *m_arrowRight, x-5, y_mid-6, TRUE );
+#endif
}
else
{
// draw line down to last child
oldY += GetLineHeight(children[n-1])>>1;
if (HasButtons()) y_mid += 5;
- dc.DrawLine(x, y_mid, x, oldY);
+
+ // Only draw the portion of the line that is visible, in case it is huge
+ wxCoord xOrigin=0, yOrigin=0, width, height;
+ dc.GetDeviceOrigin(&xOrigin, &yOrigin);
+ yOrigin = abs(yOrigin);
+ GetClientSize(&width, &height);
+
+ // Move end points to the begining/end of the view?
+ if (y_mid < yOrigin)
+ y_mid = yOrigin;
+ if (oldY > yOrigin + height)
+ oldY = yOrigin + height;
+
+ // after the adjustments if y_mid is larger than oldY then the line
+ // isn't visible at all so don't draw anything
+ if (y_mid < oldY)
+ dc.DrawLine(x, y_mid, x, oldY);
}
}
}
// 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:
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
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
// otherwise we return to where we came from
SelectItem( prev, unselect_others, extended_select );
m_key_current= (wxGenericTreeItem*) prev.m_pItem;
- EnsureVisible( prev );
break;
}
}
SelectItem( prev, unselect_others, extended_select );
m_key_current=(wxGenericTreeItem*) prev.m_pItem;
- EnsureVisible( prev );
}
}
break;
// 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
}
if (prev)
{
- EnsureVisible( prev );
SelectItem( prev, unselect_others, extended_select );
}
}
wxTreeItemId child = GetFirstChild( m_key_current, cookie );
SelectItem( child, unselect_others, extended_select );
m_key_current=(wxGenericTreeItem*) child.m_pItem;
- EnsureVisible( child );
}
else
{
wxTreeItemId current = m_key_current;
while (current && !next)
{
- current = GetParent( current );
+ current = GetItemParent( current );
if (current) next = GetNextSibling( current );
}
}
{
SelectItem( next, unselect_others, extended_select );
m_key_current=(wxGenericTreeItem*) next.m_pItem;
- EnsureVisible( next );
}
}
}
if ( last.IsOk() )
{
- EnsureVisible( last );
SelectItem( last, unselect_others, extended_select );
}
}
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();
+ }
}
}
// 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") );
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();
+ if ( m_dirty )
+ wxYieldIfNeeded();
- wxString s = m_currentEdit->GetText();
- int w = m_currentEdit->GetWidth();
- int h = m_currentEdit->GetHeight();
- int x, y;
- CalcScrolledPosition(m_currentEdit->GetX(), m_currentEdit->GetY(), &x, &y);
+ m_textCtrl = new wxTreeTextCtrl(this, itemEdit);
- int image_h = 0;
- int image_w = 0;
-
- 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->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 )
(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;
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
void wxGenericTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
{
+ // Check if we need to select the root item
+ // because nothing else has been selected.
+ // Delaying it means that we can invoke event handlers
+ // as required, when a first item is selected.
+ if (!HasFlag(wxTR_MULTIPLE) && !GetSelection().IsOk())
+ {
+ if (m_select_me)
+ SelectItem(m_select_me);
+ else if (GetRootItem().IsOk())
+ SelectItem(GetRootItem());
+ }
+
/* after all changes have been done to the tree control,
* we actually redraw the tree when everything is over */
wxSize client = GetClientSize();
wxRect rect;
- CalcScrolledPosition(0, item->GetY(), &rect.x, &rect.y);
+ CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
rect.width = client.x;
rect.height = client.y;
if (m_dirty) return;
wxRect rect;
- CalcScrolledPosition(0, item->GetY(), &rect.x, &rect.y);
+ CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
rect.width = GetClientSize().x;
rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;