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;
};
// control used for in-place edit
DECLARE_EVENT_TABLE()
};
+// 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;
+};
+
// a tree item
class WXDLLEXPORT wxGenericTreeItem
{
m_isDragging = FALSE;
m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL;
- m_renameTimer = new wxTreeRenameTimer( this );
+ m_renameTimer = NULL;
+ m_findTimer = NULL;
+
m_lastOnSame = FALSE;
m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
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;
}
// -----------------------------------------------------------------------------
return wxTreeItemId();
}
+// 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
// -----------------------------------------------------------------------------
// if root is hidden, make sure we can navigate
// into children
m_anchor->SetHasPlus();
- Expand(m_anchor);
+ m_anchor->Expand();
+ CalculatePositions();
}
if (!HasFlag(wxTR_MULTIPLE))
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;
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;
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() )
}
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") );
}
void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
- wxArrayTreeItemIds &array) const
+ wxArrayTreeItemIds &array) const
{
if ( item->IsSelected() )
array.Add(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();
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)
// 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.KeyCode();
+ switch ( keyCode )
{
case '+':
case WXK_ADD:
// 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;
}
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
{
{
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();
+ }
}
}
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;
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 x, y;
+ CalcScrolledPosition(m_currentEdit->GetX(), m_currentEdit->GetY(), &x, &y);
int image_h = 0;
int image_w = 0;
x += image_w;
w -= image_w + 4; // I don't know why +4 is needed
- wxClientDC dc(this);
- PrepareDC( dc );
- x = dc.LogicalToDeviceX( x );
- y = dc.LogicalToDeviceY( y );
-
wxTreeTextCtrl *text = new wxTreeTextCtrl(this, -1,
&m_renameAccept,
&m_renameRes,
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++;
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);
{
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);
}
(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
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 ) )
{
{
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();
}
{
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()