tdf#154211 SwNavigator: select range of content by shift + double-
click or shift + enter key
Enhancement to make a selection from the current cursor position in
the document to the position in the document that is navigated to
when a content entry in the Navigator tree is double clicked or the
enter key is pressed.
No selection is made when the current cursor position is in a table,
header, footer, footnote, or frame.
To make shift + double-click behavior for x11 and qt5 the same as
gtk3, FunctionSet::SetCursorAtPoint is added to the Shift case for
SelectionMode::Single in SelectionEngine::SelMouseButtonDown.
Change-Id: Id845dad8011ff7777a24f9b2730f10c62271c368
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151157
Tested-by: Jenkins
Reviewed-by: Jim Raykowski <raykowj@gmail.com>
diff --git a/sw/source/uibase/inc/conttree.hxx b/sw/source/uibase/inc/conttree.hxx
index 5bcdc90..ee04aea 100644
--- a/sw/source/uibase/inc/conttree.hxx
+++ b/sw/source/uibase/inc/conttree.hxx
@@ -134,6 +134,9 @@ class SwContentTree final : public SfxListener
bool m_bDocHasChanged = true;
bool m_bIgnoreDocChange = false; // used to prevent tracking update
ImplSVEvent* m_nRowActivateEventId = nullptr;
bool m_bSelectTo = false;
std::unique_ptr<weld::TreeIter> m_xOverlayCompareEntry;
std::unique_ptr<sdr::overlay::OverlayObject> m_xOverlayObject;
@@ -199,6 +202,7 @@ class SwContentTree final : public SfxListener
/** Collapse - Remember the state for content types. */
DECL_LINK(CollapseHdl, const weld::TreeIter&, bool);
DECL_LINK(ContentDoubleClickHdl, weld::TreeView&, bool);
DECL_LINK(AsyncContentDoubleClickHdl, void*, void);
DECL_LINK(SelectHdl, weld::TreeView&, void);
DECL_LINK(FocusInHdl, weld::Widget&, void);
DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
@@ -208,6 +212,7 @@ class SwContentTree final : public SfxListener
DECL_LINK(TimerUpdate, Timer *, void);
DECL_LINK(OverlayObjectDelayTimerHdl, Timer *, void);
DECL_LINK(MouseMoveHdl, const MouseEvent&, bool);
DECL_LINK(MousePressHdl, const MouseEvent&, bool);
public:
SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog);
diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx
index 86975a4..05721ad 100644
--- a/sw/source/uibase/utlui/content.cxx
+++ b/sw/source/uibase/utlui/content.cxx
@@ -1102,6 +1102,7 @@ SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNaviga
m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl));
m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl));
m_xTreeView->connect_mouse_move(LINK(this, SwContentTree, MouseMoveHdl));
m_xTreeView->connect_mouse_press(LINK(this, SwContentTree, MousePressHdl));
for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
{
@@ -1142,6 +1143,12 @@ SwContentTree::~SwContentTree()
SetActiveShell(nullptr);
}
IMPL_LINK(SwContentTree, MousePressHdl, const MouseEvent&, rMEvt, bool)
{
m_bSelectTo = rMEvt.IsShift() && (m_pConfig->IsNavigateOnSelect() || rMEvt.GetClicks() == 2);
return false;
}
IMPL_LINK(SwContentTree, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
{
if (m_eState == State::HIDDEN)
@@ -2332,9 +2339,36 @@ IMPL_LINK(SwContentTree, CollapseHdl, const weld::TreeIter&, rParent, bool)
// Also on double click will be initially opened only.
IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
{
if (m_nRowActivateEventId)
Application::RemoveUserEvent(m_nRowActivateEventId);
// post the event to process row activate after mouse press event to be able to set key
// modifier for selection feature (tdf#154211)
m_nRowActivateEventId
= Application::PostUserEvent(LINK(this, SwContentTree, AsyncContentDoubleClickHdl));
bool bConsumed = false;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
if (m_xTreeView->get_cursor(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView) &&
(State::HIDDEN != m_eState))
{
SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
assert(pCnt && "no UserData");
if (pCnt && !pCnt->IsInvisible())
{
// fdo#36308 don't expand outlines on double-click
bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE;
}
}
return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children
}
IMPL_LINK_NOARG(SwContentTree, AsyncContentDoubleClickHdl, void*, void)
{
m_nRowActivateEventId = nullptr;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
bool bEntry = m_xTreeView->get_cursor(xEntry.get());
// Is it a content type?
OSL_ENSURE(bEntry, "no current entry!");
@@ -2358,13 +2392,9 @@ IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
}
//Jump to content type:
GotoContent(pCnt);
// fdo#36308 don't expand outlines on double-click
bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE;
}
}
}
return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children
}
namespace
@@ -4191,6 +4221,10 @@ IMPL_LINK(SwContentTree, KeyInputHdl, const KeyEvent&, rEvent, bool)
else
ContentDoubleClickHdl(*m_xTreeView);
break;
case KEY_SHIFT:
m_bSelectTo = true;
ContentDoubleClickHdl(*m_xTreeView);
break;
}
}
}
@@ -5169,13 +5203,18 @@ void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode)
static void lcl_AssureStdModeAtShell(SwWrtShell* pWrtShell)
{
// deselect any drawing or frame and leave editing mode
SdrView* pSdrView = pWrtShell->GetDrawView();
if (pSdrView && pSdrView->IsTextEdit() )
if (SdrView* pSdrView = pWrtShell->GetDrawView())
{
bool bLockView = pWrtShell->IsViewLocked();
pWrtShell->LockView(true);
pWrtShell->EndTextEdit();
pWrtShell->LockView(bLockView);
if (pSdrView->IsTextEdit())
{
bool bLockView = pWrtShell->IsViewLocked();
pWrtShell->LockView(true);
pWrtShell->EndTextEdit();
pWrtShell->LockView(bLockView);
}
// go out of the frame
Point aPt(LONG_MIN, LONG_MIN);
pWrtShell->SelectObj(aPt, SW_LEAVE_FRAME);
}
if (pWrtShell->IsSelFrameMode() || pWrtShell->IsObjSelected())
@@ -5219,9 +5258,27 @@ void SwContentTree::CopyOutlineSelections()
void SwContentTree::GotoContent(const SwContent* pCnt)
{
if (m_bSelectTo)
{
if (m_pActiveShell->IsCursorInTable() ||
(m_pActiveShell->GetCursor()->GetPoint()->nNode.GetIndex() <=
m_pActiveShell->GetDoc()->GetNodes().GetEndOfExtras().GetIndex()))
{
m_bSelectTo = false;
m_pActiveShell->GetView().GetEditWin().GrabFocus();
return;
}
}
m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos;
m_sSelectedItem = "";
lcl_AssureStdModeAtShell(m_pActiveShell);
std::optional<std::unique_ptr<SwPosition>> oPosition;
if (m_bSelectTo)
oPosition.emplace(new SwPosition(m_pActiveShell->GetCursor()->GetPoint()->nNode,
m_pActiveShell->GetCursor()->GetPoint()->nContent));
switch(m_nLastSelType = pCnt->GetParent()->GetType())
{
case ContentTypeId::TEXTFIELD:
@@ -5306,25 +5363,50 @@ void SwContentTree::GotoContent(const SwContent* pCnt)
default: break;
}
if (m_pActiveShell->IsFrameSelected() || m_pActiveShell->IsObjSelected())
if (m_bSelectTo)
{
m_pActiveShell->HideCursor();
m_pActiveShell->EnterSelFrameMode();
m_pActiveShell->SttCursorMove();
while (m_pActiveShell->IsCursorInTable())
{
m_pActiveShell->MoveTable(GotoCurrTable, fnTableStart);
if (!m_pActiveShell->Left(SwCursorSkipMode::Chars, false, 1, false))
break; // Table is at the beginning of the document. It can't be selected this way.
}
m_pActiveShell->EndCursorMove();
lcl_AssureStdModeAtShell(m_pActiveShell);
m_pActiveShell->SetMark();
m_pActiveShell->GetCursor()->GetMark()->nNode = oPosition.value()->nNode;
m_pActiveShell->GetCursor()->GetMark()->nContent = oPosition.value()->nContent;
m_pActiveShell->UpdateCursor();
m_pActiveShell->GetView().GetEditWin().GrabFocus();
m_bSelectTo = false;
}
SwView& rView = m_pActiveShell->GetView();
rView.StopShellTimer();
rView.GetPostItMgr()->SetActiveSidebarWin(nullptr);
rView.GetEditWin().GrabFocus();
// Assure cursor is in visible view area.
// (tdf#147041) Always show the navigated outline at the top of the visible view area.
if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE ||
(!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() &&
!m_pActiveShell->IsObjSelected()))
else
{
Point aPoint(rView.GetVisArea().getX(), m_pActiveShell->GetCursorDocPos().getY());
rView.SetVisArea(aPoint);
if (m_pActiveShell->IsFrameSelected() || m_pActiveShell->IsObjSelected())
{
m_pActiveShell->HideCursor();
m_pActiveShell->EnterSelFrameMode();
}
SwView& rView = m_pActiveShell->GetView();
rView.StopShellTimer();
rView.GetPostItMgr()->SetActiveSidebarWin(nullptr);
rView.GetEditWin().GrabFocus();
// Assure cursor is in visible view area.
// (tdf#147041) Always show the navigated outline at the top of the visible view area.
if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE ||
(!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() &&
!m_pActiveShell->IsObjSelected()))
{
Point aPoint(rView.GetVisArea().getX(), m_pActiveShell->GetCursorDocPos().getY());
rView.SetVisArea(aPoint);
}
}
}
diff --git a/vcl/source/window/seleng.cxx b/vcl/source/window/seleng.cxx
index f81ffe6..9cb70cf 100644
--- a/vcl/source/window/seleng.cxx
+++ b/vcl/source/window/seleng.cxx
@@ -181,6 +181,7 @@ bool SelectionEngine::SelMouseButtonDown( const MouseEvent& rMEvt )
{
ReleaseMouse();
nFlags &= ~SelectionEngineFlags::IN_SEL;
pFunctionSet->SetCursorAtPoint(aPos);
return false;
}
if ( nFlags & SelectionEngineFlags::ADD_ALW )