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
static void EventFlagsToSelType(long style,
bool shiftDown,
bool ctrlDown,
- bool *is_multiple,
- bool *extended_select,
- bool *unselect_others)
+ 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));
+ is_multiple = (style & wxTR_MULTIPLE) != 0;
+ extended_select = shiftDown && is_multiple;
+ unselect_others = !(extended_select || (ctrlDown && is_multiple));
}
// -----------------------------------------------------------------------------
m_accept = accept;
m_owner = owner;
(*m_accept) = FALSE;
- (*m_res) = "";
+ (*m_res) = wxEmptyString;
m_startValue = value;
}
m_owner->OnRenameAccept();
}
-#define PIXELS_PER_UNIT 10
// -----------------------------------------------------------------------------
// wxTreeEvent
// -----------------------------------------------------------------------------
{
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;
m_imageListState = (wxImageList *) NULL;
m_dragCount = 0;
+ m_isDragging = FALSE;
+ m_dropTarget =
+ m_oldSelection = (wxGenericTreeItem *)NULL;
m_renameTimer = new wxTreeRenameTimer( this );
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_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,
m_lineHeight += 2; // at least 2 pixels
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);
- wxColour colBg;
- if ( attr && attr->HasBackgroundColour() )
- colBg = attr->GetBackgroundColour();
+ if (item->IsSelected())
+ dc.SetBrush(*m_hilightBrush);
else
- colBg = m_backgroundColour;
- dc.SetBrush(wxBrush(colBg, wxSOLID));
+ {
+ 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 );
}
dc.SetBackgroundMode(wxTRANSPARENT);
- dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY()
+ 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 )
+ {
+ 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);
+ }
+}
+
+void wxTreeCtrl::DrawBorder(const wxTreeItemId &item)
{
- 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);
+ 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);
}
// -----------------------------------------------------------------------------
EventFlagsToSelType(GetWindowStyleFlag(),
event.ShiftDown(),
event.ControlDown(),
- &is_multiple, &extended_select, &unselect_others);
-
+ 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 '+':
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);
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);
- // 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);
+ // 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;
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);
}