// 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"
+#include "wx/renderer.h"
+
// -----------------------------------------------------------------------------
// array types
// -----------------------------------------------------------------------------
class WXDLLEXPORT wxGenericTreeItem;
-WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
+WX_DEFINE_EXPORTED_ARRAY_NO_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems);
// ----------------------------------------------------------------------------
// constants
static const int PIXELS_PER_UNIT = 10;
-// ----------------------------------------------------------------------------
-// Aqua arrows
-// ----------------------------------------------------------------------------
-
-/* 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
// -----------------------------------------------------------------------------
private:
wxGenericTreeCtrl *m_owner;
+
+ DECLARE_NO_COPY_CLASS(wxTreeRenameTimer)
};
// control used for in-place edit
bool m_finished;
DECLARE_EVENT_TABLE()
+ DECLARE_NO_COPY_CLASS(wxTreeTextCtrl)
};
// timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
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)
};
// =============================================================================
EVT_CHAR (wxGenericTreeCtrl::OnChar)
EVT_SET_FOCUS (wxGenericTreeCtrl::OnSetFocus)
EVT_KILL_FOCUS (wxGenericTreeCtrl::OnKillFocus)
- EVT_IDLE (wxGenericTreeCtrl::OnIdle)
END_EVENT_TABLE()
#if !defined(__WXMSW__) || defined(__WIN16__) || defined(__WXUNIVERSAL__)
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;
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;
style |= wxTR_NO_LINES;
if (major < 10)
style |= wxTR_ROW_LINES;
- if (major >= 10)
- style |= wxTR_AQUA_BUTTONS;
-#endif
-
- if (style & wxTR_AQUA_BUTTONS)
- {
- m_arrowRight = new wxBitmap( aqua_arrow_right );
- m_arrowDown = new wxBitmap( aqua_arrow_down );
- }
- else
- {
- m_arrowRight = NULL;
- m_arrowDown = NULL;
- }
+#endif // __WXMAC__
wxScrolledWindow::Create( parent, id, pos, size,
style|wxHSCROLL|wxVSCROLL, name );
delete m_hilightBrush;
delete m_hilightUnfocusedBrush;
- delete m_arrowRight;
- delete m_arrowDown;
-
DeleteAllItems();
delete m_renameTimer;
return ((wxGenericTreeItem*) item.m_pItem)->GetParent();
}
-wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
+wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item,
+ wxTreeItemIdValue& cookie) const
{
wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
return GetNextChild(item, cookie);
}
-wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
+wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item,
+ wxTreeItemIdValue& cookie) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+
+ wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
+
+ // it's ok to cast cookie to size_t, we never have indices big enough to
+ // overflow "void *"
+ size_t *pIndex = (size_t *)&cookie;
+ if ( *pIndex < children.Count() )
+ {
+ return children.Item((*pIndex)++);
+ }
+ else
+ {
+ // there are no more of them
+ return wxTreeItemId();
+ }
+}
+
+#if WXWIN_COMPATIBILITY_2_4
+
+wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item,
+ long& cookie) const
+{
+ wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
+
+ cookie = 0;
+ return GetNextChild(item, cookie);
+}
+
+wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item,
+ long& cookie) const
{
wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
}
}
+#endif // WXWIN_COMPATIBILITY_2_4
+
wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const
{
wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
if ( data != NULL )
{
- data->m_pItem = (long) item;
+ data->m_pItem = item;
}
parent->Insert( item, previous );
image, selImage, data);
if ( data != NULL )
{
- data->m_pItem = (long) m_anchor;
+ data->m_pItem = m_anchor;
}
if (HasFlag(wxTR_HIDE_ROOT))
void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
{
wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
- event.m_item = (long) item;
+ event.m_item = item;
event.SetEventObject( this );
ProcessEvent( event );
}
// don't keep stale pointers around!
if ( IsDescendantOf(item, m_key_current) )
{
- m_key_current = parent;
+ // 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;
+ }
+
+ // 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) )
{
- m_current = parent;
+ // 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
return;
wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
- event.m_item = (long) item;
+ event.m_item = item;
event.SetEventObject( this );
if ( ProcessEvent( event ) && !event.IsAllowed() )
return;
}
- long cookie;
+ wxTreeItemIdValue cookie;
wxTreeItemId child = GetFirstChild(item, cookie);
while ( child.IsOk() )
{
return;
wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
- event.m_item = (long) item;
+ event.m_item = item;
event.SetEventObject( this );
if ( ProcessEvent( event ) && !event.IsAllowed() )
{
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())
{
wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
+ m_select_me = NULL;
+
bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
}
wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
- event.m_item = (long) item;
- event.m_itemOld = (long) m_current;
+ event.m_item = item;
+ event.m_itemOld = m_current;
event.SetEventObject( this );
// TODO : Here we don't send any selection mode yet !
if ( HasFlag(wxTR_HIDE_ROOT) )
{
- while ( parent != m_anchor )
+ while ( parent && parent != m_anchor )
{
Expand(parent);
parent = parent->GetParent();
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
{
dc.SetPen(m_dottedPen);
dc.SetTextForeground(*wxBLACK);
- if (item->HasPlus() && HasButtons()) // should the item show a button?
+ if ( !HasFlag(wxTR_NO_LINES) )
{
- if (!HasFlag(wxTR_NO_LINES))
- {
- if (x > (signed)m_indent)
- dc.DrawLine(x - m_indent, y_mid, x - 5, y_mid);
- else if (HasFlag(wxTR_LINES_AT_ROOT))
- dc.DrawLine(3, y_mid, x - 5, y_mid);
- dc.DrawLine(x + 5, y_mid, x + m_spacing, y_mid);
- }
+ // draw the horizontal line here
+ int x_start = x;
+ if (x > (signed)m_indent)
+ x_start -= m_indent;
+ else if (HasFlag(wxTR_LINES_AT_ROOT))
+ x_start = 3;
+ dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
+ }
- if (m_imageListButtons != NULL)
+ // should the item show a button?
+ if ( item->HasPlus() && HasButtons() )
+ {
+ if ( m_imageListButtons )
{
// draw the image button here
- int image_h = 0, image_w = 0, image = wxTreeItemIcon_Normal;
- if (item->IsExpanded()) image = wxTreeItemIcon_Expanded;
- if (item->IsSelected())
+ int image_h = 0,
+ image_w = 0;
+ int image = item->IsExpanded() ? wxTreeItemIcon_Expanded
+ : wxTreeItemIcon_Normal;
+ if ( item->IsSelected() )
image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal;
+
m_imageListButtons->GetSize(image, image_w, image_h);
- int xx = x - (image_w>>1);
- int yy = y_mid - (image_h>>1);
- dc.SetClippingRegion(xx, yy, image_w, image_h);
+ int xx = x - image_w/2;
+ int yy = y_mid - image_h/2;
+
+ wxDCClipper clip(dc, xx, yy, image_w, image_h);
m_imageListButtons->Draw(image, dc, xx, yy,
wxIMAGELIST_DRAW_TRANSPARENT);
- dc.DestroyClippingRegion();
}
- else if (HasFlag(wxTR_TWIST_BUTTONS))
+ else // no custom buttons
{
- // draw the twisty button here
-
- if (HasFlag(wxTR_AQUA_BUTTONS))
- {
- if (item->IsExpanded())
- dc.DrawBitmap( *m_arrowDown, x-5, y_mid-6, TRUE );
- else
- dc.DrawBitmap( *m_arrowRight, x-5, y_mid-6, TRUE );
- }
- else
- {
- dc.SetBrush(*m_hilightBrush);
- dc.SetPen(*wxBLACK_PEN);
- wxPoint button[3];
-
- if (item->IsExpanded())
- {
- button[0].x = x-5;
- button[0].y = y_mid-2;
- button[1].x = x+5;
- button[1].y = y_mid-2;
- button[2].x = x;
- button[2].y = y_mid+3;
- }
- else
- {
- button[0].y = y_mid-5;
- button[0].x = x-2;
- button[1].y = y_mid+5;
- button[1].x = x-2;
- button[2].y = y_mid;
- button[2].x = x+3;
- }
- dc.DrawPolygon(3, button);
- dc.SetPen(m_dottedPen);
- }
- }
- else // if (HasFlag(wxTR_HAS_BUTTONS))
- {
- // draw the plus sign here
- dc.SetPen(*wxGREY_PEN);
- dc.SetBrush(*wxWHITE_BRUSH);
- dc.DrawRectangle(x-5, y_mid-4, 11, 9);
- dc.SetPen(*wxBLACK_PEN);
- dc.DrawLine(x-2, y_mid, x+3, y_mid);
- if (!item->IsExpanded())
- dc.DrawLine(x, y_mid-2, x, y_mid+3);
- dc.SetPen(m_dottedPen);
+ static const int wImage = 10;
+ static const int hImage = 12;
+
+ wxRendererNative::Get().DrawTreeItemButton
+ (
+ this,
+ dc,
+ wxRect(x - wImage/2,
+ y_mid - hImage/2,
+ wImage, hImage),
+ item->IsExpanded()
+ ? wxCONTROL_EXPANDED
+ : 0
+ );
}
}
- else if (!HasFlag(wxTR_NO_LINES)) // no button; maybe a line?
- {
- // draw the horizontal line here
- int x_start = x;
- if (x > (signed)m_indent)
- x_start -= m_indent;
- else if (HasFlag(wxTR_LINES_AT_ROOT))
- x_start = 3;
- dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
- }
}
if (item->IsExpanded())
// 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);
}
}
}
// home : go to root
// end : go to last item without opening parents
// alnum : start or continue searching for the item with this prefix
- int keyCode = event.KeyCode();
+ int keyCode = event.GetKeyCode();
switch ( keyCode )
{
case '+':
if ( !event.HasModifiers() )
{
wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
- event.m_item = (long) m_current;
+ event.m_item = m_current;
event.SetEventObject( this );
GetEventHandler()->ProcessEvent( event );
}
}
if (prev)
{
- long cookie = 0;
+ wxTreeItemIdValue cookie;
wxTreeItemId current = m_key_current;
// TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
if (current == GetFirstChild( prev, cookie ))
{
if (IsExpanded(m_key_current) && HasChildren(m_key_current))
{
- long cookie = 0;
+ wxTreeItemIdValue cookie;
wxTreeItemId child = GetFirstChild( m_key_current, cookie );
SelectItem( child, unselect_others, extended_select );
m_key_current=(wxGenericTreeItem*) child.m_pItem;
if (!next)
{
wxTreeItemId current = m_key_current;
- while (current && !next)
+ while (current.IsOk() && !next)
{
current = GetItemParent( current );
if (current) next = GetNextSibling( current );
if ( HasFlag(wxTR_HIDE_ROOT) )
{
- long dummy;
- prev = GetFirstChild(prev, dummy);
+ wxTreeItemIdValue cookie;
+ prev = GetFirstChild(prev, cookie);
if (!prev)
break;
}
wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem;
wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() );
- te.m_item = (long) itemEdit;
+ te.m_item = itemEdit;
te.SetEventObject( this );
if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() )
{
const wxString& value)
{
wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
- le.m_item = (long) item;
+ le.m_item = item;
le.SetEventObject( this );
le.m_label = value;
le.m_editCancelled = FALSE;
{
// let owner know that the edit was cancelled
wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
- le.m_item = (long) item;
+ le.m_item = item;
le.SetEventObject( this );
le.m_label = wxEmptyString;
- le.m_editCancelled = FALSE;
+ le.m_editCancelled = TRUE;
GetEventHandler()->ProcessEvent( le );
}
: wxEVT_COMMAND_TREE_BEGIN_DRAG;
wxTreeEvent nevent( command, GetId() );
- nevent.m_item = (long) m_current;
+ nevent.m_item = m_current;
nevent.SetEventObject(this);
// by default the dragging is not supported, the user code must
// generate the drag end event
wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId());
- event.m_item = (long) item;
+ event.m_item = item;
event.m_pointDrag = pt;
event.SetEventObject(this);
if ( event.RightDown() )
{
wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId());
- nevent.m_item = (long) item;
+ nevent.m_item = item;
nevent.m_pointDrag = CalcScrolledPosition(pt);
nevent.SetEventObject(this);
GetEventHandler()->ProcessEvent(nevent);
}
else if ( event.LeftUp() )
{
+ // this facilitates multiple-item drag-and-drop
+
+ if (item && HasFlag(wxTR_MULTIPLE))
+ {
+ wxArrayTreeItemIds selections;
+ size_t count = GetSelections(selections);
+
+ if (count > 1 &&
+ !event.ControlDown() &&
+ !event.ShiftDown())
+ {
+ SelectItem(item, true, false);
+ }
+ }
+
if ( m_lastOnSame )
{
if ( (item == m_current) &&
return;
}
- // how should the selection work for this event?
- bool is_multiple, extended_select, unselect_others;
- EventFlagsToSelType(GetWindowStyleFlag(),
- event.ShiftDown(),
- event.ControlDown(),
- is_multiple, extended_select, unselect_others);
- SelectItem(item, unselect_others, extended_select);
+ // clear the previously selected items, if the
+ // user clicked outside of the present selection.
+ // otherwise, perform the deselection on mouse-up.
+ // this allows multiple drag and drop to work.
+
+ if (!IsSelected(item))
+ {
+ // how should the selection work for this event?
+ bool is_multiple, extended_select, unselect_others;
+ EventFlagsToSelType(GetWindowStyleFlag(),
+ event.ShiftDown(),
+ event.ControlDown(),
+ is_multiple, extended_select, unselect_others);
+
+ SelectItem(item, unselect_others, extended_select);
+ }
+
// For some reason, Windows isn't recognizing a left double-click,
// so we need to simulate it here. Allow 200 milliseconds for now.
// send activate event first
wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
- nevent.m_item = (long) item;
+ nevent.m_item = item;
nevent.m_pointDrag = CalcScrolledPosition(pt);
nevent.SetEventObject( this );
if ( !GetEventHandler()->ProcessEvent( nevent ) )
}
}
-void wxGenericTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
+void wxGenericTreeCtrl::OnInternalIdle()
{
+ wxWindow::OnInternalIdle();
+
+ // 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 */