diff options
Diffstat (limited to 'sd/source/ui/animations/CustomAnimationList.cxx')
-rw-r--r-- | sd/source/ui/animations/CustomAnimationList.cxx | 1116 |
1 files changed, 477 insertions, 639 deletions
diff --git a/sd/source/ui/animations/CustomAnimationList.cxx b/sd/source/ui/animations/CustomAnimationList.cxx index 4793d8aafcca..559ac92a6b5c 100644 --- a/sd/source/ui/animations/CustomAnimationList.cxx +++ b/sd/source/ui/animations/CustomAnimationList.cxx @@ -29,20 +29,18 @@ #include <com/sun/star/drawing/XDrawPage.hpp> #include "CustomAnimationList.hxx" #include <CustomAnimationPreset.hxx> -#include <vcl/settings.hxx> -#include <vcl/builderfactory.hxx> #include <vcl/commandevent.hxx> #include <vcl/event.hxx> +#include <vcl/image.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> #include <tools/debug.hxx> +#include <tools/gen.hxx> #include <osl/diagnose.h> #include <sdresid.hxx> -#include <vcl/svlbitm.hxx> -#include <vcl/treelistentry.hxx> -#include <vcl/viewdataentry.hxx> - - #include <strings.hrc> #include <bitmaps.hlst> @@ -215,33 +213,35 @@ static OUString getDescription( const Any& rTarget, bool bWithText ) return aDescription; } -class CustomAnimationListEntryItem : public SvLBoxString +class CustomAnimationListEntryItem { public: CustomAnimationListEntryItem(const OUString& aDescription, - const CustomAnimationEffectPtr& pEffect, CustomAnimationList* pParent); - void InitViewData(SvTreeListBox*,SvTreeListEntry*,SvViewDataItem* = nullptr) override; - virtual std::unique_ptr<SvLBoxItem> Clone(SvLBoxItem const * pSource) const override; + const CustomAnimationEffectPtr& pEffect); + const CustomAnimationEffectPtr& getEffect() const { return mpEffect; } + + Size GetSize(vcl::RenderContext& rRenderContext); + void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected); + void PaintEffect(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected); + void PaintTrigger(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect); - virtual void Paint(const Point&, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext, - const SvViewDataEntry* pView,const SvTreeListEntry& rEntry) override; private: - VclPtr<CustomAnimationList> mpParent; OUString msDescription; OUString msEffectName; CustomAnimationEffectPtr mpEffect; + static const long nIconWidth = 19; static const long nItemMinHeight = 38; }; -CustomAnimationListEntryItem::CustomAnimationListEntryItem( const OUString& aDescription, const CustomAnimationEffectPtr& pEffect, CustomAnimationList* pParent ) -: SvLBoxString( aDescription ) -, mpParent( pParent ) -, msDescription( aDescription ) -, msEffectName( OUString() ) -, mpEffect(pEffect) +CustomAnimationListEntryItem::CustomAnimationListEntryItem(const OUString& aDescription, const CustomAnimationEffectPtr& pEffect) + : msDescription(aDescription) + , msEffectName(OUString()) + , mpEffect(pEffect) { - switch(mpEffect->getPresetClass()) + if (!mpEffect) + return; + switch (mpEffect->getPresetClass()) { case EffectPresetClass::ENTRANCE: msEffectName = SdResId(STR_CUSTOMANIMATION_ENTRANCE); break; @@ -257,30 +257,85 @@ CustomAnimationListEntryItem::CustomAnimationListEntryItem( const OUString& aDes msEffectName = msEffectName.replaceFirst( "%1" , CustomAnimationPresets::getCustomAnimationPresets().getUINameForPresetId(mpEffect->getPresetId())); } -void CustomAnimationListEntryItem::InitViewData( SvTreeListBox* pView, SvTreeListEntry* pEntry, SvViewDataItem* pViewData ) +IMPL_STATIC_LINK(CustomAnimationList, CustomRenderHdl, weld::TreeView::render_args, aPayload, void) { - if( !pViewData ) - pViewData = pView->GetViewDataItem( pEntry, this ); - - long width = pView->GetTextWidth( msDescription ) + nIconWidth; - if( width < (pView->GetTextWidth( msEffectName ) + 2*nIconWidth)) - width = pView->GetTextWidth( msEffectName ) + 2*nIconWidth; - - Size aSize( width, pView->GetTextHeight() ); - if( aSize.Height() < nItemMinHeight ) - aSize.setHeight( nItemMinHeight ); - pViewData->mnWidth = aSize.Width(); - pViewData->mnHeight = aSize.Height(); + vcl::RenderContext& rRenderContext = std::get<0>(aPayload); + const ::tools::Rectangle& rRect = std::get<1>(aPayload); + bool bSelected = std::get<2>(aPayload); + const OUString& rId = std::get<3>(aPayload); + + CustomAnimationListEntryItem* pItem = reinterpret_cast<CustomAnimationListEntryItem*>(rId.toInt64()); + + pItem->Paint(rRenderContext, rRect, bSelected); } -void CustomAnimationListEntryItem::Paint(const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext, - const SvViewDataEntry* /*pView*/, const SvTreeListEntry& rEntry) +IMPL_STATIC_LINK(CustomAnimationList, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size) { + vcl::RenderContext& rRenderContext = aPayload.first; + const OUString& rId = aPayload.second; + + CustomAnimationListEntryItem* pItem = reinterpret_cast<CustomAnimationListEntryItem*>(rId.toInt64()); + return pItem->GetSize(rRenderContext); +} + +Size CustomAnimationListEntryItem::GetSize(vcl::RenderContext& rRenderContext) +{ + auto width = rRenderContext.GetTextWidth( msDescription ) + nIconWidth; + if (width < (rRenderContext.GetTextWidth( msEffectName ) + 2*nIconWidth)) + width = rRenderContext.GetTextWidth( msEffectName ) + 2*nIconWidth; + + Size aSize(width, rRenderContext.GetTextHeight()); + if (aSize.Height() < nItemMinHeight) + aSize.setHeight(nItemMinHeight); + return aSize; +} + +void CustomAnimationListEntryItem::PaintTrigger(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) +{ + Size aSize(rRect.GetSize()); + + ::tools::Rectangle aOutRect(rRect); + + // fill the background + Color aColor(rRenderContext.GetSettings().GetStyleSettings().GetDialogColor()); + + rRenderContext.Push(); + rRenderContext.SetFillColor(aColor); + rRenderContext.SetLineColor(); + rRenderContext.DrawRect(aOutRect); + + // Erase the four corner pixels to make the rectangle appear rounded. + rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); + rRenderContext.DrawPixel(aOutRect.TopLeft()); + rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Top())); + rRenderContext.DrawPixel(Point(aOutRect.Left(), aOutRect.Bottom())); + rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Bottom())); + + // draw the category title + + int nVertBorder = ((aSize.Height() - rRenderContext.GetTextHeight()) >> 1); + int nHorzBorder = rRenderContext.LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont)).Width(); + + aOutRect.AdjustLeft(nHorzBorder ); + aOutRect.AdjustRight( -nHorzBorder ); + aOutRect.AdjustTop( nVertBorder ); + aOutRect.AdjustBottom( -nVertBorder ); - const SvViewDataItem* pViewData = mpParent->GetViewDataItem(&rEntry, this); + rRenderContext.DrawText(aOutRect, rRenderContext.GetEllipsisString(msDescription, aOutRect.GetWidth())); + rRenderContext.Pop(); +} - Point aPos(rPos); - int nItemHeight = pViewData->mnHeight; +void CustomAnimationListEntryItem::PaintEffect(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected) +{ + rRenderContext.Push(PushFlags::TEXTCOLOR); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + if (bSelected) + rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); + else + rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor()); + + Point aPos(rRect.TopLeft()); + int nItemHeight = rRect.GetHeight(); sal_Int16 nNodeType = mpEffect->getNodeType(); if (nNodeType == EffectNodeType::ON_CLICK ) @@ -296,12 +351,12 @@ void CustomAnimationListEntryItem::Paint(const Point& rPos, SvTreeListBox& rDev, //FIXME With previous image not defined in CustomAnimation.src } - aPos.AdjustX(nIconWidth ); - + aPos.AdjustX(nIconWidth); - rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msDescription, rDev.GetOutputSizePixel().Width() - aPos.X())); + //TODO, full width of widget ? + rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msDescription, rRect.GetWidth())); - aPos.AdjustY(nIconWidth ); + aPos.AdjustY(nIconWidth); OUString sImage; switch (mpEffect->getPresetClass()) @@ -341,369 +396,129 @@ void CustomAnimationListEntryItem::Paint(const Point& rPos, SvTreeListBox& rDev, } aPos.AdjustX(nIconWidth ); - aPos.AdjustY((nItemHeight/2 - rDev.GetTextHeight()) >> 1 ); - - rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msEffectName, rDev.GetOutputSizePixel().Width() - aPos.X())); -} - -std::unique_ptr<SvLBoxItem> CustomAnimationListEntryItem::Clone(SvLBoxItem const *) const -{ - return nullptr; -} - -namespace { - -class CustomAnimationListEntry : public SvTreeListEntry -{ -public: - CustomAnimationListEntry(); - explicit CustomAnimationListEntry(const CustomAnimationEffectPtr& pEffect); - - const CustomAnimationEffectPtr& getEffect() const { return mpEffect; } - -private: - CustomAnimationEffectPtr mpEffect; -}; + aPos.AdjustY((nItemHeight/2 - rRenderContext.GetTextHeight()) >> 1 ); + rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msEffectName, rRect.GetWidth())); + rRenderContext.Pop(); } -CustomAnimationListEntry::CustomAnimationListEntry() -{ -} - -CustomAnimationListEntry::CustomAnimationListEntry(const CustomAnimationEffectPtr& pEffect) -: mpEffect( pEffect ) -{ -} - -namespace { - -class CustomAnimationTriggerEntryItem : public SvLBoxString +void CustomAnimationListEntryItem::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected) { -public: - explicit CustomAnimationTriggerEntryItem( const OUString& aDescription ); - - void InitViewData( SvTreeListBox*,SvTreeListEntry*,SvViewDataItem* = nullptr ) override; - virtual std::unique_ptr<SvLBoxItem> Clone(SvLBoxItem const * pSource) const override; - virtual void Paint(const Point& rPos, SvTreeListBox& rOutDev, vcl::RenderContext& rRenderContext, - const SvViewDataEntry* pView, const SvTreeListEntry& rEntry) override; - -private: - OUString msDescription; - static const long nIconWidth = 19; -}; - + if (mpEffect) + PaintEffect(rRenderContext, rRect, bSelected); + else + PaintTrigger(rRenderContext, rRect); } -CustomAnimationTriggerEntryItem::CustomAnimationTriggerEntryItem( const OUString& aDescription ) -: SvLBoxString( aDescription ), msDescription( aDescription ) +CustomAnimationList::CustomAnimationList(std::unique_ptr<weld::TreeView> xTreeView, + std::unique_ptr<weld::Label> xLabel, + std::unique_ptr<weld::Widget> xScrolledWindow) + : mxTreeView(std::move(xTreeView)) + , maDropTargetHelper(*this) + , mxEmptyLabel(std::move(xLabel)) + , mxEmptyLabelParent(std::move(xScrolledWindow)) + , mbIgnorePaint(false) + , mpController(nullptr) + , mnLastGroupId(0) + , mnPostExpandEvent(nullptr) + , mnPostCollapseEvent(nullptr) { + mxEmptyLabel->set_stack_background(); + + mxTreeView->set_selection_mode(SelectionMode::Multiple); + mxTreeView->set_column_custom_renderer(0); + mxTreeView->connect_changed(LINK(this, CustomAnimationList, SelectHdl)); + mxTreeView->connect_key_press(LINK(this, CustomAnimationList, KeyInputHdl)); + mxTreeView->connect_popup_menu(LINK(this, CustomAnimationList, CommandHdl)); + mxTreeView->connect_row_activated(LINK(this, CustomAnimationList, DoubleClickHdl)); + mxTreeView->connect_expanding(LINK(this, CustomAnimationList, ExpandHdl)); + mxTreeView->connect_collapsing(LINK(this, CustomAnimationList, CollapseHdl)); + mxTreeView->connect_drag_begin(LINK(this, CustomAnimationList, DragBeginHdl)); + mxTreeView->connect_custom_get_size(LINK(this, CustomAnimationList, CustomGetSizeHdl)); + mxTreeView->connect_custom_render(LINK(this, CustomAnimationList, CustomRenderHdl)); } -void CustomAnimationTriggerEntryItem::InitViewData( SvTreeListBox* pView, SvTreeListEntry* pEntry, SvViewDataItem* pViewData ) +CustomAnimationListDropTarget::CustomAnimationListDropTarget(CustomAnimationList& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) { - if( !pViewData ) - pViewData = pView->GetViewDataItem( pEntry, this ); - - Size aSize(pView->GetTextWidth( msDescription ) + 2 * nIconWidth, pView->GetTextHeight() ); - if( aSize.Height() < nIconWidth ) - aSize.setHeight( nIconWidth ); - pViewData->mnWidth = aSize.Width(); - pViewData->mnHeight = aSize.Height(); } -void CustomAnimationTriggerEntryItem::Paint(const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext, - const SvViewDataEntry* /*pView*/, const SvTreeListEntry& /*rEntry*/) +sal_Int8 CustomAnimationListDropTarget::AcceptDrop(const AcceptDropEvent& rEvt) { - Size aSize(rDev.GetOutputSizePixel().Width(), rDev.GetEntryHeight()); - - Point aPos(0, rPos.Y()); - - ::tools::Rectangle aOutRect(aPos, aSize); - - // fill the background - Color aColor(rRenderContext.GetSettings().GetStyleSettings().GetDialogColor()); - - rRenderContext.Push(); - rRenderContext.SetFillColor(aColor); - rRenderContext.SetLineColor(); - rRenderContext.DrawRect(aOutRect); - - // Erase the four corner pixels to make the rectangle appear rounded. - rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); - rRenderContext.DrawPixel(aOutRect.TopLeft()); - rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Top())); - rRenderContext.DrawPixel(Point(aOutRect.Left(), aOutRect.Bottom())); - rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Bottom())); - - // draw the category title + sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt); - int nVertBorder = ((aSize.Height() - rDev.GetTextHeight()) >> 1); - int nHorzBorder = rRenderContext.LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont)).Width(); - - aOutRect.AdjustLeft(nHorzBorder ); - aOutRect.AdjustRight( -nHorzBorder ); - aOutRect.AdjustTop( nVertBorder ); - aOutRect.AdjustBottom( -nVertBorder ); - - rRenderContext.DrawText(aOutRect, rRenderContext.GetEllipsisString(msDescription, aOutRect.GetWidth())); - rRenderContext.Pop(); -} + if (nAccept != DND_ACTION_NONE) + { + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr); + } -std::unique_ptr<SvLBoxItem> CustomAnimationTriggerEntryItem::Clone(SvLBoxItem const *) const -{ - return nullptr; + return nAccept; } -CustomAnimationList::CustomAnimationList( vcl::Window* pParent ) - : SvTreeListBox( pParent, WB_TABSTOP | WB_BORDER | WB_HASLINES | WB_HASBUTTONS | WB_HASBUTTONSATROOT ) - , mbIgnorePaint(false) - , mpController(nullptr) - , mnLastGroupId(0) - , mpLastParentEntry(nullptr) - , mpDndEffectDragging(nullptr) - , mpDndEffectInsertBefore(nullptr) +sal_Int8 CustomAnimationListDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt) { - EnableContextMenuHandling(); - SetSelectionMode( SelectionMode::Multiple ); - SetOptimalImageIndent(); - SetNodeDefaultImages(); - - SetDragDropMode(DragDropMode::CTRL_MOVE); + return m_rTreeView.ExecuteDrop(rEvt); } // D'n'D #1: Record selected effects for drag'n'drop. -void CustomAnimationList::StartDrag( sal_Int8 nAction, const Point& rPosPixel ) +IMPL_LINK(CustomAnimationList, DragBeginHdl, bool&, rUnsetDragIcon, bool) { + rUnsetDragIcon = false; + // Record which effects are selected: // Since NextSelected(..) iterates through the selected items in the order they // were selected, create a sorted list for simpler drag'n'drop algorithms. mDndEffectsSelected.clear(); - for( SvTreeListEntry* pEntry = First(); pEntry; pEntry = Next(pEntry) ) - { - if( IsSelected(pEntry) ) - { - mDndEffectsSelected.push_back( pEntry ); - } - } - - // Allow normal processing; this calls our NotifyStartDrag(). - SvTreeListBox::StartDrag( nAction, rPosPixel ); -} - -// D'n'D #2: Prepare selected element for moving. -DragDropMode CustomAnimationList::NotifyStartDrag( TransferDataContainer& /*rData*/, SvTreeListEntry* pEntry ) -{ - // Restore selection for multiple selected effects. - // Do it here to remove a flicker on the UI with effects being unselected and reselected. - for( auto &pEffect : mDndEffectsSelected ) - SelectListEntry( pEffect, true); + mxTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + mDndEffectsSelected.emplace_back(mxTreeView->make_iterator(&rEntry)); + return false; + }); // Note: pEntry is the effect with focus (if multiple effects are selected) - mpDndEffectDragging = pEntry; - mpDndEffectInsertBefore = pEntry; + mxDndEffectDragging = mxTreeView->make_iterator(); + mxTreeView->get_cursor(mxDndEffectDragging.get()); + mxDndEffectInsertBefore = mxTreeView->make_iterator(mxDndEffectDragging.get()); - return DragDropMode::CTRL_MOVE; + // Allow normal processing. + return false; } // D'n'D #3: Called each time mouse moves during drag sal_Int8 CustomAnimationList::AcceptDrop( const AcceptDropEvent& rEvt ) { - /* - Don't call SvTreeListBox::AcceptDrop because it puts an unnecessary - highlight via ImplShowTargetEmphasis() - */ - sal_Int8 ret = DND_ACTION_NONE; - const bool bIsMove = ( DND_ACTION_MOVE == rEvt.mnAction ); - if( mpDndEffectDragging && !rEvt.mbLeaving && bIsMove ) - { - SvTreeListEntry* pEntry = GetDropTarget( rEvt.maPosPixel ); - - const bool bOverASelectedEffect = - std::find( mDndEffectsSelected.begin(), mDndEffectsSelected.end(), pEntry ) != mDndEffectsSelected.end(); - if( pEntry && !bOverASelectedEffect ) - { - ReparentChildrenDuringDrag(); - - ReorderEffectsInUiDuringDragOver( pEntry ); - } - - // Return DND_ACTION_MOVE on internal drag'n'drops so that ExecuteDrop() is called. - // Return MOVE even if we are over other dragged effect because dragged effect moves. + const bool bIsMove = DND_ACTION_MOVE == rEvt.mnAction; + if (mxDndEffectDragging && !rEvt.mbLeaving && bIsMove) ret = DND_ACTION_MOVE; - } - return ret; } -// D'n'D: For each dragged effect, re-parent (only in the UI) non-selected -// visible children so they are not dragged with the parent. -void CustomAnimationList::ReparentChildrenDuringDrag() -{ - /* - Re-parent (only in the UI!): - a) the dragged effect's first non-selected child to the root, and - b) the remaining non-selected children to that re-parented 1st child. - */ - for( auto &pEffect : mDndEffectsSelected ) - { - const bool bExpandedWithChildren = GetVisibleChildCount( pEffect ) > 0; - if( bExpandedWithChildren ) - { - SvTreeListEntry* pEntryParent = GetParent( pEffect ); - - SvTreeListEntry* pFirstNonSelectedChild = nullptr; - sal_uLong nInsertNextChildPos = 0; - - // Process all children of this effect - SvTreeListEntry* pChild = FirstChild( pEffect ); - while( pChild && ( GetParent( pChild ) == pEffect ) ) - { - // Start by finding next child because if pChild moves, we cannot then - // ask it what the next child is because it's no longer with its siblings. - SvTreeListEntry* pNextChild = Next( pChild ); - - // Skip selected effects: they stay with their previous parent to be moved. - // During drag, the IsSelected() set changes, so use mDndEffectsSelected instead - const bool bIsSelected = std::find( mDndEffectsSelected.begin(), mDndEffectsSelected.end(), pChild ) != mDndEffectsSelected.end(); - if( !bIsSelected ) - { - // Re-parent 1st non-selected child to root, below all the other children. - if( !pFirstNonSelectedChild ) - { - pFirstNonSelectedChild = pChild; - sal_uLong nInsertAfterPos = SvTreeList::GetRelPos( pEffect ) + 1; - pModel->Move( pFirstNonSelectedChild, pEntryParent, nInsertAfterPos ); - } - else - { - // Re-parent remaining non-selected children to 1st child - ++nInsertNextChildPos; - pModel->Move( pChild, pFirstNonSelectedChild, nInsertNextChildPos ); - } - } - - pChild = pNextChild; - } - - // Expand all children (they were previously visible) - if( pFirstNonSelectedChild ) - Expand( pFirstNonSelectedChild ); - - } - } -} - -// D'n'D: Update UI to show where dragged event will appear if dropped now. -void CustomAnimationList::ReorderEffectsInUiDuringDragOver( SvTreeListEntry* pOverEntry ) -{ - /* - Update the order of effects in *just the UI* while the user is dragging. - The model (MainSequence) will only be changed after the user drops - the effect so that there is minimal work to do if the drag is canceled. - Plus only one undo record should be created per drag, and changing - the model recreates all effects (on a background timer) which invalidates - all effect pointers. - */ - - // Compute new location in *UI* - SvTreeListEntry* pNewParent = nullptr; - sal_uLong nInsertAfterPos = 0; - - Point aPosOverEffect( GetEntryPosition(pOverEntry) ); - Point aPosDraggedEffect( GetEntryPosition(mpDndEffectDragging) ); - const bool bDraggingUp = (aPosDraggedEffect.Y() - aPosOverEffect.Y()) > 0; - - if( bDraggingUp ) - { - // Drag up --> place above the element we are over - pNewParent = GetParent( pOverEntry ); - nInsertAfterPos = SvTreeList::GetRelPos( pOverEntry ); - mpDndEffectInsertBefore = pOverEntry; - } - else - { - // Drag down --> place below the element we are over - SvTreeListEntry* pNextVisBelowTarget = NextVisible( pOverEntry ); - if( pNextVisBelowTarget ) - { - // Match parent of NEXT visible effect (works for sub-items too) - pNewParent = GetParent( pNextVisBelowTarget ); - nInsertAfterPos = SvTreeList::GetRelPos( pNextVisBelowTarget ); - mpDndEffectInsertBefore = pNextVisBelowTarget; - } - else - { - // Over the last element: no next to work with - pNewParent = GetParent( pOverEntry ); - nInsertAfterPos = SvTreeList::GetRelPos( pOverEntry ) + 1; - mpDndEffectInsertBefore = nullptr; - } - } - - // Move each selected effect in *just* the UI to show where it would be if dropped. - // This leaves the exist parent relationships in the non-dragged elements so that - // the list does not seem to change structure during drag. Parent relationships will - // be correctly recreated on drop. - for( auto aItr = mDndEffectsSelected.rbegin(); - aItr != mDndEffectsSelected.rend(); - ++aItr) - { - SvTreeListEntry* pEffect = *aItr; - - // Move only effects whose parents is not selected because - // they will automatically move when their parent is moved. - const bool bParentIsSelected = - std::find(mDndEffectsSelected.begin(), mDndEffectsSelected.end(), GetParent(pEffect)) != mDndEffectsSelected.end(); - - if( !bParentIsSelected ) - { - // If the current effect is being moved down, the insert position must be decremented - // after move if it will have the same parent as it currently does because it moves - // from above the insertion point to below it, hence changing its index. - // Must decide move-up vs move-down for each effect being dragged because we may be - // processing a discontinuous set of selected effects (some below, some above insertion point) - Point aCurPosOverEffect( GetEntryPosition( pOverEntry ) ); - Point aCurPosMovedEffect( GetEntryPosition( pEffect ) ); - const bool bCurDraggingDown = ( aCurPosMovedEffect.Y() - aCurPosOverEffect.Y() ) < 0; - const bool bWillHaveSameParent = ( pNewParent == GetParent(pEffect) ); - - pModel->Move( pEffect, pNewParent, nInsertAfterPos ); - - if( bCurDraggingDown && bWillHaveSameParent ) - --nInsertAfterPos; - } - } - - // Restore selection (calling Select() is slow; SelectListEntry() is faster) - for( auto &pEffect : mDndEffectsSelected ) - SelectListEntry( pEffect, true); -} - // D'n'D #5: Tell model to update effect order. -sal_Int8 CustomAnimationList::ExecuteDrop( const ExecuteDropEvent& /*rEvt*/ ) +sal_Int8 CustomAnimationList::ExecuteDrop(const ExecuteDropEvent& rEvt) { - // NOTE: We cannot just override NotifyMoving() because it's not called - // since we dynamically reorder effects during drag. - - sal_Int8 ret = DND_ACTION_NONE; + if (!mxTreeView->get_dest_row_at_pos(rEvt.maPosPixel, mxDndEffectInsertBefore.get())) + mxDndEffectInsertBefore.reset(); - const bool bMovingEffect = ( mpDndEffectDragging != nullptr ); - const bool bMoveNotSelf = ( mpDndEffectInsertBefore != mpDndEffectDragging ); + const bool bMovingEffect = ( mxDndEffectDragging != nullptr ); + const bool bMoveNotSelf = !mxDndEffectInsertBefore || (mxDndEffectDragging && mxTreeView->iter_compare(*mxDndEffectInsertBefore, *mxDndEffectDragging) != 0); const bool bHaveSequence = ( mpMainSequence.get() != nullptr ); if( bMovingEffect && bMoveNotSelf && bHaveSequence ) { - CustomAnimationListEntry* pTarget = static_cast< CustomAnimationListEntry* >( mpDndEffectInsertBefore ); + CustomAnimationListEntryItem* pTarget = mxDndEffectInsertBefore ? + reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*mxDndEffectInsertBefore).toInt64()) : + nullptr; // Build list of effects std::vector< CustomAnimationEffectPtr > aEffects; for( const auto &pEntry : mDndEffectsSelected ) { - CustomAnimationListEntry* pCustomAnimationEffect = static_cast< CustomAnimationListEntry* >( pEntry ); - aEffects.push_back( pCustomAnimationEffect->getEffect() ); + CustomAnimationListEntryItem* pCustomAnimationEffect = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*pEntry).toInt64()); + aEffects.push_back(pCustomAnimationEffect->getEffect()); } // Callback to observer to have it update the model. @@ -713,90 +528,82 @@ sal_Int8 CustomAnimationList::ExecuteDrop( const ExecuteDropEvent& /*rEvt*/ ) pTarget ? pTarget->getEffect() : nullptr ); // Reset selection - Select( mpDndEffectDragging ); - - ret = DND_ACTION_MOVE; + mxTreeView->select(*mxDndEffectDragging); + Select(); } - // NOTE: Don't call SvTreeListBox::ExecuteDrop(...) because all required + // NOTE: Don't call default handler because all required // move operations have been completed here to update the model. - return ret; -} - -// D'n'D #6: Cleanup (regardless of if we were target of drop or not) -void CustomAnimationList::DragFinished( sal_Int8 /*nDropAction*/ ) -{ - mpDndEffectDragging = nullptr; - mpDndEffectInsertBefore = nullptr; - mDndEffectsSelected.clear(); - - // Rebuild because we may have re-parented the dragged effect's first child. - // Can hit this without running ExecuteDrop(...) when drag canceled. - mpMainSequence->rebuild(); - - // Note: Don't call SvTreeListBox::DragFinished(...) because we don't call - // SvTreeListBox::ExecuteDrop(...) which sets variables that are - // needed in its DragFinished(...) method. + return DND_ACTION_NONE; } -VCL_BUILDER_FACTORY(CustomAnimationList) - CustomAnimationList::~CustomAnimationList() { - disposeOnce(); -} + if (mnPostExpandEvent) + { + Application::RemoveUserEvent(mnPostExpandEvent); + mnPostExpandEvent = nullptr; + } + + if (mnPostCollapseEvent) + { + Application::RemoveUserEvent(mnPostCollapseEvent); + mnPostCollapseEvent = nullptr; + } -void CustomAnimationList::dispose() -{ if( mpMainSequence.get() ) mpMainSequence->removeListener( this ); clear(); - - mxMenu.disposeAndClear(); - mxBuilder.reset(); - - SvTreeListBox::dispose(); } -void CustomAnimationList::KeyInput( const KeyEvent& rKEvt ) +IMPL_LINK(CustomAnimationList, KeyInputHdl, const KeyEvent&, rKEvt, bool) { const int nKeyCode = rKEvt.GetKeyCode().GetCode(); - switch( nKeyCode ) + switch (nKeyCode) { case KEY_DELETE: mpController->onContextMenu("remove"); - return; + return true; case KEY_INSERT: mpController->onContextMenu("create"); - return; + return true; case KEY_SPACE: + { + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_cursor(xEntry.get())) { - const Point aPos; - const CommandEvent aCEvt( aPos, CommandEventId::ContextMenu ); - Command( aCEvt ); - return; + auto aRect = mxTreeView->get_row_area(*xEntry); + const Point aPos(aRect.getWidth() / 2, aRect.getHeight() / 2); + const CommandEvent aCEvt(aPos, CommandEventId::ContextMenu); + CommandHdl(aCEvt); + return true; } - + } } - - ::SvTreeListBox::KeyInput( rKEvt ); + return false; } /** selects or deselects the given effect. Selections of other effects are not changed */ void CustomAnimationList::select( const CustomAnimationEffectPtr& pEffect ) { - CustomAnimationListEntry* pEntry = static_cast< CustomAnimationListEntry* >(First()); - while( pEntry ) + CustomAnimationListEntryItem* pEntry = nullptr; + + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) { - if( pEntry->getEffect() == pEffect ) + do { - Select( pEntry ); - MakeVisible( pEntry ); - break; - } - pEntry = static_cast< CustomAnimationListEntry* >(Next( pEntry )); + CustomAnimationListEntryItem* pTestEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry).toInt64()); + if (pTestEntry->getEffect() == pEffect) + { + mxTreeView->select(*xEntry); + mxTreeView->scroll_to_row(*xEntry); + pEntry = pTestEntry; + break; + } + } while (mxTreeView->iter_next(*xEntry)); } if( !pEntry ) @@ -808,9 +615,13 @@ void CustomAnimationList::select( const CustomAnimationEffectPtr& pEffect ) void CustomAnimationList::clear() { - Clear(); + mxEntries.clear(); + mxTreeView->clear(); - mpLastParentEntry = nullptr; + mxEmptyLabelParent->show(); + mxTreeView->hide(); + + mxLastParentEntry.reset(); mxLastTargetShape = nullptr; } @@ -841,9 +652,6 @@ void stl_append_effect_func::operator()(const CustomAnimationEffectPtr& pEffect) void CustomAnimationList::update() { mbIgnorePaint = true; - SetUpdateMode( false ); - - CustomAnimationListEntry* pEntry = nullptr; std::vector< CustomAnimationEffectPtr > aVisible; std::vector< CustomAnimationEffectPtr > aSelected; @@ -856,62 +664,77 @@ void CustomAnimationList::update() long nFirstSelOld = -1; long nLastSelOld = -1; + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if( mpMainSequence.get() ) { - // save scroll position - pEntry = static_cast<CustomAnimationListEntry*>(GetFirstEntryInView()); - if( pEntry ) - nFirstVis = GetAbsPos( pEntry ); + std::unique_ptr<weld::TreeIter> xLastSelectedEntry; + std::unique_ptr<weld::TreeIter> xLastVisibleEntry; - pEntry = static_cast<CustomAnimationListEntry*>(GetLastEntryInView()); - if( pEntry ) - nLastVis = GetAbsPos( pEntry ); + // save selection, current, and expand (visible) states + mxTreeView->all_foreach([this, &aVisible, &nFirstVis, &xLastVisibleEntry, + &aSelected, &nFirstSelOld, &pFirstSelEffect, &xLastSelectedEntry](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(rEntry).toInt64()); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect.get()) + { + if (weld::IsEntryVisible(*mxTreeView, rEntry)) + { + aVisible.push_back(pEffect); + // save scroll position + if (nFirstVis == -1) + nFirstVis = weld::GetAbsPos(*mxTreeView, rEntry); + if (!xLastVisibleEntry) + xLastVisibleEntry = mxTreeView->make_iterator(&rEntry); + else + mxTreeView->copy_iterator(rEntry, *xLastVisibleEntry); + } - pEntry = static_cast<CustomAnimationListEntry*>(FirstSelected()); - if( pEntry ) - { - pFirstSelEffect = pEntry->getEffect(); - nFirstSelOld = GetAbsPos( pEntry ); - } + if (mxTreeView->is_selected(rEntry)) + { + aSelected.push_back(pEffect); + if (nFirstSelOld == -1) + { + pFirstSelEffect = pEffect; + nFirstSelOld = weld::GetAbsPos(*mxTreeView, rEntry); + } + if (!xLastSelectedEntry) + xLastSelectedEntry = mxTreeView->make_iterator(&rEntry); + else + mxTreeView->copy_iterator(rEntry, *xLastSelectedEntry); + } + } + + return false; + }); - pEntry = static_cast<CustomAnimationListEntry*>(LastSelected()); - if( pEntry ) + if (xLastSelectedEntry) { + CustomAnimationListEntryItem* pEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xLastSelectedEntry).toInt64()); pLastSelEffect = pEntry->getEffect(); - nLastSelOld = GetAbsPos( pEntry ); + nLastSelOld = weld::GetAbsPos(*mxTreeView, *xLastSelectedEntry); } - // save selection, current, and expand (visible) states - pEntry = static_cast<CustomAnimationListEntry*>(First()); + if (xLastVisibleEntry) + nLastVis = weld::GetAbsPos(*mxTreeView, *xLastVisibleEntry); - while( pEntry ) + if (mxTreeView->get_cursor(xEntry.get())) { - CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); - if( pEffect.get() ) - { - if( IsEntryVisible( pEntry ) ) - aVisible.push_back( pEffect ); - - if( IsSelected( pEntry ) ) - aSelected.push_back( pEffect ); - } - - pEntry = static_cast<CustomAnimationListEntry*>(Next( pEntry )); - } - - pEntry = static_cast<CustomAnimationListEntry*>(GetCurEntry()); - if( pEntry ) + CustomAnimationListEntryItem* pEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry).toInt64()); aCurrent = pEntry->getEffect(); + } } // rebuild list + + mxTreeView->freeze(); + clear(); - if( mpMainSequence.get() ) + + if (mpMainSequence.get()) { - long nFirstSelNew = -1; - long nLastSelNew = -1; std::for_each( mpMainSequence->getBegin(), mpMainSequence->getEnd(), stl_append_effect_func( *this ) ); - mpLastParentEntry = nullptr; + mxLastParentEntry.reset(); auto rInteractiveSequenceVector = mpMainSequence->getInteractiveSequenceVector(); @@ -920,54 +743,76 @@ void CustomAnimationList::update() Reference< XShape > xShape( pIS->getTriggerShape() ); if( xShape.is() ) { - SvTreeListEntry* pLBoxEntry = new CustomAnimationListEntry; - pLBoxEntry->AddItem(std::make_unique<SvLBoxContextBmp>(Image(), Image(), false)); OUString aDescription = SdResId(STR_CUSTOMANIMATION_TRIGGER) + ": " + getShapeDescription( xShape, false ); - pLBoxEntry->AddItem(std::make_unique<CustomAnimationTriggerEntryItem>(aDescription)); - Insert( pLBoxEntry ); - SvViewDataEntry* pViewData = GetViewData( pLBoxEntry ); - if( pViewData ) - pViewData->SetSelectable(false); + mxEntries.emplace_back(std::make_unique<CustomAnimationListEntryItem>(aDescription, nullptr)); + + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(mxEntries.back().get()))); + mxTreeView->insert(nullptr, -1, &aDescription, &sId, nullptr, nullptr, nullptr, false, nullptr); std::for_each( pIS->getBegin(), pIS->getEnd(), stl_append_effect_func( *this ) ); - mpLastParentEntry = nullptr; + mxLastParentEntry.reset(); } } + } - // restore selection state, expand state, and current-entry (under cursor) - pEntry = static_cast<CustomAnimationListEntry*>(First()); + mxTreeView->thaw(); - while( pEntry ) + if (mxTreeView->n_children()) + { + mxEmptyLabelParent->hide(); + mxTreeView->show(); + } + + if (mpMainSequence.get()) + { + long nFirstSelNew = -1; + long nLastSelNew = -1; + + std::vector<std::unique_ptr<weld::TreeIter>> aNewSelection; + + // restore selection state, expand state, and current-entry (under cursor) + if (mxTreeView->get_iter_first(*xEntry)) { - CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); - if( pEffect.get() ) + do { - // Any effects that were visible should still be visible, so expand their parents. - // (a previously expanded parent may have moved leaving a child to now be the new parent to expand) - if( std::find( aVisible.begin(), aVisible.end(), pEffect ) != aVisible.end() ) - { - if( GetParent(pEntry) ) - Expand( GetParent(pEntry) ); - } + CustomAnimationListEntryItem* pEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry).toInt64()); - if( std::find( aSelected.begin(), aSelected.end(), pEffect ) != aSelected.end() ) - Select( pEntry ); + CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); + if (pEffect.get()) + { + // Any effects that were visible should still be visible, so expand their parents. + // (a previously expanded parent may have moved leaving a child to now be the new parent to expand) + if( std::find( aVisible.begin(), aVisible.end(), pEffect ) != aVisible.end() ) + { + if (mxTreeView->get_iter_depth(*xEntry)) + { + std::unique_ptr<weld::TreeIter> xParentEntry = mxTreeView->make_iterator(xEntry.get()); + mxTreeView->iter_parent(*xParentEntry); + mxTreeView->expand_row(*xParentEntry); + } + } - // Restore the cursor; don't use SetCurEntry() as it may deselect other effects - if( pEffect == aCurrent ) - SetCursor( pEntry ); + if( std::find( aSelected.begin(), aSelected.end(), pEffect ) != aSelected.end() ) + aNewSelection.emplace_back(mxTreeView->make_iterator(xEntry.get())); - if( pEffect == pFirstSelEffect ) - nFirstSelNew = GetAbsPos( pEntry ); + // Restore the cursor, as it may deselect other effects wait until + // after the loop to reset the selection + if( pEffect == aCurrent ) + mxTreeView->set_cursor(*xEntry); - if( pEffect == pLastSelEffect ) - nLastSelNew = GetAbsPos( pEntry ); - } + if (pEffect == pFirstSelEffect) + nFirstSelNew = weld::GetAbsPos(*mxTreeView, *xEntry); - pEntry = static_cast<CustomAnimationListEntry*>(Next( pEntry )); + if (pEffect == pLastSelEffect) + nLastSelNew = weld::GetAbsPos(*mxTreeView, *xEntry); + } + } while (mxTreeView->iter_next(*xEntry)); } + for (const auto& rEntry : aNewSelection) + mxTreeView->select(*rEntry); + // Scroll to a selected entry, depending on where the selection moved. const bool bMoved = nFirstSelNew != nFirstSelOld; const bool bMovedUp = nFirstSelNew < nFirstSelOld; @@ -981,103 +826,104 @@ void CustomAnimationList::update() { // The entries in the selection range can't fit in view. // Scroll so the last selected entry is last in view. - ScrollToAbsPos( nLastSelNew - (nLastVis - nFirstVis) ); + mxTreeView->vadjustment_set_value(nLastSelNew - (nLastVis - nFirstVis)); } else - ScrollToAbsPos( nFirstSelNew ); + mxTreeView->vadjustment_set_value(nFirstSelNew); } else if( bMoved && nFirstSelOld > nLastVis && nFirstSelNew > nLastVis ) { // The selection is below the visible area. // Scroll down to the first few selected entries. - ScrollToAbsPos( nFirstSelNew ); + mxTreeView->vadjustment_set_value(nFirstSelNew); } else if( bMovedUp && nFirstSelOld <= nFirstVis ) { // A visible entry has moved up out of view; scroll up one. - ScrollToAbsPos( nFirstVis - 1 ); + mxTreeView->vadjustment_set_value(nFirstVis - 1); } else if( bMovedDown && nLastSelOld >= nLastVis ) { // An entry has moved down out of view; scroll down one. - ScrollToAbsPos( nFirstVis + 1 ); + mxTreeView->vadjustment_set_value(nFirstVis + 1); } else if ( nFirstVis != -1 ) { // The selection is still in view, or it hasn't moved. - ScrollToAbsPos( nFirstVis ); + mxTreeView->vadjustment_set_value(nFirstVis); } } mbIgnorePaint = false; - SetUpdateMode( true ); - Invalidate(); + + Select(); } void CustomAnimationList::append( CustomAnimationEffectPtr pEffect ) { - // create a ui description - OUString aDescription; - Any aTarget( pEffect->getTarget() ); if( !aTarget.hasValue() ) return; try { - aDescription = getDescription( aTarget, pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_BACKGROUND ); + // create a ui description + OUString aDescription = getDescription(aTarget, pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_BACKGROUND); - SvTreeListEntry* pParentEntry = nullptr; + std::unique_ptr<weld::TreeIter> xParentEntry; Reference< XShape > xTargetShape( pEffect->getTargetShape() ); sal_Int32 nGroupId = pEffect->getGroupId(); // if this effect has the same target and group-id as the last root effect, // the last root effect is also this effects parent - if( mpLastParentEntry && (nGroupId != -1) && (mxLastTargetShape == xTargetShape) && (mnLastGroupId == nGroupId) ) - pParentEntry = mpLastParentEntry; + if (mxLastParentEntry && nGroupId != -1 && mxLastTargetShape == xTargetShape && mnLastGroupId == nGroupId) + xParentEntry = mxTreeView->make_iterator(mxLastParentEntry.get()); // create an entry for the effect - SvTreeListEntry* pEntry = new CustomAnimationListEntry( pEffect ); + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + + mxEntries.emplace_back(std::make_unique<CustomAnimationListEntryItem>(aDescription, pEffect)); - pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(Image(), Image(), false)); - pEntry->AddItem(std::make_unique<CustomAnimationListEntryItem>(aDescription, pEffect, this)); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(mxEntries.back().get()))); - if( pParentEntry ) + if (xParentEntry) { // add a subentry - Insert( pEntry, pParentEntry ); + mxTreeView->insert(xParentEntry.get(), -1, &aDescription, &sId, nullptr, nullptr, nullptr, false, xEntry.get()); } else { // add a root entry - Insert( pEntry ); + mxTreeView->insert(nullptr, -1, &aDescription, &sId, nullptr, nullptr, nullptr, false, xEntry.get()); // and the new root entry becomes the possible next group header mxLastTargetShape = xTargetShape; mnLastGroupId = nGroupId; - mpLastParentEntry = pEntry; + mxLastParentEntry = std::move(xEntry); } } - catch( Exception& ) + catch (const Exception&) { OSL_FAIL("sd::CustomAnimationList::append(), exception caught!" ); } } -static void selectShape( SvTreeListBox* pTreeList, const Reference< XShape >& xShape ) +static void selectShape(weld::TreeView* pTreeList, const Reference< XShape >& xShape ) { - CustomAnimationListEntry* pEntry = static_cast< CustomAnimationListEntry* >(pTreeList->First()); - while( pEntry ) + std::unique_ptr<weld::TreeIter> xEntry = pTreeList->make_iterator(); + if (pTreeList->get_iter_first(*xEntry)) { - CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); - if( pEffect.get() ) + do { - if( pEffect->getTarget() == xShape ) - pTreeList->Select( pEntry ); - } - - pEntry = static_cast< CustomAnimationListEntry* >(pTreeList->Next( pEntry )); + CustomAnimationListEntryItem* pEntry = reinterpret_cast<CustomAnimationListEntryItem*>(pTreeList->get_id(*xEntry).toInt64()); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + { + if (pEffect->getTarget() == xShape) + pTreeList->select(*xEntry); + } + } while (pTreeList->iter_next(*xEntry)); } } @@ -1085,7 +931,7 @@ void CustomAnimationList::onSelectionChanged(const Any& rSelection) { try { - SelectAll(false); + mxTreeView->unselect_all(); if (rSelection.hasValue()) { @@ -1098,18 +944,18 @@ void CustomAnimationList::onSelectionChanged(const Any& rSelection) { Reference< XShape > xShape( xShapes->getByIndex( nIndex ), UNO_QUERY ); if( xShape.is() ) - selectShape( this, xShape ); + selectShape(mxTreeView.get(), xShape); } } else { Reference< XShape > xShape(rSelection, UNO_QUERY); if( xShape.is() ) - selectShape( this, xShape ); + selectShape(mxTreeView.get(), xShape); } } - SelectHdl(); + Select(); } catch( Exception& ) { @@ -1117,170 +963,186 @@ void CustomAnimationList::onSelectionChanged(const Any& rSelection) } } -// Notify controller to refresh UI when we are notified of selection change from base class -void CustomAnimationList::SelectHdl() +IMPL_LINK_NOARG(CustomAnimationList, SelectHdl, weld::TreeView&, void) { - if( mbIgnorePaint ) - return; - SvTreeListBox::SelectHdl(); - mpController->onSelect(); + Select(); } // Notify controller to refresh UI when we are notified of selection change from base class -void CustomAnimationList::DeselectHdl() +void CustomAnimationList::Select() { if( mbIgnorePaint ) return; - SvTreeListBox::DeselectHdl(); mpController->onSelect(); } - -bool CustomAnimationList::Expand( SvTreeListEntry* pParent ) +IMPL_LINK_NOARG(CustomAnimationList, PostExpandHdl, void*, void) { - bool result = SvTreeListBox::Expand( pParent ); - - // If expanded entry is selected, then select its children too. - if( IsSelected( pParent )) { - for( auto pChild = FirstChild( pParent ); pChild; pChild = pChild->NextSibling() ) + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_selected(xEntry.get())) + { + for (bool bChild = mxTreeView->iter_children(*xEntry); bChild; bChild = mxTreeView->iter_next_sibling(*xEntry)) { - if( !IsSelected( pChild ) ) - { - SelectListEntry( pChild, true ); - } + if (!mxTreeView->is_selected(*xEntry)) + mxTreeView->select(*xEntry); } } // Notify controller that selection has changed (it should update the UI) mpController->onSelect(); - return result; + mnPostExpandEvent = nullptr; } -bool CustomAnimationList::Collapse( SvTreeListEntry* pParent ) +IMPL_LINK(CustomAnimationList, ExpandHdl, const weld::TreeIter&, rParent, bool) { - // SvTreeListBox::Collapse(..) discards multi-selection state - // of list entries, so first save current selection state - std::vector< SvTreeListEntry* > selectedEntries; - for( auto pEntry = FirstSelected(); pEntry; pEntry = NextSelected( pEntry )) - { - selectedEntries.push_back( pEntry ); + // If expanded entry is selected, then select its children too afterwards. + if (mxTreeView->is_selected(rParent) && !mnPostExpandEvent) { + mnPostExpandEvent = Application::PostUserEvent(LINK(this, CustomAnimationList, PostExpandHdl)); } - // Execute collapse on base class - bool result = SvTreeListBox::Collapse( pParent ); + return true; +} +IMPL_LINK_NOARG(CustomAnimationList, PostCollapseHdl, void*, void) +{ // Deselect all entries as SvTreeListBox::Collapse selects the last // entry to have focus (or its parent), which is not desired - for( auto pEntry = FirstSelected(); pEntry; pEntry = NextSelected( pEntry )) - { - SelectListEntry( pEntry, false ); - } + mxTreeView->unselect_all(); // Restore selection state for entries which are still visible - for( auto &pEntry : selectedEntries ) + for (auto &pEntry : lastSelectedEntries) { - if( IsEntryVisible( pEntry )) - { - SelectListEntry( pEntry, true ); - } + if (weld::IsEntryVisible(*mxTreeView, *pEntry)) + mxTreeView->select(*pEntry); } + lastSelectedEntries.clear(); + // Notify controller that selection has changed (it should update the UI) mpController->onSelect(); - return result; + mnPostCollapseEvent = nullptr; } -bool CustomAnimationList::isExpanded( const CustomAnimationEffectPtr& pEffect ) const +IMPL_LINK_NOARG(CustomAnimationList, CollapseHdl, const weld::TreeIter&, bool) { - CustomAnimationListEntry* pEntry = static_cast<CustomAnimationListEntry*>(First()); - - while( pEntry ) + if (!mnPostCollapseEvent) { - if( pEntry->getEffect() == pEffect ) - break; - - pEntry = static_cast<CustomAnimationListEntry*>(Next( pEntry )); + // weld::TreeView::collapse() discards multi-selection state + // of list entries, so first save current selection state + mxTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + lastSelectedEntries.emplace_back(mxTreeView->make_iterator(&rEntry)); + return false; + }); + + mnPostCollapseEvent = Application::PostUserEvent(LINK(this, CustomAnimationList, PostCollapseHdl)); } - if( pEntry ) - pEntry = static_cast<CustomAnimationListEntry*>(GetParent( pEntry )); - - return (pEntry == nullptr) || IsExpanded( pEntry ); + // Execute collapse on base class + return true; } -bool CustomAnimationList::isVisible( const CustomAnimationEffectPtr& pEffect ) const +bool CustomAnimationList::isExpanded( const CustomAnimationEffectPtr& pEffect ) const { - CustomAnimationListEntry* pEntry = static_cast<CustomAnimationListEntry*>(First()); + bool bExpanded = true; // we assume expanded by default - while( pEntry ) + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) { - if( pEntry->getEffect() == pEffect ) - break; - - pEntry = static_cast<CustomAnimationListEntry*>(Next( pEntry )); + do + { + CustomAnimationListEntryItem* pEntry = + reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry).toInt64()); + if (pEntry->getEffect() == pEffect) + { + if (mxTreeView->get_iter_depth(*xEntry)) // no parent, keep expanded default of true + { + std::unique_ptr<weld::TreeIter> xParentEntry = mxTreeView->make_iterator(xEntry.get()); + if (mxTreeView->iter_parent(*xParentEntry)) + bExpanded = mxTreeView->get_row_expanded(*xParentEntry); + } + break; + } + } while (mxTreeView->iter_next(*xEntry)); } - return (pEntry == nullptr) || IsEntryVisible( pEntry ); + return bExpanded; +} + +bool CustomAnimationList::isVisible(const CustomAnimationEffectPtr& pEffect) const +{ + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pTestEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry).toInt64()); + if (pTestEntry->getEffect() == pEffect) + return weld::IsEntryVisible(*mxTreeView, *xEntry); + } while (mxTreeView->iter_next(*xEntry)); + } + return true; } EffectSequence CustomAnimationList::getSelection() const { EffectSequence aSelection; - CustomAnimationListEntry* pEntry = dynamic_cast< CustomAnimationListEntry* >(FirstSelected()); - while( pEntry ) - { - CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); - if( pEffect.get() ) - aSelection.push_back( pEffect ); + mxTreeView->selected_foreach([this, &aSelection](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(rEntry).toInt64()); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + aSelection.push_back(pEffect); // if the selected effect is not expanded and has children // we say that the children are automatically selected - if( !IsExpanded( pEntry ) ) + if (!mxTreeView->get_row_expanded(rEntry) && mxTreeView->iter_has_child(rEntry)) { - CustomAnimationListEntry* pChild = dynamic_cast< CustomAnimationListEntry* >( FirstChild( pEntry ) ); - while( pChild ) + std::unique_ptr<weld::TreeIter> xChild = mxTreeView->make_iterator(&rEntry); + mxTreeView->iter_children(*xChild); + + do { - if( !IsSelected( pChild ) ) + if (!mxTreeView->is_selected(*xChild)) { + CustomAnimationListEntryItem* pChild = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xChild).toInt64()); const CustomAnimationEffectPtr& pChildEffect( pChild->getEffect() ); if( pChildEffect.get() ) aSelection.push_back( pChildEffect ); } - - pChild = dynamic_cast< CustomAnimationListEntry* >( pChild->NextSibling() ); - } + } while (mxTreeView->iter_next_sibling(*xChild)); } - pEntry = static_cast< CustomAnimationListEntry* >(NextSelected( pEntry )); - } + return false; + }); return aSelection; } -bool CustomAnimationList::DoubleClickHdl() +IMPL_LINK_NOARG(CustomAnimationList, DoubleClickHdl, weld::TreeView&, bool) { mpController->onDoubleClick(); return false; } -VclPtr<PopupMenu> CustomAnimationList::CreateContextMenu() +IMPL_LINK(CustomAnimationList, CommandHdl, const CommandEvent&, rCEvt, bool) { - mxMenu.disposeAndClear(); - mxBuilder.reset(new VclBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/simpress/ui/effectmenu.ui", "")); - mxMenu.set(mxBuilder->get_menu("menu")); + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxTreeView.get(), "modules/simpress/ui/effectmenu.ui")); + std::unique_ptr<weld::Menu> xMenu = xBuilder->weld_menu("menu"); sal_Int16 nNodeType = -1; sal_Int16 nEntries = 0; - CustomAnimationListEntry* pEntry = static_cast< CustomAnimationListEntry* >(FirstSelected()); - while( pEntry ) - { + mxTreeView->selected_foreach([this, &nNodeType, &nEntries](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = reinterpret_cast<CustomAnimationListEntryItem*>(mxTreeView->get_id(rEntry).toInt64()); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + nEntries++; - CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); - if( pEffect.get() ) + if (pEffect.get()) { if( nNodeType == -1 ) { @@ -1291,26 +1153,30 @@ VclPtr<PopupMenu> CustomAnimationList::CreateContextMenu() if( nNodeType != pEffect->getNodeType() ) { nNodeType = -1; - break; + return true; } } } - pEntry = static_cast< CustomAnimationListEntry* >(NextSelected( pEntry )); - } + return false; + }); + + xMenu->set_active("onclick", nNodeType == EffectNodeType::ON_CLICK); + xMenu->set_active("withprev", nNodeType == EffectNodeType::WITH_PREVIOUS); + xMenu->set_active("afterprev", nNodeType == EffectNodeType::AFTER_PREVIOUS); + xMenu->set_sensitive("options", nEntries == 1); + xMenu->set_sensitive("timing", nEntries == 1); - mxMenu->CheckItem("onclick", nNodeType == EffectNodeType::ON_CLICK); - mxMenu->CheckItem("withprev", nNodeType == EffectNodeType::WITH_PREVIOUS); - mxMenu->CheckItem("afterprev", nNodeType == EffectNodeType::AFTER_PREVIOUS); - mxMenu->EnableItem(mxMenu->GetItemId("options"), nEntries == 1); - mxMenu->EnableItem(mxMenu->GetItemId("timing"), nEntries == 1); + OString sCommand = xMenu->popup_at_rect(mxTreeView.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); - return mxMenu; + return true; } -void CustomAnimationList::ExecuteContextMenuAction( sal_uInt16 nSelectedPopupEntry ) +void CustomAnimationList::ExecuteContextMenuAction(const OString& rIdent) { - mpController->onContextMenu(mxMenu->GetItemIdent(nSelectedPopupEntry)); + mpController->onContextMenu(rIdent); } void CustomAnimationList::notify_change() @@ -1319,34 +1185,6 @@ void CustomAnimationList::notify_change() mpController->onSelect(); } -void CustomAnimationList::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) -{ - if( mbIgnorePaint ) - return; - - SvTreeListBox::Paint(rRenderContext, rRect); - - // draw help text if list box is still empty - if( First() != nullptr ) - return; - - Color aOldColor(rRenderContext.GetTextColor()); - rRenderContext.SetTextColor(rRenderContext.GetSettings().GetStyleSettings().GetDisableColor()); - ::Point aOffset(rRenderContext.LogicToPixel(Point(6, 6), MapMode(MapUnit::MapAppFont))); - - ::tools::Rectangle aRect(Point(0,0), GetOutputSizePixel()); - - aRect.AdjustLeft(aOffset.X() ); - aRect.AdjustTop(aOffset.Y() ); - aRect.AdjustRight( -(aOffset.X()) ); - aRect.AdjustBottom( -(aOffset.Y()) ); - - rRenderContext.DrawText(aRect, SdResId(STR_CUSTOMANIMATION_LIST_HELPTEXT), - DrawTextFlags::MultiLine | DrawTextFlags::WordBreak | DrawTextFlags::Center | DrawTextFlags::VCenter ); - - rRenderContext.SetTextColor(aOldColor); -} - } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |