tdf#145359 related: SdNavigator dnd

This is an enhancement patch that provides drag and drop capability
within the Navigator page object tree to allow arranging object
navigation order and grouping of objects.

Change-Id: I76996cd909765fb3503cf077566bec267b7705e2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142640
Tested-by: Jenkins
Reviewed-by: Jim Raykowski <raykowj@gmail.com>
diff --git a/sd/source/ui/dlg/navigatr.cxx b/sd/source/ui/dlg/navigatr.cxx
index 98883e7..cdc2db1 100644
--- a/sd/source/ui/dlg/navigatr.cxx
+++ b/sd/source/ui/dlg/navigatr.cxx
@@ -808,7 +808,7 @@ void SdNavigatorControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId,
    if (nState & NavState::TableUpdate)
    {
        // InitTlb; is initiated by Slot
        if (maUpdateRequest)
        if (maUpdateRequest && !pNavigatorWin->GetObjects().get_treeview().has_focus())
            maUpdateRequest();
    }
}
diff --git a/sd/source/ui/dlg/sdtreelb.cxx b/sd/source/ui/dlg/sdtreelb.cxx
index 8a9002a..a90a76a 100644
--- a/sd/source/ui/dlg/sdtreelb.cxx
+++ b/sd/source/ui/dlg/sdtreelb.cxx
@@ -379,8 +379,8 @@ namespace

        std::unique_ptr<weld::TreeIter> xSourceParent(rTreeView.make_iterator(xSource.get()));
        bool bSourceHasParent = rTreeView.iter_parent(*xSourceParent);
        // level 1 objects only
        if (!bSourceHasParent || rTreeView.get_iter_depth(*xSourceParent))
        // disallow root drag
        if (!bSourceHasParent)
            return false;

        SdrObject* pSourceObject = weld::fromId<SdrObject*>(rTreeView.get_id(*xSource));
@@ -495,20 +495,36 @@ sal_Int8 SdPageObjsTLVDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
    if (!m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true))
        return DND_ACTION_NONE;

    // disallow when root is drop target
    if (m_rTreeView.get_iter_depth(*xTarget) == 0)
        return DND_ACTION_NONE;

    // disallow if there is no source entry selected
    std::unique_ptr<weld::TreeIter> xSource(m_rTreeView.make_iterator());
    if (!m_rTreeView.get_selected(xSource.get()))
        return DND_ACTION_NONE;

    // disallow when root is source
    if (m_rTreeView.get_iter_depth(*xSource) == 0)
        return DND_ACTION_NONE;

    // disallow when the source is the parent or ancestoral parent of the target
    std::unique_ptr<weld::TreeIter> xTargetParent(m_rTreeView.make_iterator(xTarget.get()));
    while (m_rTreeView.get_iter_depth(*xTargetParent))
        m_rTreeView.iter_parent(*xTargetParent);
    while (m_rTreeView.get_iter_depth(*xTargetParent) > 1)
    {
        if (!m_rTreeView.iter_parent(*xTargetParent) ||
                m_rTreeView.iter_compare(*xSource, *xTargetParent) == 0)
            return DND_ACTION_NONE;
    }

    std::unique_ptr<weld::TreeIter> xSourceParent(m_rTreeView.make_iterator(xSource.get()));
    while (m_rTreeView.get_iter_depth(*xSourceParent))
        m_rTreeView.iter_parent(*xSourceParent);

    // can only drop within the same page
    if (m_rTreeView.iter_compare(*xTargetParent, *xSourceParent) != 0)
    // disallow drop when source and target are not within the same page
    std::unique_ptr<weld::TreeIter> xSourcePage(m_rTreeView.make_iterator(xSource.get()));
    std::unique_ptr<weld::TreeIter> xTargetPage(m_rTreeView.make_iterator(xTarget.get()));
    while (m_rTreeView.get_iter_depth(*xTargetPage))
        m_rTreeView.iter_parent(*xTargetPage);
    while (m_rTreeView.get_iter_depth(*xSourcePage))
        m_rTreeView.iter_parent(*xSourcePage);
    if (m_rTreeView.iter_compare(*xTargetPage, *xSourcePage) != 0)
        return DND_ACTION_NONE;

    return DND_ACTION_MOVE;
@@ -529,35 +545,73 @@ sal_Int8 SdPageObjsTLVDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt )
        return DND_ACTION_NONE;

    std::unique_ptr<weld::TreeIter> xTarget(m_rTreeView.make_iterator());
    if (!m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true))
    if (!m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), false))
        return DND_ACTION_NONE;
    int nTargetPos = m_rTreeView.get_iter_index_in_parent(*xTarget) + 1;

    auto nIterCompare = m_rTreeView.iter_compare(*xSource, *xTarget);
    if (nIterCompare == 0)
    {
        // drop position is the same as source position
        return DND_ACTION_NONE;
    }

    SdrObject* pTargetObject = weld::fromId<SdrObject*>(m_rTreeView.get_id(*xTarget));
    SdrObject* pSourceObject = weld::fromId<SdrObject*>(m_rTreeView.get_id(*xSource));
    if (pSourceObject == reinterpret_cast<SdrObject*>(1))
        pSourceObject = nullptr;
    if (pTargetObject == reinterpret_cast<SdrObject*>(1))
        pTargetObject = nullptr;

    if (pTargetObject != nullptr && pSourceObject != nullptr)
    {
        SdrPage* pObjectList = pSourceObject->getSdrPageFromSdrObject();
        if (pObjectList != nullptr)
        {
            sal_uInt32 nNewPosition;
            if (pTargetObject == reinterpret_cast<SdrObject*>(1))
            {
                nNewPosition = 0;
                nTargetPos = 0;
            }
            else
                nNewPosition = pTargetObject->GetNavigationPosition() + 1;
            pObjectList->SetObjectNavigationPosition(*pSourceObject, nNewPosition);
        }

        std::unique_ptr<weld::TreeIter> xSourceParent(m_rTreeView.make_iterator(xSource.get()));
        m_rTreeView.iter_parent(*xSourceParent);
        std::unique_ptr<weld::TreeIter> xTargetParent(m_rTreeView.make_iterator(xTarget.get()));
        m_rTreeView.iter_parent(*xTargetParent);

        m_rTreeView.move_subtree(*xSource, xSourceParent.get(), nTargetPos);
        int nTargetPos = m_rTreeView.get_iter_index_in_parent(*xTarget);

        // Make the tree view what the model will be when it is changed below.
        m_rTreeView.move_subtree(*xSource, xTargetParent.get(), nTargetPos);
        m_rTreeView.iter_previous_sibling(*xTarget);
        m_rTreeView.set_cursor(*xTarget);

        if (m_rTreeView.iter_compare(*xSourceParent, *xTargetParent) == 0 && nIterCompare < 0)
            nTargetPos = m_rTreeView.get_iter_index_in_parent(*xTarget);

        // Remove the source object from soure parent list and insert it in the target parent list.
        SdrObject* pSourceParentObject = weld::fromId<SdrObject*>(m_rTreeView.get_id(*xSourceParent));
        SdrObject* pTargetParentObject = weld::fromId<SdrObject*>(m_rTreeView.get_id(*xTargetParent));

        // Presumably there is need for a hard reference to hold on to the removed object so it is
        // guaranteed to be valid for insert back into an object list.
        rtl::Reference<SdrObject> rSourceObject;

        // remove object
        if (pSourceParentObject == reinterpret_cast<SdrObject*>(1))
        {
            rSourceObject = pObjectList->NbcRemoveObject(pSourceObject->GetOrdNum());
        }
        else
        {
            SdrObjList* pList = pSourceParentObject->GetSubList();
            rSourceObject = pList->NbcRemoveObject(pSourceObject->GetOrdNum());
        }

        // insert object
        if (pTargetParentObject == reinterpret_cast<SdrObject*>(1))
        {
            pObjectList->NbcInsertObject(rSourceObject.get());
            pObjectList->SetObjectNavigationPosition(*rSourceObject, nTargetPos);
        }
        else
        {
            SdrObjList* pList = pTargetParentObject->GetSubList();
            pList->NbcInsertObject(rSourceObject.get(), nTargetPos);
            pList->SetObjectNavigationPosition(*rSourceObject, nTargetPos);
        }
    }

    return DND_ACTION_NONE;
diff --git a/sd/uiconfig/simpress/ui/navigatorpanel.ui b/sd/uiconfig/simpress/ui/navigatorpanel.ui
index 98d22df..1059f7e 100644
--- a/sd/uiconfig/simpress/ui/navigatorpanel.ui
+++ b/sd/uiconfig/simpress/ui/navigatorpanel.ui
@@ -1,33 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.36.0 -->
<!-- Generated with glade 3.40.0 -->
<interface domain="sd">
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkMenu" id="dragmodemenu">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="can-focus">False</property>
    <child>
      <object class="GtkRadioMenuItem" id="1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="can-focus">False</property>
        <property name="label" translatable="yes" context="navigatorpanelSTR_DRAGTYPE_URL">Insert as Hyperlink</property>
        <property name="draw_as_radio">True</property>
        <property name="draw-as-radio">True</property>
      </object>
    </child>
    <child>
      <object class="GtkRadioMenuItem" id="2">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="can-focus">False</property>
        <property name="label" translatable="yes" context="navigatorpanel|STR_DRAGTYPE_LINK">Insert as Link</property>
        <property name="draw_as_radio">True</property>
        <property name="draw-as-radio">True</property>
        <property name="group">1</property>
      </object>
    </child>
    <child>
      <object class="GtkRadioMenuItem" id="3">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="can-focus">False</property>
        <property name="label" translatable="yes" context="navigatorpanel|STR_DRAGTYPE_EMBEDDED">Insert as Copy</property>
        <property name="draw_as_radio">True</property>
        <property name="draw-as-radio">True</property>
        <property name="group">1</property>
      </object>
    </child>
@@ -45,24 +45,24 @@
  <!-- n-columns=1 n-rows=1 -->
  <object class="GtkGrid" id="NavigatorPanel">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="can-focus">False</property>
    <property name="hexpand">True</property>
    <property name="vexpand">True</property>
    <property name="border_width">6</property>
    <property name="border-width">6</property>
    <child>
      <!-- n-columns=1 n-rows=1 -->
      <!-- n-columns=1 n-rows=3 -->
      <object class="GtkGrid">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="can-focus">False</property>
        <property name="hexpand">True</property>
        <property name="vexpand">True</property>
        <property name="row_spacing">3</property>
        <property name="column_spacing">6</property>
        <property name="row-spacing">3</property>
        <property name="column-spacing">6</property>
        <child>
          <object class="GtkComboBoxText" id="documents">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="tooltip_text" translatable="yes" context="navigatorpanel|documents|tooltip_text">Document</property>
            <property name="can-focus">False</property>
            <property name="tooltip-text" translatable="yes" context="navigatorpanel|documents|tooltip_text">Document</property>
            <child internal-child="accessible">
              <object class="AtkObject" id="documents-atkobject">
                <property name="AtkObject::accessible-name" translatable="yes" context="navigatorpanel|documents-atkobject">Active Window</property>
@@ -71,30 +71,29 @@
            </child>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">2</property>
            <property name="left-attach">0</property>
            <property name="top-attach">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="can-focus">True</property>
            <property name="hexpand">True</property>
            <property name="vexpand">True</property>
            <property name="shadow_type">in</property>
            <property name="shadow-type">in</property>
            <child>
              <object class="GtkTreeView" id="tree">
                <property name="width_request">-1</property>
                <property name="width-request">-1</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="can-focus">True</property>
                <property name="receives-default">True</property>
                <property name="hexpand">True</property>
                <property name="vexpand">True</property>
                <property name="model">liststore2</property>
                <property name="headers_visible">False</property>
                <property name="headers-visible">False</property>
                <property name="reorderable">True</property>
                <property name="search_column">1</property>
                <property name="enable_tree_lines">True</property>
                <property name="search-column">1</property>
                <child internal-child="selection">
                  <object class="GtkTreeSelection" id="Macro Library List-selection11"/>
                </child>
@@ -125,22 +124,23 @@
            </child>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">1</property>
            <property name="left-attach">0</property>
            <property name="top-attach">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkToolbar" id="toolbox">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="can-focus">True</property>
            <property name="hexpand">True</property>
            <property name="toolbar_style">icons</property>
            <property name="toolbar-style">icons</property>
            <property name="icon_size">2</property>
            <child>
              <object class="GtkToolButton" id="first">
                <property name="visible">True</property>
                <property name="tooltip_text" translatable="yes" context="navigatorpanel|first|tooltip_text">First Slide</property>
                <property name="icon_name">sd/res/nv03.png</property>
                <property name="can-focus">False</property>
                <property name="tooltip-text" translatable="yes" context="navigatorpanel|first|tooltip_text">First Slide</property>
                <property name="icon-name">sd/res/nv03.png</property>
                <child internal-child="accessible">
                  <object class="AtkObject" id="first-atkobject">
                    <property name="AtkObject::accessible-description" translatable="yes" context="navigatorpanel|extended_tip|first">Jumps to the first page.</property>
@@ -155,8 +155,9 @@
            <child>
              <object class="GtkToolButton" id="previous">
                <property name="visible">True</property>
                <property name="tooltip_text" translatable="yes" context="navigatorpanel|previous|tooltip_text">Previous Slide</property>
                <property name="icon_name">sd/res/nv04.png</property>
                <property name="can-focus">False</property>
                <property name="tooltip-text" translatable="yes" context="navigatorpanel|previous|tooltip_text">Previous Slide</property>
                <property name="icon-name">sd/res/nv04.png</property>
                <child internal-child="accessible">
                  <object class="AtkObject" id="previous-atkobject">
                    <property name="AtkObject::accessible-description" translatable="yes" context="navigatorpanel|extended_tip|previous">Moves back one page.</property>
@@ -171,8 +172,9 @@
            <child>
              <object class="GtkToolButton" id="next">
                <property name="visible">True</property>
                <property name="tooltip_text" translatable="yes" context="navigatorpanel|next|tooltip_text">Next Slide</property>
                <property name="icon_name">sd/res/nv05.png</property>
                <property name="can-focus">False</property>
                <property name="tooltip-text" translatable="yes" context="navigatorpanel|next|tooltip_text">Next Slide</property>
                <property name="icon-name">sd/res/nv05.png</property>
                <child internal-child="accessible">
                  <object class="AtkObject" id="next-atkobject">
                    <property name="AtkObject::accessible-description" translatable="yes" context="navigatorpanel|extended_tip|next">Move forward one page.</property>
@@ -187,8 +189,9 @@
            <child>
              <object class="GtkToolButton" id="last">
                <property name="visible">True</property>
                <property name="tooltip_text" translatable="yes" context="navigatorpanel|last|tooltip_text">Last Slide</property>
                <property name="icon_name">sd/res/nv06.png</property>
                <property name="can-focus">False</property>
                <property name="tooltip-text" translatable="yes" context="navigatorpanel|last|tooltip_text">Last Slide</property>
                <property name="icon-name">sd/res/nv06.png</property>
                <child internal-child="accessible">
                  <object class="AtkObject" id="last-atkobject">
                    <property name="AtkObject::accessible-description" translatable="yes" context="navigatorpanel|extended_tip|last">Jumps to the last page.</property>
@@ -203,7 +206,7 @@
            <child>
              <object class="GtkSeparatorToolItem" id="separator">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="can-focus">False</property>
                <property name="halign">end</property>
                <property name="hexpand">True</property>
              </object>
@@ -215,8 +218,9 @@
            <child>
              <object class="GtkMenuToolButton" id="dragmode">
                <property name="visible">True</property>
                <property name="tooltip_text" translatable="yes" context="navigatorpanel|dragmode|tooltip_text">Drag Mode</property>
                <property name="icon_name">sd/res/nv09.png</property>
                <property name="can-focus">False</property>
                <property name="tooltip-text" translatable="yes" context="navigatorpanel|dragmode|tooltip_text">Drag Mode</property>
                <property name="icon-name">sd/res/nv09.png</property>
                <child internal-child="accessible">
                  <object class="AtkObject" id="dragmode-atkobject">
                    <property name="AtkObject::accessible-description" translatable="yes" context="navigatorpanel|extended_tip|dragmode">Drag and drop slides and named objects into the active slide.</property>
@@ -231,8 +235,9 @@
            <child>
              <object class="GtkMenuToolButton" id="shapes">
                <property name="visible">True</property>
                <property name="tooltip_text" translatable="yes" context="navigatorpanel|shapes|tooltip_text">Show Shapes</property>
                <property name="icon_name">sd/res/graphic.png</property>
                <property name="can-focus">False</property>
                <property name="tooltip-text" translatable="yes" context="navigatorpanel|shapes|tooltip_text">Show Shapes</property>
                <property name="icon-name">sd/res/graphic.png</property>
                <child internal-child="accessible">
                  <object class="AtkObject" id="shapes-atkobject">
                    <property name="AtkObject::accessible-description" translatable="yes" context="navigatorpanel|extended_tip|shapes">In the submenu you can choose to display a list of all shapes or only the named shapes. Use drag-and-drop in the list to reorder the shapes. When you set the focus to a slide and press the Tab key, the next shape in the defined order is selected.</property>
@@ -246,14 +251,14 @@
            </child>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">0</property>
            <property name="left-attach">0</property>
            <property name="top-attach">0</property>
          </packing>
        </child>
      </object>
      <packing>
        <property name="left_attach">0</property>
        <property name="top_attach">0</property>
        <property name="left-attach">0</property>
        <property name="top-attach">0</property>
      </packing>
    </child>
    <child internal-child="accessible">
@@ -264,23 +269,23 @@
  </object>
  <object class="GtkMenu" id="shapemenu">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="can-focus">False</property>
    <child>
      <object class="GtkRadioMenuItem" id="named">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="can-focus">False</property>
        <property name="label" translatable="yes" context="navigatorpanel|STR_NAVIGATOR_SHOW_NAMED_SHAPES">Named shapes</property>
        <property name="use_underline">True</property>
        <property name="draw_as_radio">True</property>
        <property name="use-underline">True</property>
        <property name="draw-as-radio">True</property>
      </object>
    </child>
    <child>
      <object class="GtkRadioMenuItem" id="all">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="can-focus">False</property>
        <property name="label" translatable="yes" context="navigatorpanel|STR_NAVIGATOR_SHOW_ALL_SHAPES">All shapes</property>
        <property name="use_underline">True</property>
        <property name="draw_as_radio">True</property>
        <property name="use-underline">True</property>
        <property name="draw-as-radio">True</property>
        <property name="group">named</property>
      </object>
    </child>