X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6cedba093a6d87371ca67534b1b9165fe3d45fbc..77611ad4da9df336a0805508276a81b8f4437107:/src/generic/treectrl.cpp?ds=sidebyside diff --git a/src/generic/treectrl.cpp b/src/generic/treectrl.cpp index a438ee6aaa..9a43e04af0 100644 --- a/src/generic/treectrl.cpp +++ b/src/generic/treectrl.cpp @@ -53,10 +53,54 @@ WX_DEFINE_OBJARRAY(wxArrayTreeItemIds); 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 { @@ -76,7 +120,7 @@ public: 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 @@ -191,13 +235,13 @@ private: 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)); } // ----------------------------------------------------------------------------- @@ -242,7 +286,7 @@ wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent, m_accept = accept; m_owner = owner; (*m_accept) = FALSE; - (*m_res) = ""; + (*m_res) = wxEmptyString; m_startValue = value; } @@ -283,7 +327,6 @@ void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) m_owner->OnRenameAccept(); } -#define PIXELS_PER_UNIT 10 // ----------------------------------------------------------------------------- // wxTreeEvent // ----------------------------------------------------------------------------- @@ -430,7 +473,7 @@ wxGenericTreeItem *wxGenericTreeItem::HitTest( const wxPoint& point, { if ((point.y > m_y) && (point.y < m_y + theTree->GetLineHeight(this))) { - if (point.yGetLineHeight(this)/2) + if (point.y < m_y+theTree->GetLineHeight(this)/2 ) flags |= wxTREE_HITTEST_ONITEMUPPERPART; else flags |= wxTREE_HITTEST_ONITEMLOWERPART; @@ -563,6 +606,9 @@ void wxTreeCtrl::Init() m_imageListState = (wxImageList *) NULL; m_dragCount = 0; + m_isDragging = FALSE; + m_dropTarget = + m_oldSelection = (wxGenericTreeItem *)NULL; m_renameTimer = new wxTreeRenameTimer( this ); @@ -1450,10 +1496,12 @@ void wxTreeCtrl::SetImageList(wxImageList *imageList) { 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, @@ -1469,7 +1517,6 @@ void wxTreeCtrl::SetImageList(wxImageList *imageList) m_lineHeight += 2; // at least 2 pixels else m_lineHeight += m_lineHeight/10; // otherwise 10% extra spacing -#endif } void wxTreeCtrl::SetStateImageList(wxImageList *imageList) @@ -1525,8 +1572,15 @@ void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) 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); @@ -1663,42 +1717,66 @@ void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int & } } -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); } // ----------------------------------------------------------------------------- @@ -1756,8 +1834,18 @@ void wxTreeCtrl::OnChar( wxKeyEvent &event ) 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 '+': @@ -1982,8 +2070,15 @@ void wxTreeCtrl::Edit( const wxTreeItemId& item ) 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 @@ -2018,84 +2113,175 @@ void wxTreeCtrl::OnRenameAccept() 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 ); + } + } } } @@ -2116,8 +2302,8 @@ void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) ) void wxTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) { - int text_w = 0; - int text_h = 0; + wxCoord text_w = 0; + wxCoord text_h = 0; if (item->IsBold()) dc.SetFont(m_boldFont); @@ -2133,8 +2319,11 @@ void wxTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) 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;