static const int NO_IMAGE = -1;
+#define PIXELS_PER_UNIT 10
+
// -----------------------------------------------------------------------------
// private classes
// -----------------------------------------------------------------------------
+// timer used for enabling in-place edit
+class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
+{
+public:
+ wxTreeRenameTimer( wxTreeCtrl *owner );
+
+ void Notify();
+
+private:
+ wxTreeCtrl *m_owner;
+};
+
+// control used for in-place edit
+class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl
+{
+public:
+ wxTreeTextCtrl() { }
+ wxTreeTextCtrl( wxWindow *parent,
+ const wxWindowID id,
+ bool *accept,
+ wxString *res,
+ wxTreeCtrl *owner,
+ const wxString &value = wxEmptyString,
+ const wxPoint &pos = wxDefaultPosition,
+ const wxSize &size = wxDefaultSize,
+ int style = 0,
+ const wxValidator& validator = wxDefaultValidator,
+ const wxString &name = wxTextCtrlNameStr );
+
+ void OnChar( wxKeyEvent &event );
+ void OnKillFocus( wxFocusEvent &event );
+
+private:
+ bool *m_accept;
+ wxString *m_res;
+ wxTreeCtrl *m_owner;
+ wxString m_startValue;
+
+ DECLARE_EVENT_TABLE()
+ DECLARE_DYNAMIC_CLASS(wxTreeTextCtrl);
+};
+
// a tree item
class WXDLLEXPORT wxGenericTreeItem
{
const wxString& GetText() const { return m_text; }
int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const
- { return m_images[which]; }
+ { return m_images[which]; }
wxTreeItemData *GetData() const { return m_data; }
// returns the current image for the item (depending on its
// children but has a [+] button
int m_isBold :1; // render the label in bold font
- int m_x, m_y;
- long m_height, m_width;
+ wxCoord m_x, m_y;
+ wxCoord m_height, m_width;
int m_xCross, m_yCross;
int m_level;
// implementation
// =============================================================================
+// ----------------------------------------------------------------------------
+// private functions
+// ----------------------------------------------------------------------------
+
+// translate the key or mouse event flags to the type of selection we're
+// dealing with
+static void EventFlagsToSelType(long style,
+ bool shiftDown,
+ bool ctrlDown,
+ bool &is_multiple,
+ bool &extended_select,
+ bool &unselect_others)
+{
+ is_multiple = (style & wxTR_MULTIPLE) != 0;
+ extended_select = shiftDown && is_multiple;
+ unselect_others = !(extended_select || (ctrlDown && is_multiple));
+}
// -----------------------------------------------------------------------------
// wxTreeRenameTimer (internal)
EVT_KILL_FOCUS (wxTreeTextCtrl::OnKillFocus)
END_EVENT_TABLE()
-wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent, const wxWindowID id,
- bool *accept, wxString *res, wxTreeCtrl *owner,
- const wxString &value, const wxPoint &pos, const wxSize &size,
-#if wxUSE_VALIDATORS
- int style, const wxValidator& validator, const wxString &name ) :
-#endif
- wxTextCtrl( parent, id, value, pos, size, style, validator, name )
+wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent,
+ const wxWindowID id,
+ bool *accept,
+ wxString *res,
+ wxTreeCtrl *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;
m_owner = owner;
(*m_accept) = FALSE;
- (*m_res) = "";
+ (*m_res) = wxEmptyString;
m_startValue = value;
}
{
(*m_accept) = TRUE;
(*m_res) = GetValue();
- m_owner->SetFocus();
+
+ if (!wxPendingDelete.Member(this))
+ wxPendingDelete.Append(this);
+
+ if ((*m_accept) && ((*m_res) != m_startValue))
+ m_owner->OnRenameAccept();
+
return;
}
if (event.m_keyCode == WXK_ESCAPE)
{
(*m_accept) = FALSE;
(*m_res) = "";
- m_owner->SetFocus();
+
+ if (!wxPendingDelete.Member(this))
+ wxPendingDelete.Append(this);
+
return;
}
event.Skip();
void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
{
- if (wxPendingDelete.Member(this)) return;
-
- wxPendingDelete.Append(this);
+ if (!wxPendingDelete.Member(this))
+ wxPendingDelete.Append(this);
if ((*m_accept) && ((*m_res) != m_startValue))
m_owner->OnRenameAccept();
}
-#define PIXELS_PER_UNIT 10
// -----------------------------------------------------------------------------
// wxTreeEvent
// -----------------------------------------------------------------------------
m_parent = parent;
m_attr = (wxTreeItemAttr *)NULL;
-
+
// We don't know the height here yet.
m_width = 0;
m_height = 0;
{
if ((point.y > m_y) && (point.y < m_y + theTree->GetLineHeight(this)))
{
- if (point.y<m_y+theTree->GetLineHeight(this)/2)
+ if (point.y < m_y+theTree->GetLineHeight(this)/2 )
flags |= wxTREE_HITTEST_ONITEMUPPERPART;
else
flags |= wxTREE_HITTEST_ONITEMLOWERPART;
}
flags|=wxTREE_HITTEST_NOWHERE;
-
+
return (wxGenericTreeItem*) NULL;
}
m_imageListState = (wxImageList *) NULL;
m_dragCount = 0;
+ m_isDragging = FALSE;
+ m_dropTarget =
+ m_oldSelection = (wxGenericTreeItem *)NULL;
m_renameTimer = new wxTreeRenameTimer( this );
bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
const wxPoint& pos, const wxSize& size,
long style,
-#if wxUSE_VALIDATORS
- const wxValidator &validator,
-#endif
- const wxString& name )
+ const wxValidator &validator,
+ const wxString& name )
{
Init();
SetValidator( validator );
#endif
- SetBackgroundColour( *wxWHITE );
+ SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
// m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86
m_dottedPen = wxPen( "grey", 0, 0 );
{
m_indent = indent;
m_dirty = TRUE;
- Refresh();
}
void wxTreeCtrl::SetSpacing(unsigned int spacing)
{
m_spacing = spacing;
m_dirty = TRUE;
- Refresh();
}
size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
wxArrayGenericTreeItems& children = item.m_pItem->GetChildren();
if ( (size_t)cookie < children.Count() )
{
- return children.Item(cookie++);
+ return children.Item((size_t)cookie++);
}
else
{
if (!HasFlag(wxTR_MULTIPLE))
{
m_current = m_key_current = m_anchor;
- m_current->SetHilight( TRUE );
+ m_current->SetHilight( TRUE );
}
Refresh();
int index = parent->GetChildren().Index(idPrevious.m_pItem);
wxASSERT_MSG( index != wxNOT_FOUND,
wxT("previous item in wxTreeCtrl::InsertItem() is not a sibling") );
-
+
return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
}
crt_item->SetHilight(select);
RefreshLine(crt_item);
- if (crt_item==last_item)
+ if (crt_item==last_item)
return TRUE;
if (crt_item->HasChildren())
size_t count = children.Count();
for ( size_t n = 0; n < count; ++n )
{
- if (TagAllChildrenUntilLast(children[n], last_item, select))
+ if (TagAllChildrenUntilLast(children[n], last_item, select))
return TRUE;
- }
+ }
}
return FALSE;
// choice first' and 'last' between item1 and item2
if (item1->GetY()<item2->GetY())
- {
+ {
first=item1;
last=item2;
}
{
m_imageListNormal = imageList;
+ if ( !m_imageListNormal )
+ return;
+
// Calculate a m_lineHeight value from the image sizes.
// May be toggle off. Then wxTreeCtrl will spread when
// necessary (which might look ugly).
-#if 1
wxClientDC dc(this);
m_lineHeight = (int)(dc.GetCharHeight() + 4);
int width = 0, height = 0,
n = m_imageListNormal->GetImageCount();
-
+
for (int i = 0; i < n ; i++)
{
m_imageListNormal->GetSize(i, width, height);
if (height > m_lineHeight) m_lineHeight = height;
}
- if (m_lineHeight < 40)
+ if (m_lineHeight < 40)
m_lineHeight += 2; // at least 2 pixels
- else
+ else
m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing
-#endif
}
void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
int image = item->GetCurrentImage();
if ( image != NO_IMAGE )
{
- m_imageListNormal->GetSize( image, image_w, image_h );
- image_w += 4;
+ if ( m_imageListNormal )
+ {
+ m_imageListNormal->GetSize( image, image_w, image_h );
+ image_w += 4;
+ }
+ else
+ {
+ image = NO_IMAGE;
+ }
}
int total_h = GetLineHeight(item);
+ if (item->IsSelected())
+ dc.SetBrush(*m_hilightBrush);
+ else
+ {
+ wxColour colBg;
+ if ( attr && attr->HasBackgroundColour() )
+ colBg = attr->GetBackgroundColour();
+ else
+ colBg = m_backgroundColour;
+ dc.SetBrush(wxBrush(colBg, wxSOLID));
+ }
+
dc.DrawRectangle( item->GetX()-2, item->GetY(), item->GetWidth()+2, total_h );
if ( image != NO_IMAGE )
dc.DestroyClippingRegion();
}
- bool hasBgCol = attr && attr->HasBackgroundColour();
- dc.SetBackgroundMode(hasBgCol ? wxSOLID : wxTRANSPARENT);
- if ( hasBgCol )
- dc.SetTextBackground(attr->GetBackgroundColour());
- dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY()
+ dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.DrawText( item->GetText(), image_w + item->GetX(), (wxCoord)item->GetY()
+ ((total_h > text_h) ? (total_h - text_h)/2 : 0));
// restore normal font
}
wxPen *pen = wxTRANSPARENT_PEN;
- wxBrush *brush; // FIXME is this really needed?
wxColour colText;
if ( item->IsSelected() )
{
colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
- brush = m_hilightBrush;
-
if ( m_hasFocus )
pen = wxBLACK_PEN;
colText = attr->GetTextColour();
else
colText = *wxBLACK;
-
- brush = wxWHITE_BRUSH;
}
// prepare to draw
dc.SetTextForeground(colText);
dc.SetPen(*pen);
- dc.SetBrush(*brush);
// draw
PaintItem(item, dc);
}
}
-void wxTreeCtrl::DrawBorder(wxTreeItemId &item)
+void wxTreeCtrl::DrawDropEffect(wxGenericTreeItem *item)
{
- if (!item) return;
+ if ( item )
+ {
+ if ( item->HasPlus() )
+ {
+ // it's a folder, indicate it by a border
+ DrawBorder(item);
+ }
+ else
+ {
+ // draw a line under the drop target because the item will be
+ // dropped there
+ DrawLine(item, TRUE /* below */);
+ }
+
+ SetCursor(wxCURSOR_BULLSEYE);
+ }
+ else
+ {
+ // can't drop here
+ SetCursor(wxCURSOR_NO_ENTRY);
+ }
+}
- wxGenericTreeItem *i=item.m_pItem;
+void wxTreeCtrl::DrawBorder(const wxTreeItemId &item)
+{
+ wxCHECK_RET( item.IsOk(), _T("invalid item in wxTreeCtrl::DrawLine") );
+
+ wxGenericTreeItem *i = item.m_pItem;
wxClientDC dc(this);
PrepareDC( dc );
dc.SetLogicalFunction(wxINVERT);
+ dc.SetBrush(*wxTRANSPARENT_BRUSH);
- int w,h,x;
- ViewStart(&x,&h); // we only need x
- GetClientSize(&w,&h); // we only need w
+ int w = i->GetWidth() + 2;
+ int h = GetLineHeight(i) + 2;
- h=GetLineHeight(i)+1;
- // 2 white column at border
- dc.DrawRectangle( PIXELS_PER_UNIT*x+2, i->GetY()-1, w-6, h);
+ dc.DrawRectangle( i->GetX() - 1, i->GetY() - 1, w, h);
}
-void wxTreeCtrl::DrawLine(wxTreeItemId &item, bool below)
+void wxTreeCtrl::DrawLine(const wxTreeItemId &item, bool below)
{
- if (!item) return;
+ wxCHECK_RET( item.IsOk(), _T("invalid item in wxTreeCtrl::DrawLine") );
- wxGenericTreeItem *i=item.m_pItem;
+ wxGenericTreeItem *i = item.m_pItem;
wxClientDC dc(this);
PrepareDC( dc );
dc.SetLogicalFunction(wxINVERT);
- int w,h,y;
- GetSize(&w,&h);
-
- if (below) y=i->GetY()+GetLineHeight(i)-1;
- else y=i->GetY();
+ int x = i->GetX(),
+ y = i->GetY();
+ if ( below )
+ {
+ y += GetLineHeight(i) - 1;
+ }
- dc.DrawLine( 0, y, w, y);
+ dc.DrawLine( x, y, x + i->GetWidth(), y);
}
// -----------------------------------------------------------------------------
void wxTreeCtrl::OnChar( wxKeyEvent &event )
{
wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
- te.m_code = event.KeyCode();
+ te.m_code = (int)event.KeyCode();
te.SetEventObject( this );
GetEventHandler()->ProcessEvent( te );
return;
}
- bool is_multiple=(GetWindowStyleFlag() & wxTR_MULTIPLE);
- bool extended_select=(event.ShiftDown() && is_multiple);
- bool unselect_others=!(extended_select || (event.ControlDown() && is_multiple));
-
+ // 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);
+
+ // + : Expand
+ // - : Collaspe
+ // * : Toggle Expand/Collapse
+ // ' ' | return : activate
+ // up : go up (not last children!)
+ // down : go down
+ // left : go to parent
+ // right : open if parent and go next
+ // home : go to root
+ // end : go to last item without opening parents
switch (event.KeyCode())
{
case '+':
wxClientDC dc(this);
PrepareDC(dc);
- long x = dc.DeviceToLogicalX( (long)point.x );
- long y = dc.DeviceToLogicalY( (long)point.y );
+ wxCoord x = dc.DeviceToLogicalX( point.x );
+ wxCoord y = dc.DeviceToLogicalY( point.y );
int w, h;
GetSize(&w, &h);
int image = m_currentEdit->GetCurrentImage();
if ( image != NO_IMAGE )
{
- m_imageListNormal->GetSize( image, image_w, image_h );
- image_w += 4;
+ 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
void wxTreeCtrl::OnMouse( wxMouseEvent &event )
{
- if ( !(event.LeftUp() || event.RightDown() || event.LeftDClick() || event.Dragging()) ) return;
-
if ( !m_anchor ) return;
+ // we process left mouse up event (enables in-place edit), right down
+ // (pass to the user code), left dbl click (activate item) and
+ // dragging/moving events for items drag-and-drop
+ if ( !(event.LeftUp() ||
+ event.RightDown() ||
+ event.LeftDClick() ||
+ event.Dragging() ||
+ ((event.Moving() || event.RightUp()) && m_isDragging)) )
+ {
+ event.Skip();
+
+ return;
+ }
+
wxClientDC dc(this);
PrepareDC(dc);
- long x = dc.DeviceToLogicalX( (long)event.GetX() );
- long y = dc.DeviceToLogicalY( (long)event.GetY() );
+ wxCoord x = dc.DeviceToLogicalX( event.GetX() );
+ wxCoord y = dc.DeviceToLogicalY( event.GetY() );
- int flags=0;
+ int flags = 0;
wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), this, flags);
+
bool onButton = flags & wxTREE_HITTEST_ONITEMBUTTON;
- if (event.Dragging())
+ if ( event.Dragging() && !m_isDragging )
{
if (m_dragCount == 0)
m_dragStart = wxPoint(x,y);
m_dragCount++;
- if (m_dragCount != 3) return;
+ if (m_dragCount != 3)
+ {
+ // wait until user drags a bit further...
+ return;
+ }
- int command = wxEVT_COMMAND_TREE_BEGIN_DRAG;
- if (event.RightIsDown()) command = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
+ wxEventType command = event.RightIsDown()
+ ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
+ : wxEVT_COMMAND_TREE_BEGIN_DRAG;
wxTreeEvent nevent( command, GetId() );
nevent.m_item = m_current;
nevent.SetEventObject(this);
- GetEventHandler()->ProcessEvent(nevent);
- return;
+
+ // by default the dragging is not supported, the user code must
+ // explicitly allow the event for it to take place
+ nevent.Veto();
+
+ if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() )
+ {
+ // we're going to drag this item
+ m_isDragging = TRUE;
+
+ // remember the old cursor because we will change it while
+ // dragging
+ m_oldCursor = m_cursor;
+
+ // in a single selection control, hide the selection temporarily
+ if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
+ {
+ m_oldSelection = GetSelection().m_pItem;
+
+ if ( m_oldSelection )
+ {
+ m_oldSelection->SetHilight(FALSE);
+ RefreshLine(m_oldSelection);
+ }
+ }
+
+ CaptureMouse();
+ }
}
- else
+ else if ( event.Moving() )
{
- m_dragCount = 0;
- }
+ if ( item != m_dropTarget )
+ {
+ // unhighlight the previous drop target
+ DrawDropEffect(m_dropTarget);
- if (item == NULL) return; /* we hit the blank area */
+ m_dropTarget = item;
- if (event.RightDown()) {
- wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,GetId());
- nevent.m_item=item;
- nevent.m_code=0;
- nevent.SetEventObject(this);
- GetEventHandler()->ProcessEvent(nevent);
- return;
- }
+ // highlight the current drop target if any
+ DrawDropEffect(m_dropTarget);
- if (event.LeftUp() && (item == m_current) &&
- (flags & wxTREE_HITTEST_ONITEMLABEL) &&
- HasFlag(wxTR_EDIT_LABELS) )
- {
- m_renameTimer->Start( 100, TRUE );
- return;
+ wxYield();
+ }
}
+ else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
+ {
+ // erase the highlighting
+ DrawDropEffect(m_dropTarget);
- bool is_multiple=(GetWindowStyleFlag() & wxTR_MULTIPLE);
- bool extended_select=(event.ShiftDown() && is_multiple);
- bool unselect_others=!(extended_select || (event.ControlDown() && is_multiple));
+ // generate the drag end event
+ wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId());
- if (onButton)
- {
- Toggle( item );
- if (is_multiple)
- return;
- }
+ event.m_item = item;
+ event.m_pointDrag = wxPoint(x, y);
+ event.SetEventObject(this);
+
+ (void)GetEventHandler()->ProcessEvent(event);
+
+ m_isDragging = FALSE;
+ m_dropTarget = (wxGenericTreeItem *)NULL;
+
+ if ( m_oldSelection )
+ {
+ m_oldSelection->SetHilight(TRUE);
+ RefreshLine(m_oldSelection);
+ m_oldSelection = (wxGenericTreeItem *)NULL;
+ }
+
+ ReleaseMouse();
- SelectItem(item, unselect_others, extended_select);
+ SetCursor(m_oldCursor);
- if (event.LeftDClick())
+ wxYield();
+ }
+ else
{
- wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
- event.m_item = item;
- event.m_code = 0;
- event.SetEventObject( this );
- GetEventHandler()->ProcessEvent( event );
+ // here we process only the messages which happen on tree items
+
+ m_dragCount = 0;
+
+ if (item == NULL) return; /* we hit the blank area */
+
+ if ( event.RightDown() )
+ {
+ wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId());
+ nevent.m_item = item;
+ nevent.m_code = 0;
+ nevent.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(nevent);
+ }
+ else if ( event.LeftUp() && (item == m_current) &&
+ (flags & wxTREE_HITTEST_ONITEMLABEL) &&
+ HasFlag(wxTR_EDIT_LABELS) )
+ {
+ m_renameTimer->Start( 100, TRUE );
+ }
+ else
+ {
+ // 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);
+
+ if ( onButton )
+ {
+ Toggle( item );
+ if ( is_multiple )
+ return;
+ }
+
+ SelectItem(item, unselect_others, extended_select);
+
+ if ( event.LeftDClick() )
+ {
+ wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
+ nevent.m_item = item;
+ nevent.m_code = 0;
+ nevent.SetEventObject( this );
+ GetEventHandler()->ProcessEvent( nevent );
+ }
+ }
}
}
void wxTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc )
{
- long text_w = 0;
- long text_h = 0;
+ wxCoord text_w = 0;
+ wxCoord text_h = 0;
if (item->IsBold())
dc.SetFont(m_boldFont);
int image = item->GetCurrentImage();
if ( image != NO_IMAGE )
{
- m_imageListNormal->GetSize( image, image_w, image_h );
- image_w += 4;
+ if ( m_imageListNormal )
+ {
+ m_imageListNormal->GetSize( image, image_w, image_h );
+ image_w += 4;
+ }
}
int total_h = (image_h > text_h) ? image_h : text_h;
- if (total_h < 40)
+ if (total_h < 40)
total_h += 2; // at least 2 pixels
- else
+ else
total_h += total_h/10; // otherwise 10% extra spacing
item->SetHeight(total_h);
- if (total_h>m_lineHeight) m_lineHeight=total_h;
+ if (total_h>m_lineHeight)
+ m_lineHeight=total_h;
item->SetWidth(image_w+text_w+2);
}
void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
{
+ if (m_dirty) return;
+
wxClientDC dc(this);
PrepareDC(dc);
void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
{
+ if (m_dirty) return;
+
wxClientDC dc(this);
PrepareDC( dc );