X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/674ac8b919eecbc201b5f23b470a567cd0565e10..5f9fd7ea86d211e3681a71c9f70303aa72b5de3c:/src/generic/treectrl.cpp diff --git a/src/generic/treectrl.cpp b/src/generic/treectrl.cpp index af2d4cf2e3..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 @@ -182,6 +226,23 @@ private: // 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) @@ -225,7 +286,7 @@ wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent, m_accept = accept; m_owner = owner; (*m_accept) = FALSE; - (*m_res) = ""; + (*m_res) = wxEmptyString; m_startValue = value; } @@ -235,14 +296,23 @@ void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) { (*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(); @@ -250,15 +320,13 @@ void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) 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 // ----------------------------------------------------------------------------- @@ -302,7 +370,7 @@ wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent, m_parent = parent; m_attr = (wxTreeItemAttr *)NULL; - + // We don't know the height here yet. m_width = 0; m_height = 0; @@ -405,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; @@ -458,7 +526,7 @@ wxGenericTreeItem *wxGenericTreeItem::HitTest( const wxPoint& point, } flags|=wxTREE_HITTEST_NOWHERE; - + return (wxGenericTreeItem*) NULL; } @@ -538,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 ); @@ -563,7 +634,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id, 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 ); @@ -781,7 +852,7 @@ wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) co wxArrayGenericTreeItems& children = item.m_pItem->GetChildren(); if ( (size_t)cookie < children.Count() ) { - return children.Item(cookie++); + return children.Item((size_t)cookie++); } else { @@ -913,7 +984,7 @@ wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, if (!HasFlag(wxTR_MULTIPLE)) { m_current = m_key_current = m_anchor; - m_current->SetHilight( TRUE ); + m_current->SetHilight( TRUE ); } Refresh(); @@ -946,7 +1017,7 @@ wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId, 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); } @@ -1171,7 +1242,7 @@ bool wxTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericT crt_item->SetHilight(select); RefreshLine(crt_item); - if (crt_item==last_item) + if (crt_item==last_item) return TRUE; if (crt_item->HasChildren()) @@ -1180,9 +1251,9 @@ bool wxTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericT 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; @@ -1195,7 +1266,7 @@ void wxTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *it // choice first' and 'last' between item1 and item2 if (item1->GetY()GetY()) - { + { first=item1; last=item2; } @@ -1425,26 +1496,27 @@ 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, 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) @@ -1500,12 +1572,31 @@ 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); + 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 ) @@ -1518,11 +1609,8 @@ void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) 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 @@ -1571,15 +1659,12 @@ void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int & } 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; @@ -1591,14 +1676,11 @@ void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int & 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); @@ -1635,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) 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 */); + } - wxGenericTreeItem *i=item.m_pItem; + SetCursor(wxCURSOR_BULLSEYE); + } + else + { + // can't drop here + SetCursor(wxCURSOR_NO_ENTRY); + } +} + +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); } // ----------------------------------------------------------------------------- @@ -1713,7 +1819,7 @@ void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) 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 ); @@ -1723,10 +1829,23 @@ void wxTreeCtrl::OnChar( wxKeyEvent &event ) 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 '+': @@ -1905,8 +2024,8 @@ wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& flags) 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); @@ -1951,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 @@ -1987,81 +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); - 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; + } - SelectItem(item, unselect_others, extended_select); + ReleaseMouse(); - if (event.LeftDClick()) + SetCursor(m_oldCursor); + + 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 ); + } + } } } @@ -2082,8 +2302,8 @@ void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) ) 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); @@ -2099,19 +2319,23 @@ 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; - 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); }