summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editeng/source/editeng/editeng.cxx9
-rw-r--r--editeng/source/editeng/editview.cxx2
-rw-r--r--editeng/source/editeng/impedit3.cxx6
-rw-r--r--editeng/source/outliner/outliner.cxx7
-rw-r--r--include/editeng/editeng.hxx6
-rw-r--r--sd/qa/unit/tiledrendering/tiledrendering.cxx185
6 files changed, 203 insertions, 12 deletions
diff --git a/editeng/source/editeng/editeng.cxx b/editeng/source/editeng/editeng.cxx
index 9871e502316f..5a6aee022a80 100644
--- a/editeng/source/editeng/editeng.cxx
+++ b/editeng/source/editeng/editeng.cxx
@@ -1449,11 +1449,14 @@ sal_uInt32 EditEngine::CalcTextWidth()
return nWidth;
}
-void EditEngine::SetUpdateMode( bool bUpdate )
+void EditEngine::SetUpdateMode(bool bUpdate, bool bRestoring)
{
pImpEditEngine->SetUpdateMode( bUpdate );
- if ( pImpEditEngine->pActiveView )
- pImpEditEngine->pActiveView->ShowCursor( false, false, /*bActivate=*/true );
+ if (pImpEditEngine->pActiveView)
+ {
+ // Not an activation if we are restoring the previous update mode.
+ pImpEditEngine->pActiveView->ShowCursor(false, false, /*bActivate=*/!bRestoring);
+ }
}
bool EditEngine::GetUpdateMode() const
diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx
index 315f9ef6716f..3848553cf4d9 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -482,7 +482,7 @@ void EditView::ShowCursor( bool bGotoCursor, bool bForceVisCursor, bool bActivat
if (pParent && pParent->GetLOKWindowId() != 0)
return;
- OString aPayload = OString::boolean(true);
+ static const OString aPayload = OString::boolean(true);
pImpEditView->mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload.getStr());
pImpEditView->mpViewShell->NotifyOtherViews(LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload);
}
diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx
index 6ec2a6152ee6..05ab3ccf6823 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -3939,12 +3939,12 @@ EditPaM ImpEditEngine::ConnectContents( sal_Int32 nLeftNode, bool bBackward )
void ImpEditEngine::SetUpdateMode( bool bUp, EditView* pCurView, bool bForceUpdate )
{
- bool bChanged = ( GetUpdateMode() != bUp );
+ const bool bChanged = (GetUpdateMode() != bUp);
- // When switching from sal_True to sal_False, all selections were visible,
+ // When switching from true to false, all selections were visible,
// => paint over
// the other hand, were all invisible => paint
- // If !bFormatted, e.g. after SetText, then if UpdateMode=sal_True
+ // If !bFormatted, e.g. after SetText, then if UpdateMode=true
// formatting is not needed immediately, probably because more text is coming.
// At latest it is formatted at a Paint/CalcTextWidth.
bUpdate = bUp;
diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx
index 072851d2702f..f7f8cc316ad1 100644
--- a/editeng/source/outliner/outliner.cxx
+++ b/editeng/source/outliner/outliner.cxx
@@ -399,7 +399,7 @@ void Outliner::SetText( const OUString& rText, Paragraph* pPara )
{
DBG_ASSERT(pPara,"SetText:No Para");
- sal_Int32 nPara = pParaList->GetAbsPos( pPara );
+ const sal_Int32 nPara = pParaList->GetAbsPos( pPara );
if (pEditEngine->GetText( nPara ) == rText)
{
@@ -408,7 +408,7 @@ void Outliner::SetText( const OUString& rText, Paragraph* pPara )
return;
}
- bool bUpdate = pEditEngine->GetUpdateMode();
+ const bool bUpdate = pEditEngine->GetUpdateMode();
pEditEngine->SetUpdateMode( false );
ImplBlockInsertionCallbacks( true );
@@ -482,7 +482,8 @@ void Outliner::SetText( const OUString& rText, Paragraph* pPara )
DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"SetText failed!");
bFirstParaIsEmpty = false;
ImplBlockInsertionCallbacks( false );
- pEditEngine->SetUpdateMode( bUpdate );
+ // Restore the update mode.
+ pEditEngine->SetUpdateMode(bUpdate, /*bRestoring=*/true);
}
// pView == 0 -> Ignore tabs
diff --git a/include/editeng/editeng.hxx b/include/editeng/editeng.hxx
index 5f45dd1fbf29..20c15e6539e1 100644
--- a/include/editeng/editeng.hxx
+++ b/include/editeng/editeng.hxx
@@ -209,7 +209,11 @@ public:
void SetRefMapMode( const MapMode& rMapMode );
MapMode const & GetRefMapMode() const;
- void SetUpdateMode( bool bUpdate );
+ /// Change the update mode per bUpdate and potentially trigger FormatAndUpdate.
+ /// bRestoring is used for LOK to update cursor visibility, specifically,
+ /// when true, it means we are restoring the update mode after internally
+ /// disabling it (f.e. during SetText to set/delete default text in Impress).
+ void SetUpdateMode(bool bUpdate, bool bRestoring = false);
bool GetUpdateMode() const;
void SetBackgroundColor( const Color& rColor );
diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx b/sd/qa/unit/tiledrendering/tiledrendering.cxx
index 3f374b0fa051..2fed5db352df 100644
--- a/sd/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx
@@ -98,6 +98,10 @@ public:
void testViewCursors();
void testViewCursorParts();
void testCursorViews();
+ void testCursorVisibility_SingleClick();
+ void testCursorVisibility_DoubleClick();
+ void testCursorVisibility_MultiView();
+ void testCursorVisibility_Escape();
void testViewLock();
void testUndoLimiting();
void testCreateViewGraphicSelection();
@@ -149,6 +153,10 @@ public:
CPPUNIT_TEST(testViewCursors);
CPPUNIT_TEST(testViewCursorParts);
CPPUNIT_TEST(testCursorViews);
+ CPPUNIT_TEST(testCursorVisibility_SingleClick);
+ CPPUNIT_TEST(testCursorVisibility_DoubleClick);
+ CPPUNIT_TEST(testCursorVisibility_MultiView);
+ CPPUNIT_TEST(testCursorVisibility_Escape);
CPPUNIT_TEST(testViewLock);
CPPUNIT_TEST(testUndoLimiting);
CPPUNIT_TEST(testCreateViewGraphicSelection);
@@ -958,6 +966,7 @@ public:
/// Our current part, to be able to decide if a view cursor/selection is relevant for us.
int m_nPart;
bool m_bCursorVisibleChanged;
+ bool m_bCursorVisible;
bool m_bViewLock;
bool m_bTilesInvalidated;
std::vector<tools::Rectangle> m_aInvalidations;
@@ -971,6 +980,7 @@ public:
m_bGraphicViewSelectionInvalidated(false),
m_nPart(0),
m_bCursorVisibleChanged(false),
+ m_bCursorVisible(false),
m_bViewLock(false),
m_bTilesInvalidated(false),
m_bViewSelectionSet(false)
@@ -1030,6 +1040,7 @@ public:
case LOK_CALLBACK_CURSOR_VISIBLE:
{
m_bCursorVisibleChanged = true;
+ m_bCursorVisible = (OString("true") == pPayload);
}
break;
case LOK_CALLBACK_VIEW_LOCK:
@@ -1054,7 +1065,7 @@ public:
std::stringstream aStream(pPayload);
boost::property_tree::ptree aTree;
boost::property_tree::read_json(aStream, aTree);
- int nViewId = aTree.get_child("viewId").get_value<int>();
+ const int nViewId = aTree.get_child("viewId").get_value<int>();
m_aViewCursorVisibilities[nViewId] = OString("true") == pPayload;
}
break;
@@ -1173,6 +1184,178 @@ void SdTiledRenderingTest::testCursorViews()
CPPUNIT_ASSERT(aView2.m_bTilesInvalidated);
}
+void SdTiledRenderingTest::testCursorVisibility_SingleClick()
+{
+ // Single-clicking in a text box enters editing only
+ // when it's on the text, even if it's the default text.
+
+ // Load doc.
+ SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
+ ViewCallback aView1;
+
+ // Begin text edit on the only object on the slide.
+ sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+ SdPage* pActualPage = pViewShell->GetActualPage();
+ SdrObject* pObject1 = pActualPage->GetObj(0);
+ CPPUNIT_ASSERT(pObject1 != nullptr);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(OBJ_TITLETEXT), pObject1->GetObjIdentifier());
+ SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
+
+ // Click once outside of the text (in the first quartile) => no editing.
+ const ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
+ const auto cornerX = convertMm100ToTwip(aRect.getX() + (aRect.getWidth() / 4));
+ const auto cornerY = convertMm100ToTwip(aRect.getY() + (aRect.getHeight() / 4));
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ cornerX, cornerY,
+ 1, MOUSE_LEFT, 0);
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ cornerX, cornerY,
+ 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // No editing.
+ CPPUNIT_ASSERT(!pViewShell->GetView()->IsTextEdit());
+ CPPUNIT_ASSERT(!aView1.m_bCursorVisible);
+
+ // Click again, now on the text, in the center, to start editing.
+ const auto centerX = convertMm100ToTwip(aRect.getX() + (aRect.getWidth() / 2));
+ const auto centerY = convertMm100ToTwip(aRect.getY() + (aRect.getHeight() / 2));
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ centerX, centerY,
+ 1, MOUSE_LEFT, 0);
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ centerX, centerY,
+ 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // We must be in text editing mode and have cursor visible.
+ CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
+ CPPUNIT_ASSERT(aView1.m_bCursorVisible);
+}
+
+
+void SdTiledRenderingTest::testCursorVisibility_DoubleClick()
+{
+ // Double-clicking anywhere in the TextBox should start editing.
+
+ // Create the first view.
+ SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
+ ViewCallback aView1;
+
+ // Begin text edit on the only object on the slide.
+ sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+ SdPage* pActualPage = pViewShell->GetActualPage();
+ SdrObject* pObject1 = pActualPage->GetObj(0);
+ CPPUNIT_ASSERT(pObject1 != nullptr);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(OBJ_TITLETEXT), pObject1->GetObjIdentifier());
+ SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
+
+ // Double-click outside the text to enter edit mode.
+ const ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
+ const auto cornerX = convertMm100ToTwip(aRect.getX() + (aRect.getWidth() / 4));
+ const auto cornerY = convertMm100ToTwip(aRect.getY() + (aRect.getHeight() / 4));
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ cornerX, cornerY,
+ 2, MOUSE_LEFT, 0);
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ cornerX, cornerY,
+ 2, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // We must be in text editing mode and have cursor visible.
+ CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
+ CPPUNIT_ASSERT(aView1.m_bCursorVisible);
+}
+
+void SdTiledRenderingTest::testCursorVisibility_MultiView()
+{
+ // Create the first view.
+ SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
+ const int nView1 = SfxLokHelper::getView();
+ ViewCallback aView1;
+
+ // Begin text edit on the only object on the slide.
+ sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+ SdPage* pActualPage = pViewShell->GetActualPage();
+ SdrObject* pObject1 = pActualPage->GetObj(0);
+ CPPUNIT_ASSERT(pObject1);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(OBJ_TITLETEXT), pObject1->GetObjIdentifier());
+ SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
+
+ // Make sure that cursor state is not changed just because we create a second view.
+ SfxLokHelper::createView();
+ pXImpressDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ const int nView2 = SfxLokHelper::getView();
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT_EQUAL(false, aView1.m_bCursorVisibleChanged);
+ CPPUNIT_ASSERT_EQUAL(false, aView1.m_aViewCursorVisibilities[nView2]);
+
+ // Also check that the second view gets the notifications.
+ ViewCallback aView2;
+
+ SfxLokHelper::setView(nView1);
+
+ ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
+ const auto centerX = convertMm100ToTwip(aRect.getX() + (aRect.getWidth() / 2));
+ const auto centerY = convertMm100ToTwip(aRect.getY() + (aRect.getHeight() / 2));
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ centerX, centerY,
+ 2, MOUSE_LEFT, 0);
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ centerX, centerY,
+ 2, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // We must be in text editing mode and have cursor visible.
+ CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
+ CPPUNIT_ASSERT(aView1.m_bCursorVisible);
+ CPPUNIT_ASSERT_EQUAL(false, aView1.m_aViewCursorVisibilities[nView2]);
+
+ CPPUNIT_ASSERT_EQUAL(false, aView2.m_bCursorVisible);
+ CPPUNIT_ASSERT_EQUAL(false, aView2.m_aViewCursorVisibilities[nView1]);
+ CPPUNIT_ASSERT_EQUAL(false, aView2.m_aViewCursorVisibilities[nView2]);
+}
+
+void SdTiledRenderingTest::testCursorVisibility_Escape()
+{
+ // Load doc.
+ SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
+ ViewCallback aView1;
+
+ // Begin text edit on the only object on the slide.
+ sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+ SdPage* pActualPage = pViewShell->GetActualPage();
+ SdrObject* pObject1 = pActualPage->GetObj(0);
+ CPPUNIT_ASSERT(pObject1 != nullptr);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(OBJ_TITLETEXT), pObject1->GetObjIdentifier());
+ SdrTextObj* pTextObject = static_cast<SdrTextObj*>(pObject1);
+
+ // Click once on the text to start editing.
+ const ::tools::Rectangle aRect = pTextObject->GetCurrentBoundRect();
+ const auto centerX = convertMm100ToTwip(aRect.getX() + (aRect.getWidth() / 2));
+ const auto centerY = convertMm100ToTwip(aRect.getY() + (aRect.getHeight() / 2));
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN,
+ centerX, centerY,
+ 1, MOUSE_LEFT, 0);
+ pXImpressDocument->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP,
+ centerX, centerY,
+ 1, MOUSE_LEFT, 0);
+ Scheduler::ProcessEventsToIdle();
+
+ // We must be in text editing mode and have cursor visible.
+ CPPUNIT_ASSERT(pViewShell->GetView()->IsTextEdit());
+ CPPUNIT_ASSERT(aView1.m_bCursorVisible);
+
+ // End editing by pressing the escape key.
+ pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
+ pXImpressDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
+ Scheduler::ProcessEventsToIdle();
+
+ // We must be in text editing mode and have cursor visible.
+ CPPUNIT_ASSERT(!pViewShell->GetView()->IsTextEdit());
+ CPPUNIT_ASSERT_EQUAL(false, aView1.m_bCursorVisible);
+}
+
void SdTiledRenderingTest::testViewLock()
{
// Load a document that has a shape and create two views.